mirror of
https://github.com/linuxserver/Heimdall.git
synced 2026-02-26 22:50:32 +09:00
Compare commits
951 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1ccc0da2a7 | ||
|
|
41aa255b88 | ||
|
|
a8e4ab448b | ||
|
|
e42f78bd49 | ||
|
|
08b8ab6d4f | ||
|
|
57e0a335b7 | ||
|
|
0d90170385 | ||
|
|
abe08a7b04 | ||
|
|
6075dcca2d | ||
|
|
4fa41d8114 | ||
|
|
1e6b1f6de5 | ||
|
|
53fd6242db | ||
|
|
69bc8cb34e | ||
|
|
0388ee939a | ||
|
|
2df58472a1 | ||
|
|
abbc78ee8d | ||
|
|
d1801d1088 | ||
|
|
22f66d35e5 | ||
|
|
f197aeb013 | ||
|
|
8fb6438254 | ||
|
|
d972cbcd0a | ||
|
|
638d2fe4a4 | ||
|
|
e251f4f1bc | ||
|
|
ecc54b6fbe | ||
|
|
f0ce9d633a | ||
|
|
968a5823a1 | ||
|
|
53f28b5056 | ||
|
|
7bb0a7ceb4 | ||
|
|
f5ddd93141 | ||
|
|
8554861d0a | ||
|
|
9091d1d707 | ||
|
|
42d29f0fdb | ||
|
|
474059eee8 | ||
|
|
1fb6f75555 | ||
|
|
48e16ebfc9 | ||
|
|
7153a41e03 | ||
|
|
b9e75b9284 | ||
|
|
b3cdc5779c | ||
|
|
c1c3888673 | ||
|
|
11453e1018 | ||
|
|
749442ec6e | ||
|
|
5950ca9076 | ||
|
|
55d3766b39 | ||
|
|
c71479f266 | ||
|
|
2e5d7453cf | ||
|
|
3cbbf807ab | ||
|
|
65a6ad53bf | ||
|
|
dff3f90d00 | ||
|
|
041ec4236b | ||
|
|
808c41acd6 | ||
|
|
c08b0bfe39 | ||
|
|
f7de56b7a7 | ||
|
|
921631bdcd | ||
|
|
d146fed320 | ||
|
|
c56ffe1d1e | ||
|
|
e9d561466b | ||
|
|
b90b38eae9 | ||
|
|
be59ac794e | ||
|
|
f820c0b4f6 | ||
|
|
2b43760dc8 | ||
|
|
8f360f97ef | ||
|
|
014f054862 | ||
|
|
5ccb87cd7f | ||
|
|
8ba8f0c867 | ||
|
|
c2a3368c7b | ||
|
|
395c775d4e | ||
|
|
1063d2cd09 | ||
|
|
18609fb3c2 | ||
|
|
b880333856 | ||
|
|
026bcf9e43 | ||
|
|
837f5c49fa | ||
|
|
a4973869d4 | ||
|
|
07ea22dd10 | ||
|
|
6188b1d669 | ||
|
|
184e19abbc | ||
|
|
f405cf2ddf | ||
|
|
c16a4847f7 | ||
|
|
9d0209c9df | ||
|
|
599035b3f8 | ||
|
|
f3bc6ab618 | ||
|
|
09e4bb8cad | ||
|
|
4b8bf5156d | ||
|
|
809a997631 | ||
|
|
fb7f9de127 | ||
|
|
002bae3fb3 | ||
|
|
b7c0fd29c6 | ||
|
|
70252b7a43 | ||
|
|
d276bf8b1d | ||
|
|
a0726a43ad | ||
|
|
8fa870e8eb | ||
|
|
d184427016 | ||
|
|
36d1d55934 | ||
|
|
3a509fca47 | ||
|
|
afc07ab9bc | ||
|
|
0de1e5bd25 | ||
|
|
029def9d90 | ||
|
|
c6c9ec1d89 | ||
|
|
0f19e3c5de | ||
|
|
f3a4ac3619 | ||
|
|
baf7131f20 | ||
|
|
4430b21740 | ||
|
|
3a3cea437b | ||
|
|
aa5a6fce4c | ||
|
|
8781dbc9f5 | ||
|
|
23f3a9c343 | ||
|
|
2899b7db61 | ||
|
|
e9bfd9a878 | ||
|
|
5825858abc | ||
|
|
379ba34fe1 | ||
|
|
a0e5111771 | ||
|
|
c69961b200 | ||
|
|
39f23103e4 | ||
|
|
e7d581244c | ||
|
|
2fea82c21e | ||
|
|
274894562c | ||
|
|
9fbb3457d1 | ||
|
|
70f212c502 | ||
|
|
5e2c3a5f66 | ||
|
|
ffb5b03704 | ||
|
|
d52ae0d3c3 | ||
|
|
22d7a59e59 | ||
|
|
f3a5be79dc | ||
|
|
3487f52a6b | ||
|
|
2cb837e4b5 | ||
|
|
8725493fcf | ||
|
|
e902b3a38a | ||
|
|
854fb420c2 | ||
|
|
5b4e14d886 | ||
|
|
ef25ec4f69 | ||
|
|
b943674881 | ||
|
|
3188a8f311 | ||
|
|
095aff86c2 | ||
|
|
018996526c | ||
|
|
1c73cf08de | ||
|
|
4bfcd643e6 | ||
|
|
04b8ede8db | ||
|
|
a6ae60270f | ||
|
|
ee3361d9c9 | ||
|
|
42b5a294d0 | ||
|
|
14b89ac44d | ||
|
|
ad86e54f37 | ||
|
|
0f4b336d82 | ||
|
|
6423ccd075 | ||
|
|
72cd16afa1 | ||
|
|
d41c4c8d4c | ||
|
|
2b602ce66d | ||
|
|
aa72ce0a3f | ||
|
|
825ff6a6c8 | ||
|
|
be93195188 | ||
|
|
51b30e55cd | ||
|
|
ecb668a8f3 | ||
|
|
2e96b7bd09 | ||
|
|
768e7a6576 | ||
|
|
431eafb2b6 | ||
|
|
d2184eef0a | ||
|
|
fbd050d4e4 | ||
|
|
5d67f570a9 | ||
|
|
7d016cdaa6 | ||
|
|
6e954a355d | ||
|
|
60faccad27 | ||
|
|
1f5493ac98 | ||
|
|
427353bdfc | ||
|
|
9c117b7946 | ||
|
|
c72fee9644 | ||
|
|
0999bebcb4 | ||
|
|
1c41e3d1ef | ||
|
|
ed3cd79c92 | ||
|
|
192002f898 | ||
|
|
18f7306b2c | ||
|
|
e9754519be | ||
|
|
1c276fbfc2 | ||
|
|
cefe07d218 | ||
|
|
cd4b522935 | ||
|
|
1ad5603c3e | ||
|
|
302e04bb12 | ||
|
|
49dab6e72b | ||
|
|
c5d0769afb | ||
|
|
4005894c16 | ||
|
|
a4022ce517 | ||
|
|
cd07d47445 | ||
|
|
df70dcc521 | ||
|
|
9e6321e500 | ||
|
|
0d9850c1c7 | ||
|
|
4d3083886e | ||
|
|
52f59afe63 | ||
|
|
aa886e4f77 | ||
|
|
3c9f361f5f | ||
|
|
5eb1f55b82 | ||
|
|
d910f8e4f7 | ||
|
|
751b23a5e4 | ||
|
|
a4fa490fb4 | ||
|
|
662f6ac3eb | ||
|
|
11257a272e | ||
|
|
6b93f8ed5c | ||
|
|
8f9b2959b2 | ||
|
|
45cc84c99c | ||
|
|
52620bc331 | ||
|
|
bd76efc613 | ||
|
|
6dead1c529 | ||
|
|
bb5a078f35 | ||
|
|
2ee5d07e48 | ||
|
|
d30edb6749 | ||
|
|
a53192beeb | ||
|
|
ef1ffe880d | ||
|
|
877b31f947 | ||
|
|
827088df76 | ||
|
|
a12c1d559d | ||
|
|
f0b60fc19a | ||
|
|
7c4619adb9 | ||
|
|
aa49a5fb42 | ||
|
|
7565bd4028 | ||
|
|
181b7564e8 | ||
|
|
0d3640b67a | ||
|
|
82b209d4b5 | ||
|
|
f576a82803 | ||
|
|
bb07ba5964 | ||
|
|
9aaa900a3e | ||
|
|
07f07449b9 | ||
|
|
2836dadf1a | ||
|
|
4a90fdf22d | ||
|
|
0de56e5876 | ||
|
|
d886b44c1e | ||
|
|
898ee6915d | ||
|
|
f6eae7f25f | ||
|
|
c1255280aa | ||
|
|
5514d893f8 | ||
|
|
c4d5ca43dd | ||
|
|
a90bcb1151 | ||
|
|
ffc736ada6 | ||
|
|
966027faf5 | ||
|
|
1111bf72d6 | ||
|
|
cc6e47372e | ||
|
|
256160727c | ||
|
|
0299ee6fa9 | ||
|
|
8957a2f49f | ||
|
|
756ab353f3 | ||
|
|
76f0b84827 | ||
|
|
68c8dbc33a | ||
|
|
b1e856ccfc | ||
|
|
44fb923b7f | ||
|
|
8bad474808 | ||
|
|
9a80064a50 | ||
|
|
e86c5e601d | ||
|
|
c4bf619be4 | ||
|
|
4b29b476df | ||
|
|
3a39809585 | ||
|
|
157b972ff5 | ||
|
|
71b15a5aed | ||
|
|
b50bf716dc | ||
|
|
722d00df5f | ||
|
|
54679ff9eb | ||
|
|
4c7e56e6fe | ||
|
|
7b0ae66101 | ||
|
|
48616e87bd | ||
|
|
f9280cd4c0 | ||
|
|
3124a01011 | ||
|
|
ec27889086 | ||
|
|
aba1f37b1c | ||
|
|
6565dfde07 | ||
|
|
aa98c3505c | ||
|
|
15f5c3e5be | ||
|
|
fc2d153ded | ||
|
|
4a7690147e | ||
|
|
7d32e6edb0 | ||
|
|
3d1fc80d64 | ||
|
|
8b0700e9ef | ||
|
|
cc241f1bd6 | ||
|
|
810ee03965 | ||
|
|
7809abb376 | ||
|
|
2f3b7e52db | ||
|
|
fdd5a78eb8 | ||
|
|
98401f20a2 | ||
|
|
30663e4d5e | ||
|
|
8972a11c0a | ||
|
|
bf349eec7a | ||
|
|
cb314a4fd9 | ||
|
|
1a78c8077a | ||
|
|
a3c75b2dd4 | ||
|
|
ee77d09c58 | ||
|
|
0966639699 | ||
|
|
153bb0e91d | ||
|
|
0c7a60a5c7 | ||
|
|
edb51e56f1 | ||
|
|
99193a578e | ||
|
|
49b5d57e14 | ||
|
|
716d5a2f69 | ||
|
|
62340b07a9 | ||
|
|
2312e8b648 | ||
|
|
b390a719e9 | ||
|
|
d4f7ad842c | ||
|
|
5c2501c1a7 | ||
|
|
198ffd7665 | ||
|
|
7cf9d46eb3 | ||
|
|
66dbbc835e | ||
|
|
178e46ca05 | ||
|
|
27f58c0866 | ||
|
|
43f894b58d | ||
|
|
b83f24c6f3 | ||
|
|
b22fbdbad1 | ||
|
|
6552a8c061 | ||
|
|
bcd6f72b72 | ||
|
|
d4fe699029 | ||
|
|
db8fa38665 | ||
|
|
df41de3a9a | ||
|
|
b3070bc8f9 | ||
|
|
487cdc483c | ||
|
|
dcc0f90b23 | ||
|
|
75014c8269 | ||
|
|
386e4c788b | ||
|
|
fe0109494e | ||
|
|
6907695c5d | ||
|
|
533af2dc49 | ||
|
|
1ac5eb6d23 | ||
|
|
ca557fcaf1 | ||
|
|
e2ba89c80e | ||
|
|
bbae811cd8 | ||
|
|
589aa73a15 | ||
|
|
fc023401f5 | ||
|
|
1a4b6da3ea | ||
|
|
02b82dc740 | ||
|
|
4e5129ab8f | ||
|
|
62d9b0cc76 | ||
|
|
02dfed0bc7 | ||
|
|
71d356935f | ||
|
|
e53bae2c69 | ||
|
|
3117b9c483 | ||
|
|
c5817c9b2e | ||
|
|
c403e8b2df | ||
|
|
1ca403cc6e | ||
|
|
758c174141 | ||
|
|
9bc9c48e06 | ||
|
|
9a6dd623db | ||
|
|
d1c6001fae | ||
|
|
74196a2bfa | ||
|
|
724110dad2 | ||
|
|
3a043e0165 | ||
|
|
ac37c60e1f | ||
|
|
937e576520 | ||
|
|
9086e9bd75 | ||
|
|
4c90d8c87e | ||
|
|
a4cceb3ae0 | ||
|
|
90c21d700d | ||
|
|
15b1f3234b | ||
|
|
a116345d60 | ||
|
|
2b4e6d372c | ||
|
|
d370f2c3ee | ||
|
|
ade09f3b1c | ||
|
|
80aa120086 | ||
|
|
a5cbcaca03 | ||
|
|
f2bfc26c5e | ||
|
|
9f0abb6f4d | ||
|
|
9ca2078e7d | ||
|
|
9e4a06999b | ||
|
|
8b72ff0a0d | ||
|
|
51ddaccc16 | ||
|
|
4ff1a4aec4 | ||
|
|
d5bd614cc6 | ||
|
|
0f498eca75 | ||
|
|
3dac9828c8 | ||
|
|
297c2bb30f | ||
|
|
b1dc4d4a41 | ||
|
|
0fe6565346 | ||
|
|
85c68c1f46 | ||
|
|
fc2191b8db | ||
|
|
ee36a0cfae | ||
|
|
a868a6cac1 | ||
|
|
574756b236 | ||
|
|
f9599079e5 | ||
|
|
10afffb71d | ||
|
|
79d53af339 | ||
|
|
8cc6a9cc63 | ||
|
|
18001fbdd0 | ||
|
|
398df75865 | ||
|
|
41c2983312 | ||
|
|
bd3b882b3a | ||
|
|
e52bc9c6b2 | ||
|
|
a16233ee10 | ||
|
|
5b177f1127 | ||
|
|
f7633b6a5f | ||
|
|
d58e6cb560 | ||
|
|
87fe9c1771 | ||
|
|
95d9e9edb9 | ||
|
|
f7b2c51f9d | ||
|
|
72c70e27f2 | ||
|
|
becbe5ab96 | ||
|
|
f5bce95808 | ||
|
|
1e2b807e05 | ||
|
|
ed3dbf2f14 | ||
|
|
1e35f83fed | ||
|
|
8499d100ff | ||
|
|
bdeae6a722 | ||
|
|
4c63b66dbf | ||
|
|
371f52f92e | ||
|
|
6daaf94974 | ||
|
|
b8e5a22648 | ||
|
|
e821edf01a | ||
|
|
188316df47 | ||
|
|
dd14ce8738 | ||
|
|
d8e4053ce4 | ||
|
|
f73b78f292 | ||
|
|
96ec1e0b08 | ||
|
|
1904d9d910 | ||
|
|
beb2851f4a | ||
|
|
645a0bc7fe | ||
|
|
ef73918098 | ||
|
|
06a23c70af | ||
|
|
323a6e17c7 | ||
|
|
0ca07c9698 | ||
|
|
fbb21d2885 | ||
|
|
b6741c8ff0 | ||
|
|
7fdca02af7 | ||
|
|
271100c25b | ||
|
|
cfecce6100 | ||
|
|
d71341df7a | ||
|
|
ac851f5a02 | ||
|
|
d2a8b7af48 | ||
|
|
4d7a86fbf7 | ||
|
|
87a3af3dbf | ||
|
|
79c76b9f50 | ||
|
|
d7742df802 | ||
|
|
736222e173 | ||
|
|
047ffb0b55 | ||
|
|
505d17cf57 | ||
|
|
71c2da160d | ||
|
|
3487b9e479 | ||
|
|
7cdf315d78 | ||
|
|
a1812a962c | ||
|
|
b97cab34b0 | ||
|
|
3426450b3c | ||
|
|
75804f4624 | ||
|
|
23f757e06f | ||
|
|
fabd704bab | ||
|
|
c16fbae963 | ||
|
|
348759b9ad | ||
|
|
d961aeb19b | ||
|
|
987a9c03b0 | ||
|
|
d17fb04983 | ||
|
|
b07918d751 | ||
|
|
f9a19fce91 | ||
|
|
1b71e80d52 | ||
|
|
9fac11cb77 | ||
|
|
a94fb108fa | ||
|
|
f51893b2d6 | ||
|
|
a4b32dcafd | ||
|
|
862f870303 | ||
|
|
869cfd34ab | ||
|
|
9e67b7c72e | ||
|
|
a5b7f10809 | ||
|
|
698665176c | ||
|
|
1bd65222e4 | ||
|
|
61a5a1a8b0 | ||
|
|
e51cdd7f7a | ||
|
|
a626222ff3 | ||
|
|
af68a45a3d | ||
|
|
7969df9344 | ||
|
|
67bc656325 | ||
|
|
b5baf0d606 | ||
|
|
32964e9045 | ||
|
|
27a667675a | ||
|
|
9a221f47bf | ||
|
|
8d2fbc5599 | ||
|
|
84ebe6587d | ||
|
|
89a71a18da | ||
|
|
05dd7a090b | ||
|
|
3a9bdd2c43 | ||
|
|
0292e9976e | ||
|
|
365de9512f | ||
|
|
e857023903 | ||
|
|
c058e1a452 | ||
|
|
7f42967b67 | ||
|
|
994961de54 | ||
|
|
049d20536c | ||
|
|
c9c8171a52 | ||
|
|
e7a6ac5a75 | ||
|
|
3a372107ba | ||
|
|
493374de65 | ||
|
|
c43fc929f7 | ||
|
|
895d5f2ebe | ||
|
|
cb4768e2cf | ||
|
|
6ca4bfb9ee | ||
|
|
1f7de03d90 | ||
|
|
861d287750 | ||
|
|
e2cc32fd0a | ||
|
|
762df25c45 | ||
|
|
0b114e7dc1 | ||
|
|
981876c43c | ||
|
|
7fe9c477e3 | ||
|
|
5b3a7f4e02 | ||
|
|
60e20c4023 | ||
|
|
9e1bb6c075 | ||
|
|
9c05d0d803 | ||
|
|
3d1393c0b3 | ||
|
|
9914fdb111 | ||
|
|
9b24c9f1b5 | ||
|
|
81b8c646d2 | ||
|
|
f734832e0f | ||
|
|
1bde11b30b | ||
|
|
9e80c3fe40 | ||
|
|
f272dd13ce | ||
|
|
9f26de89a4 | ||
|
|
e23964ebad | ||
|
|
d0584ac941 | ||
|
|
1410c41f48 | ||
|
|
5f524563cf | ||
|
|
3d181623c3 | ||
|
|
1919dbfd96 | ||
|
|
8e1615ac5f | ||
|
|
7966f07fdb | ||
|
|
ac8fe7012b | ||
|
|
51d89dae82 | ||
|
|
ef21ac0fe7 | ||
|
|
02697687cc | ||
|
|
fd2f7f27a6 | ||
|
|
21b1ef5e4b | ||
|
|
e452d3b5f6 | ||
|
|
1419882455 | ||
|
|
d79202ed87 | ||
|
|
aa05c947a9 | ||
|
|
eb69ea05fa | ||
|
|
244b0998f9 | ||
|
|
1f608b1c21 | ||
|
|
7d6df3843b | ||
|
|
dd4b94ba71 | ||
|
|
edb9397a47 | ||
|
|
66815a8487 | ||
|
|
5ee19bceb6 | ||
|
|
deba7d0279 | ||
|
|
38e93d33f1 | ||
|
|
f182f305fc | ||
|
|
03c675ed57 | ||
|
|
3916b2d4dc | ||
|
|
698a7a9bbc | ||
|
|
9954d1b6bf | ||
|
|
a98b981efe | ||
|
|
dd2ca62eaf | ||
|
|
dc2a42ad6d | ||
|
|
b5b25458db | ||
|
|
ac4bbceb2f | ||
|
|
4980bfab12 | ||
|
|
63e0d07d50 | ||
|
|
2b76520e3a | ||
|
|
6631b8a23b | ||
|
|
04d34017c1 | ||
|
|
6dcbcb452e | ||
|
|
7c74b3bb13 | ||
|
|
b76a9ee118 | ||
|
|
40da649b10 | ||
|
|
cd64d762e7 | ||
|
|
b91c52820a | ||
|
|
1204ffd306 | ||
|
|
978267dd14 | ||
|
|
f935fd94c6 | ||
|
|
fb2428e21b | ||
|
|
e36a126c01 | ||
|
|
b186978624 | ||
|
|
49773d7654 | ||
|
|
53c03751a1 | ||
|
|
9d2908449a | ||
|
|
caf92bcf6d | ||
|
|
cbef469e02 | ||
|
|
a6bfc1d76c | ||
|
|
0446a74de9 | ||
|
|
1a14079b39 | ||
|
|
d9c1919ddc | ||
|
|
f42ac0c5ba | ||
|
|
c073929895 | ||
|
|
02df7844a4 | ||
|
|
e43aba0111 | ||
|
|
ac7446ffe6 | ||
|
|
e45d3ca6ec | ||
|
|
603550a453 | ||
|
|
4463ef4a9a | ||
|
|
49b8dc0079 | ||
|
|
ab83c3a551 | ||
|
|
98c6093674 | ||
|
|
e1f51521bf | ||
|
|
e85bc98dcc | ||
|
|
4ba52baa5c | ||
|
|
a82077b4de | ||
|
|
b8f04f3d11 | ||
|
|
e91f65f833 | ||
|
|
575ab9be2d | ||
|
|
64071bb60f | ||
|
|
44621a1a61 | ||
|
|
c56043e1f9 | ||
|
|
e8e4cbfd41 | ||
|
|
4b2bbe0614 | ||
|
|
9adfa14e62 | ||
|
|
067f82b632 | ||
|
|
e24e7979be | ||
|
|
c7c2b6e6f2 | ||
|
|
96798963d6 | ||
|
|
75508a81ef | ||
|
|
f7f4efadb7 | ||
|
|
4cc80b98db | ||
|
|
b07efaa7d0 | ||
|
|
7ba8ea6dd4 | ||
|
|
479461821f | ||
|
|
d25aea38fb | ||
|
|
0067502b37 | ||
|
|
d321af6085 | ||
|
|
b30f1730e4 | ||
|
|
7a02986982 | ||
|
|
a4107cba25 | ||
|
|
ba187aa345 | ||
|
|
b400cef734 | ||
|
|
03b72b8d2e | ||
|
|
0c7e20dee0 | ||
|
|
aa42c71a06 | ||
|
|
041c0b81a3 | ||
|
|
fe6776ee9d | ||
|
|
125b9f4160 | ||
|
|
488fee7b4b | ||
|
|
4fba596909 | ||
|
|
af04781830 | ||
|
|
2507cda94c | ||
|
|
21c1401859 | ||
|
|
4351f55225 | ||
|
|
d1956c4e88 | ||
|
|
d76d056ed1 | ||
|
|
e081cc31a2 | ||
|
|
88153b0e32 | ||
|
|
2b2d51cb6f | ||
|
|
abd8c7227d | ||
|
|
50d6dc7b71 | ||
|
|
b4a1ecc305 | ||
|
|
540bead0db | ||
|
|
6e9f25d680 | ||
|
|
268afe7006 | ||
|
|
7b9d3f0ec6 | ||
|
|
de116030bc | ||
|
|
4fb59385b7 | ||
|
|
40d6808067 | ||
|
|
3572bd8068 | ||
|
|
380a0e8623 | ||
|
|
4f6a0cb7c6 | ||
|
|
56cce8233b | ||
|
|
af8afcf931 | ||
|
|
7f997d60cd | ||
|
|
d45f5a805e | ||
|
|
fc9c624509 | ||
|
|
33372509eb | ||
|
|
bca0b02925 | ||
|
|
24995135e6 | ||
|
|
c1e4103edd | ||
|
|
6eda423156 | ||
|
|
7dc72d3519 | ||
|
|
907c22179b | ||
|
|
cdafbab7b1 | ||
|
|
e0064504e7 | ||
|
|
b22117dd01 | ||
|
|
c88efa8dbc | ||
|
|
7d060ff803 | ||
|
|
d5f8b6aae0 | ||
|
|
2416993c5e | ||
|
|
494165a03a | ||
|
|
88e607533c | ||
|
|
035d2f9209 | ||
|
|
53903daa87 | ||
|
|
567994be1a | ||
|
|
32fa27337a | ||
|
|
e1bb7646ac | ||
|
|
108b636def | ||
|
|
4abb14bf54 | ||
|
|
f8f96593c1 | ||
|
|
cb21b0f8f1 | ||
|
|
4c8c5fa27f | ||
|
|
6093119dde | ||
|
|
15755a3fd1 | ||
|
|
0213c81e0d | ||
|
|
c47f296f17 | ||
|
|
75133474f7 | ||
|
|
ddbe171f3a | ||
|
|
12e109f82c | ||
|
|
e095589172 | ||
|
|
d0293c785b | ||
|
|
aa351e31bf | ||
|
|
aceed3d13b | ||
|
|
10b70d4a09 | ||
|
|
cb9e014cf3 | ||
|
|
99017d834e | ||
|
|
fb73f5ca24 | ||
|
|
4369f1aeda | ||
|
|
6501aacb1b | ||
|
|
c3da17befc | ||
|
|
46bb073001 | ||
|
|
e8b47776ce | ||
|
|
6941fd3e2d | ||
|
|
f5937879df | ||
|
|
93877b7025 | ||
|
|
6612631cc3 | ||
|
|
30c3079a90 | ||
|
|
e86e681c53 | ||
|
|
48f72652da | ||
|
|
a3f7bafedb | ||
|
|
d30d610042 | ||
|
|
882e406266 | ||
|
|
45d421256c | ||
|
|
a2f20fc18f | ||
|
|
988364cb7c | ||
|
|
a3816ed8a1 | ||
|
|
d48805ee2c | ||
|
|
e820b81259 | ||
|
|
8b1046ce17 | ||
|
|
a138b65842 | ||
|
|
c344de3f04 | ||
|
|
427659a897 | ||
|
|
a6543970e7 | ||
|
|
399ea088dc | ||
|
|
3f87833e52 | ||
|
|
6dcb77023c | ||
|
|
4d37135bdf | ||
|
|
d109047fa5 | ||
|
|
b6112501e2 | ||
|
|
0663e236b4 | ||
|
|
f25cea1749 | ||
|
|
03e16415aa | ||
|
|
9a55e05943 | ||
|
|
c06fa4eab6 | ||
|
|
5575b082ea | ||
|
|
1c6da858bc | ||
|
|
f0a14641c1 | ||
|
|
c6d07cd8a4 | ||
|
|
df9f07faf4 | ||
|
|
a3dcc278d7 | ||
|
|
5de473fa44 | ||
|
|
763c1545a6 | ||
|
|
ecde7a0f32 | ||
|
|
7fc5e0abb5 | ||
|
|
06a655ac0c | ||
|
|
875ddaa834 | ||
|
|
030fccbb50 | ||
|
|
908f70d90a | ||
|
|
8724ced531 | ||
|
|
aa1a3a95ca | ||
|
|
0767dc075e | ||
|
|
c8a6c89036 | ||
|
|
cafe386cc4 | ||
|
|
0184c9695b | ||
|
|
19536edf28 | ||
|
|
045e4a20fa | ||
|
|
6cb8487a52 | ||
|
|
c39e9aa13f | ||
|
|
0203440b06 | ||
|
|
7f7bf60456 | ||
|
|
e8673634bc | ||
|
|
53e52c93ee | ||
|
|
c239c0ea5a | ||
|
|
7142f755f5 | ||
|
|
9fbc8dc1f9 | ||
|
|
d3819a6a88 | ||
|
|
6e93ed8e5f | ||
|
|
98543d49a9 | ||
|
|
9195eead27 | ||
|
|
e5b384736d | ||
|
|
4def720d1a | ||
|
|
2a0404ea17 | ||
|
|
6d22c4f02e | ||
|
|
24ac12da65 | ||
|
|
3f19882df8 | ||
|
|
dd54c16c1f | ||
|
|
223e9289dc | ||
|
|
2e301bdd51 | ||
|
|
e3ec7de23a | ||
|
|
18ec208381 | ||
|
|
8666daa07d | ||
|
|
926a9bdb03 | ||
|
|
fc2d837a2c | ||
|
|
3f69ccab99 | ||
|
|
b6ee82ee52 | ||
|
|
da1eb859a9 | ||
|
|
2b5269b823 | ||
|
|
6c8eeb0ced | ||
|
|
c5e0f3abc8 | ||
|
|
c6dbe22c57 | ||
|
|
51776e2aa3 | ||
|
|
c9d24607f6 | ||
|
|
46f7a3d4f3 | ||
|
|
56cf450c89 | ||
|
|
38c350b020 | ||
|
|
1f46040f1b | ||
|
|
63b95319cd | ||
|
|
a25d92be9e | ||
|
|
a161210a4a | ||
|
|
24469eb2fa | ||
|
|
a93fb49875 | ||
|
|
08d7cdf95b | ||
|
|
a9334bc247 | ||
|
|
2357d0c466 | ||
|
|
b003d51276 | ||
|
|
503cbf9830 | ||
|
|
2466058c5a | ||
|
|
6b1f422456 | ||
|
|
1d16d67733 | ||
|
|
6f9d15aed8 | ||
|
|
8725f974da | ||
|
|
2551c949ae | ||
|
|
30c6020ce7 | ||
|
|
d00b1ce1a1 | ||
|
|
2c43d79585 | ||
|
|
3e4aacb2b0 | ||
|
|
67cd22371b | ||
|
|
586941ece7 | ||
|
|
32a57cbfa6 | ||
|
|
1837a69b3e | ||
|
|
aa8bfcfd92 | ||
|
|
9e0c658470 | ||
|
|
66aefff4f3 | ||
|
|
bd0fdeecee | ||
|
|
1055eeb01b | ||
|
|
263a4578d4 | ||
|
|
c446b8a5af | ||
|
|
847b34cdcb | ||
|
|
5a57f90b8f | ||
|
|
f8eb9f5bd0 | ||
|
|
c8c336b574 | ||
|
|
43f1410974 | ||
|
|
1df110b3fb | ||
|
|
9bedce0df5 | ||
|
|
ae1d879e5a | ||
|
|
caab2e0952 | ||
|
|
a29d6517d3 | ||
|
|
dae2781818 | ||
|
|
c1de609b1a | ||
|
|
4e84807950 | ||
|
|
41c9cb45d6 | ||
|
|
2d72772f86 | ||
|
|
1cf1f0e04d | ||
|
|
324b88ec2b | ||
|
|
ec64c7ba0b | ||
|
|
67e0f8570e | ||
|
|
1745100705 | ||
|
|
28501944c5 | ||
|
|
c5c1a68f5c | ||
|
|
e2ed7fbd28 | ||
|
|
ab0675055c | ||
|
|
2b65559f36 | ||
|
|
b8beb07df5 | ||
|
|
01748fec49 | ||
|
|
42a50b1a02 | ||
|
|
d81595f43f | ||
|
|
71fa2d867f | ||
|
|
f55dbb0002 | ||
|
|
3e4d623786 | ||
|
|
1b109aac3a | ||
|
|
8558975d55 | ||
|
|
0e895089c7 | ||
|
|
5dd44f66c1 | ||
|
|
a1cfea46c6 | ||
|
|
42c492c85a | ||
|
|
9e8794a39f | ||
|
|
66c3604b2a | ||
|
|
c0ee1ee27b | ||
|
|
a3e669e433 | ||
|
|
8a83b4fff5 | ||
|
|
69c48d3f5d | ||
|
|
88504a335e | ||
|
|
a44a433807 | ||
|
|
9c03a8ae28 | ||
|
|
767a5f3a94 | ||
|
|
db1e138d36 | ||
|
|
84aa05f9a9 | ||
|
|
1071b85472 | ||
|
|
479412b190 | ||
|
|
ac4fc5b0ba | ||
|
|
4a8770232d | ||
|
|
2ea983bdae | ||
|
|
ffce8fa505 | ||
|
|
cb9e529eb7 | ||
|
|
7f0ee208fa | ||
|
|
2d38652034 | ||
|
|
4a2076a550 | ||
|
|
35049b26fe | ||
|
|
39648ba372 | ||
|
|
44ce0bbffd | ||
|
|
a634472873 | ||
|
|
a98c30f7fa | ||
|
|
43d4b80e11 | ||
|
|
09926fcc53 | ||
|
|
17eef7a4aa | ||
|
|
4738d7c951 | ||
|
|
4be0af5fa3 | ||
|
|
8aaa0900e5 | ||
|
|
49b5d9b886 | ||
|
|
7a3912767b | ||
|
|
d1e473aafe | ||
|
|
c91eb7ed47 | ||
|
|
443f631bac | ||
|
|
95be8d4698 | ||
|
|
f3926d020f | ||
|
|
b48eb5fdd3 | ||
|
|
b0fecdd017 | ||
|
|
793483b6e9 | ||
|
|
c99c7fa9af | ||
|
|
59449a73e1 | ||
|
|
dc86d636ac | ||
|
|
27dd2dd5f3 | ||
|
|
494ae1a47f | ||
|
|
7d1e1e5ff1 | ||
|
|
bcd1567b7d | ||
|
|
ae391b885b | ||
|
|
226bc84a4c | ||
|
|
e60a0c8f61 | ||
|
|
218c90a306 | ||
|
|
2a60c80194 | ||
|
|
35b1c55564 | ||
|
|
20476387ff | ||
|
|
a787748a00 | ||
|
|
34ee540c30 | ||
|
|
975a5ffc82 | ||
|
|
dee0870bf6 | ||
|
|
96ec3bd44e | ||
|
|
e4cf4096a6 | ||
|
|
3d79694c0a | ||
|
|
eab3e4e6f7 | ||
|
|
462152bab2 | ||
|
|
766a455db0 | ||
|
|
30200ac219 | ||
|
|
90a9113971 | ||
|
|
ad1834568f | ||
|
|
6231500b4a | ||
|
|
b2dbc08ea0 | ||
|
|
4700f68f4d | ||
|
|
fa73738309 | ||
|
|
25f92ec438 | ||
|
|
6a59c1dfd6 | ||
|
|
bad6e9d2fe | ||
|
|
e544972c1d | ||
|
|
f1e5de0d58 | ||
|
|
e434e1effb | ||
|
|
ca04b210e6 | ||
|
|
51275af41d | ||
|
|
c2417ac5b3 | ||
|
|
72831d413b | ||
|
|
5cb4bd5819 | ||
|
|
2d9bbca9a9 | ||
|
|
7b54c4e969 | ||
|
|
b50f4ec5ce | ||
|
|
86cc7534c2 | ||
|
|
1ab2565244 | ||
|
|
d2089a9344 | ||
|
|
39153c6936 | ||
|
|
1a8e2b92de | ||
|
|
cf63e751bf | ||
|
|
0c51bc2771 | ||
|
|
5f278cce3e | ||
|
|
f8cf3ac832 | ||
|
|
50bdd02a72 | ||
|
|
424155e5cd | ||
|
|
c4a4d25f7e | ||
|
|
ca2e135cba |
31
.env
31
.env
@@ -1,31 +0,0 @@
|
||||
APP_NAME=Heimdall
|
||||
APP_ENV=local
|
||||
APP_KEY=base64:I206O8ibx+GQyRE7BeOxDobn04Mfmyyc5Ptzns/C0mY=
|
||||
APP_DEBUG=true
|
||||
APP_LOG_LEVEL=debug
|
||||
APP_URL=http://localhost
|
||||
|
||||
DB_CONNECTION=sqlite
|
||||
DB_DATABASE=app.sqlite
|
||||
|
||||
BROADCAST_DRIVER=log
|
||||
CACHE_DRIVER=file
|
||||
SESSION_DRIVER=file
|
||||
SESSION_LIFETIME=120
|
||||
QUEUE_DRIVER=sync
|
||||
|
||||
REDIS_HOST=127.0.0.1
|
||||
REDIS_PASSWORD=null
|
||||
REDIS_PORT=6379
|
||||
|
||||
MAIL_DRIVER=smtp
|
||||
MAIL_HOST=smtp.mailtrap.io
|
||||
MAIL_PORT=2525
|
||||
MAIL_USERNAME=null
|
||||
MAIL_PASSWORD=null
|
||||
MAIL_ENCRYPTION=null
|
||||
|
||||
PUSHER_APP_ID=
|
||||
PUSHER_APP_KEY=
|
||||
PUSHER_APP_SECRET=
|
||||
PUSHER_APP_CLUSTER=mt1
|
||||
58
.env.example
58
.env.example
@@ -1,35 +1,69 @@
|
||||
APP_NAME=Laravel
|
||||
APP_NAME=Heimdall
|
||||
APP_ENV=local
|
||||
APP_KEY=
|
||||
APP_DEBUG=true
|
||||
APP_LOG_LEVEL=debug
|
||||
APP_DEBUG=false
|
||||
APP_URL=http://localhost
|
||||
|
||||
DB_CONNECTION=mysql
|
||||
DB_HOST=127.0.0.1
|
||||
DB_PORT=3306
|
||||
DB_DATABASE=homestead
|
||||
DB_USERNAME=homestead
|
||||
DB_PASSWORD=secret
|
||||
APP_LOCALE=en
|
||||
APP_FALLBACK_LOCALE=en
|
||||
APP_FAKER_LOCALE=en_US
|
||||
APP_MAINTENANCE_DRIVER=file
|
||||
APP_MAINTENANCE_STORE=database
|
||||
BCRYPT_ROUNDS=12
|
||||
|
||||
BROADCAST_DRIVER=log
|
||||
CACHE_DRIVER=file
|
||||
LOG_CHANNEL=daily
|
||||
LOG_STACK=single
|
||||
|
||||
DB_CONNECTION=sqlite
|
||||
DB_DATABASE=app.sqlite
|
||||
|
||||
#DB_CONNECTION=<mysql | pgsql>
|
||||
#DB_HOST=<hostname | ip>
|
||||
#DB_PORT=<port number>
|
||||
#DB_DATABASE=<database>
|
||||
#DB_USERNAME=<user>
|
||||
#DB_PASSWORD=<password>
|
||||
|
||||
BROADCAST_CONNECTION=log
|
||||
CACHE_STORE=file
|
||||
QUEUE_CONNECTION=sync
|
||||
SESSION_DRIVER=file
|
||||
SESSION_LIFETIME=120
|
||||
SESSION_ENCRYPT=false
|
||||
SESSION_PATH=/
|
||||
SESSION_DOMAIN=null
|
||||
QUEUE_DRIVER=sync
|
||||
|
||||
REDIS_HOST=127.0.0.1
|
||||
REDIS_PASSWORD=null
|
||||
REDIS_PORT=6379
|
||||
|
||||
MAIL_DRIVER=smtp
|
||||
MAIL_MAILER=smtp
|
||||
MAIL_HOST=smtp.mailtrap.io
|
||||
MAIL_PORT=2525
|
||||
MAIL_USERNAME=null
|
||||
MAIL_PASSWORD=null
|
||||
MAIL_ENCRYPTION=null
|
||||
MAIL_FROM_ADDRESS=null
|
||||
MAIL_FROM_NAME="${APP_NAME}"
|
||||
|
||||
AWS_ACCESS_KEY_ID=
|
||||
AWS_SECRET_ACCESS_KEY=
|
||||
AWS_DEFAULT_REGION=us-east-1
|
||||
AWS_BUCKET=
|
||||
|
||||
PUSHER_APP_ID=
|
||||
PUSHER_APP_KEY=
|
||||
PUSHER_APP_SECRET=
|
||||
PUSHER_APP_CLUSTER=mt1
|
||||
|
||||
MIX_PUSHER_APP_KEY="${PUSHER_APP_KEY}"
|
||||
MIX_PUSHER_APP_CLUSTER="${PUSHER_APP_CLUSTER}"
|
||||
|
||||
AUTH_ROLES_ENABLE=false
|
||||
AUTH_ROLES_HEADER="remote-groups"
|
||||
AUTH_ROLES_HTTP_HEADER="HTTP_REMOTE_GROUPS"
|
||||
AUTH_ROLES_ADMIN="admin"
|
||||
AUTH_ROLES_DELIMITER=","
|
||||
|
||||
ALLOW_INTERNAL_REQUESTS=false
|
||||
3
.eslintignore
Normal file
3
.eslintignore
Normal file
@@ -0,0 +1,3 @@
|
||||
huebee.js
|
||||
jquery-ui.min.js
|
||||
bootstrap.js
|
||||
13
.eslintrc
Normal file
13
.eslintrc
Normal file
@@ -0,0 +1,13 @@
|
||||
{
|
||||
"extends": ["airbnb-base", "prettier"],
|
||||
"plugins": ["prettier"],
|
||||
"rules": {
|
||||
"prettier/prettier": ["error"]
|
||||
},
|
||||
"env": {
|
||||
"browser": true
|
||||
},
|
||||
"globals": {
|
||||
"$": true
|
||||
}
|
||||
}
|
||||
44
.gitattributes
vendored
44
.gitattributes
vendored
@@ -1,5 +1,49 @@
|
||||
# Configuration file for Git attributes
|
||||
|
||||
# Core Settings {{{
|
||||
# .gitattributes
|
||||
.gitattributes !filter !diff
|
||||
|
||||
# Line Endings
|
||||
* text=auto
|
||||
|
||||
# Set binary to none-text files
|
||||
*.png -text
|
||||
|
||||
# }}}
|
||||
|
||||
|
||||
# GitHub Linguist {{{
|
||||
|
||||
# Exclude files/folder from being detected by the GitHub linguist
|
||||
# statistic.
|
||||
node_modules/* linguist-vendored
|
||||
public/* linguist-generated=true
|
||||
vendor/* linguist-vendored
|
||||
|
||||
# Remove Vue as it's currently not used in the project.
|
||||
resources/assets/js/components/ExampleComponent.vue linguist-vendored
|
||||
|
||||
# System Wide
|
||||
*.css linguist-vendored
|
||||
*.scss linguist-vendored
|
||||
*.js linguist-vendored
|
||||
|
||||
# Include user generated files that's removed bu the setting above.
|
||||
resources/assets/js/app.js linguist-vendored=false
|
||||
resources/assets/sass/_app.scss linguist-vendored=false
|
||||
resources/assets/sass/_rune.scss linguist-vendored=false
|
||||
resources/assets/sass/_variables.scss linguist-vendored=false
|
||||
|
||||
# }}}
|
||||
|
||||
|
||||
# Archive Exlude {{{
|
||||
# Exclude files/folders from being exported when creating an archive.
|
||||
|
||||
.gitattributes export-ignore
|
||||
.gitignore export-ignore
|
||||
.travis.yml export-ignore
|
||||
CHANGELOG.md export-ignore
|
||||
|
||||
# }}}
|
||||
|
||||
16
.github/workflows/call_issue_pr_tracker.yml
vendored
Normal file
16
.github/workflows/call_issue_pr_tracker.yml
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
name: Issue & PR Tracker
|
||||
|
||||
on:
|
||||
issues:
|
||||
types: [opened,reopened,labeled,unlabeled,closed]
|
||||
pull_request_target:
|
||||
types: [opened,reopened,review_requested,review_request_removed,labeled,unlabeled,closed]
|
||||
pull_request_review:
|
||||
types: [submitted,edited,dismissed]
|
||||
|
||||
jobs:
|
||||
manage-project:
|
||||
permissions:
|
||||
issues: write
|
||||
uses: linuxserver/github-workflows/.github/workflows/issue-pr-tracker.yml@v1
|
||||
secrets: inherit
|
||||
13
.github/workflows/call_issues_cron.yml
vendored
Normal file
13
.github/workflows/call_issues_cron.yml
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
name: Mark stale issues and pull requests
|
||||
on:
|
||||
schedule:
|
||||
- cron: '35 15 * * *'
|
||||
workflow_dispatch:
|
||||
|
||||
jobs:
|
||||
stale:
|
||||
permissions:
|
||||
issues: write
|
||||
pull-requests: write
|
||||
uses: linuxserver/github-workflows/.github/workflows/issues-cron.yml@v1
|
||||
secrets: inherit
|
||||
59
.github/workflows/ci.yml
vendored
Normal file
59
.github/workflows/ci.yml
vendored
Normal file
@@ -0,0 +1,59 @@
|
||||
name: Tests (PHP)
|
||||
|
||||
on: [pull_request]
|
||||
|
||||
jobs:
|
||||
tests:
|
||||
name: Run tests
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v1
|
||||
|
||||
- name: Setup PHP, with composer and extensions
|
||||
uses: shivammathur/setup-php@v2 #https://github.com/shivammathur/setup-php
|
||||
with:
|
||||
php-version: '8.3'
|
||||
extensions: mbstring, dom, fileinfo, mysql, libxml, xml, xmlwriter, dom, tokenizer, filter, json, phar, pcre, openssl, pdo, intl, curl
|
||||
|
||||
- name: Cache composer dependencies
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: vendor
|
||||
key: composer-${{ hashFiles('composer.lock') }}
|
||||
|
||||
#- name: Run composer install
|
||||
# run: composer install -n --prefer-dist
|
||||
# env:
|
||||
# APP_ENV: testing
|
||||
|
||||
- name: Prepare Laravel Application
|
||||
run: |
|
||||
cp .env.example .env
|
||||
php artisan key:generate
|
||||
|
||||
- name: Cache yarn dependencies
|
||||
uses: actions/cache@v4
|
||||
with:
|
||||
path: node_modules
|
||||
key: yarn-${{ hashFiles('yarn.lock') }}
|
||||
|
||||
- name: Run yarn
|
||||
run: yarn && yarn dev
|
||||
|
||||
- name: Run ESLint
|
||||
run: yarn lint
|
||||
|
||||
- name: Run tests
|
||||
run: php artisan test
|
||||
env:
|
||||
APP_ENV: testing
|
||||
|
||||
- name: Php code sniffer
|
||||
run: ./vendor/bin/phpcs --config-set ignore_warnings_on_exit 1
|
||||
|
||||
- name: Upload artifacts
|
||||
uses: actions/upload-artifact@master
|
||||
if: failure()
|
||||
with:
|
||||
name: Logs
|
||||
path: ./storage/logs
|
||||
7
.gitignore
vendored
7
.gitignore
vendored
@@ -1,7 +1,9 @@
|
||||
/app/SupportedApps
|
||||
/node_modules
|
||||
/public/hot
|
||||
/public/storage
|
||||
/storage/*.key
|
||||
/storage/debugbar
|
||||
/.idea
|
||||
/.vagrant
|
||||
Homestead.json
|
||||
@@ -23,4 +25,7 @@ yarn-error.log
|
||||
.Spotlight-V100
|
||||
.TemporaryItems
|
||||
.Trashes
|
||||
.VolumeIcon.icns
|
||||
.VolumeIcon.icns
|
||||
storage/app/public/avatars/*
|
||||
.env
|
||||
.phpunit.result.cache
|
||||
|
||||
2548
.phpstorm.meta.php
Normal file
2548
.phpstorm.meta.php
Normal file
File diff suppressed because it is too large
Load Diff
15
.vscode/launch.json
vendored
Normal file
15
.vscode/launch.json
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"name": "Listen for Xdebug",
|
||||
"type": "php",
|
||||
"request": "launch",
|
||||
"port": 9003,
|
||||
"pathMappings": {
|
||||
"/var/www/html": "${workspaceFolder}"
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
16
.vscode/tasks.json
vendored
Normal file
16
.vscode/tasks.json
vendored
Normal file
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"version": "2.0.0",
|
||||
"tasks": [
|
||||
{
|
||||
"label": "Start Docker Compose",
|
||||
"type": "shell",
|
||||
"command": "docker-compose up --build",
|
||||
"group": {
|
||||
"kind": "build",
|
||||
"isDefault": true
|
||||
},
|
||||
"problemMatcher": []
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
14
SECURITY.md
Normal file
14
SECURITY.md
Normal file
@@ -0,0 +1,14 @@
|
||||
# Security Policy
|
||||
|
||||
## Supported Versions
|
||||
|
||||
| Version | Supported |
|
||||
| ------- | ------------------ |
|
||||
| 2.3.x | :white_check_mark: |
|
||||
| < 2.3 | :x: |
|
||||
|
||||
## Reporting a Vulnerability
|
||||
|
||||
You can report any vulnerabilities on our discord server by DM-ing a team member, or asking a team member to DM you.
|
||||
|
||||
https://discord.com/invite/YWrKVTn
|
||||
28371
_ide_helper.php
Normal file
28371
_ide_helper.php
Normal file
File diff suppressed because it is too large
Load Diff
200
app/Application.php
Normal file
200
app/Application.php
Normal file
@@ -0,0 +1,200 @@
|
||||
<?php
|
||||
|
||||
namespace App;
|
||||
|
||||
use GuzzleHttp\Exception\GuzzleException;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Support\Collection;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
/**
|
||||
* App\Application
|
||||
*
|
||||
* @property string $appid
|
||||
* @property string $name
|
||||
* @property string|null $sha
|
||||
* @property string|null $icon
|
||||
* @property string|null $website
|
||||
* @property string|null $license
|
||||
* @property string|null $description
|
||||
* @property int $enhanced
|
||||
* @property string $tile_background
|
||||
* @property \Illuminate\Support\Carbon|null $created_at
|
||||
* @property \Illuminate\Support\Carbon|null $updated_at
|
||||
* @property string|null $class
|
||||
* @method static Builder|Application newModelQuery()
|
||||
* @method static Builder|Application newQuery()
|
||||
* @method static Builder|Application query()
|
||||
* @method static Builder|Application whereAppid($value)
|
||||
* @method static Builder|Application whereClass($value)
|
||||
* @method static Builder|Application whereCreatedAt($value)
|
||||
* @method static Builder|Application whereDescription($value)
|
||||
* @method static Builder|Application whereEnhanced($value)
|
||||
* @method static Builder|Application whereIcon($value)
|
||||
* @method static Builder|Application whereLicense($value)
|
||||
* @method static Builder|Application whereName($value)
|
||||
* @method static Builder|Application whereSha($value)
|
||||
* @method static Builder|Application whereTileBackground($value)
|
||||
* @method static Builder|Application whereUpdatedAt($value)
|
||||
* @method static Builder|Application whereWebsite($value)
|
||||
*/
|
||||
class Application extends Model
|
||||
{
|
||||
/**
|
||||
* @var bool
|
||||
*/
|
||||
public $incrementing = false;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
protected $primaryKey = 'appid';
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
public function icon()
|
||||
{
|
||||
if (! file_exists(storage_path('app/public/'.$this->icon))) {
|
||||
$img_src = app_path('SupportedApps/'.$this->name.'/'.str_replace('icons/', '', $this->icon));
|
||||
$img_dest = storage_path('app/public/'.$this->icon);
|
||||
//die("i: ".$img_src);
|
||||
@copy($img_src, $img_dest);
|
||||
}
|
||||
|
||||
return $this->icon;
|
||||
}
|
||||
|
||||
public function iconView(): string
|
||||
{
|
||||
return asset('storage/'.$this->icon);
|
||||
}
|
||||
|
||||
public function defaultColour(): string
|
||||
{
|
||||
// check if light or dark
|
||||
if ($this->tile_background == 'light') {
|
||||
return '#fafbfc';
|
||||
}
|
||||
|
||||
return '#161b1f';
|
||||
}
|
||||
|
||||
public function class(): string
|
||||
{
|
||||
$name = $this->name;
|
||||
$name = preg_replace('/[^\p{L}\p{N}]/u', '', $name);
|
||||
|
||||
return \App\SupportedApps::class.'\\'.$name.'\\'.$name;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $name
|
||||
*/
|
||||
public static function classFromName($name): string
|
||||
{
|
||||
$name = preg_replace('/[^\p{L}\p{N}]/u', '', $name);
|
||||
|
||||
$class = \App\SupportedApps::class.'\\'.$name.'\\'.$name;
|
||||
|
||||
return $class;
|
||||
}
|
||||
|
||||
public static function apps(): Collection
|
||||
{
|
||||
$json = json_decode(file_get_contents(storage_path('app/supportedapps.json'))) ?? [];
|
||||
$apps = collect($json->apps);
|
||||
|
||||
return $apps->sortBy('name', SORT_NATURAL | SORT_FLAG_CASE);
|
||||
}
|
||||
|
||||
public static function autocomplete(): array
|
||||
{
|
||||
$apps = self::apps();
|
||||
$list = [];
|
||||
foreach ($apps as $app) {
|
||||
$list[] = (object) [
|
||||
'label' => $app->name,
|
||||
'value' => $app->appid,
|
||||
];
|
||||
}
|
||||
|
||||
return $list;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $appid
|
||||
* @return mixed|null
|
||||
* @throws GuzzleException
|
||||
*/
|
||||
public static function getApp($appid)
|
||||
{
|
||||
Log::debug("Get app triggered for: $appid");
|
||||
|
||||
$localapp = self::where('appid', $appid)->first();
|
||||
$app = self::single($appid);
|
||||
|
||||
$application = ($localapp) ? $localapp : new self;
|
||||
|
||||
// Files missing? || app not in db || old sha version
|
||||
if (! file_exists(app_path('SupportedApps/'.className($app->name))) ||
|
||||
! $localapp ||
|
||||
$localapp->sha !== $app->sha
|
||||
) {
|
||||
$gotFiles = SupportedApps::getFiles($app);
|
||||
if ($gotFiles) {
|
||||
$app = SupportedApps::saveApp($app, $application);
|
||||
}
|
||||
}
|
||||
|
||||
return $app;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $appid
|
||||
* @return mixed|null
|
||||
*/
|
||||
public static function single($appid)
|
||||
{
|
||||
$apps = self::apps();
|
||||
$app = $apps->where('appid', $appid)->first();
|
||||
|
||||
if ($app === null) {
|
||||
// Try in db for Private App
|
||||
$appModel = self::where('appid', $appid)->first();
|
||||
if ($appModel) {
|
||||
$app = json_decode($appModel->toJson());
|
||||
}
|
||||
}
|
||||
|
||||
if ($app === null) {
|
||||
return null;
|
||||
}
|
||||
$classname = preg_replace('/[^\p{L}\p{N}]/u', '', $app->name);
|
||||
$app->class = \App\SupportedApps::class.'\\'.$classname.'\\'.$classname;
|
||||
|
||||
return $app;
|
||||
}
|
||||
|
||||
public static function applist(): array
|
||||
{
|
||||
$list = [];
|
||||
$list['null'] = 'None';
|
||||
$apps = self::apps();
|
||||
foreach ($apps as $app) {
|
||||
$list[$app->appid] = $app->name;
|
||||
}
|
||||
|
||||
// Check for private apps in the db
|
||||
$appsListFromDB = self::all(['appid', 'name']);
|
||||
|
||||
foreach ($appsListFromDB as $app) {
|
||||
// Already existing keys are overwritten,
|
||||
// only private apps should be added at the end
|
||||
$list[$app->appid] = $app->name;
|
||||
}
|
||||
|
||||
return $list;
|
||||
}
|
||||
}
|
||||
102
app/Console/Commands/RegisterApp.php
Normal file
102
app/Console/Commands/RegisterApp.php
Normal file
@@ -0,0 +1,102 @@
|
||||
<?php
|
||||
|
||||
namespace App\Console\Commands;
|
||||
|
||||
use App\Application;
|
||||
use App\SupportedApps;
|
||||
use Illuminate\Console\Command;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
|
||||
class RegisterApp extends Command
|
||||
{
|
||||
/**
|
||||
* The name and signature of the console command.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $signature = 'register:app {folder} {--remove}';
|
||||
|
||||
/**
|
||||
* The console command description.
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $description = 'Add a local app to the registry';
|
||||
|
||||
/**
|
||||
* Create a new command instance.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the console command.
|
||||
*/
|
||||
public function handle(): void
|
||||
{
|
||||
$folder = $this->argument('folder');
|
||||
if ($folder == 'all') {
|
||||
$apps = scandir(app_path('SupportedApps'));
|
||||
foreach ($apps as $folder) {
|
||||
if ($folder == '.' || $folder == '..') {
|
||||
continue;
|
||||
}
|
||||
$this->addApp($folder);
|
||||
}
|
||||
} else {
|
||||
$this->addApp($folder, $this->option('remove'));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $folder
|
||||
*/
|
||||
public function addApp($folder, bool $remove = false): void
|
||||
{
|
||||
$json = app_path('SupportedApps/'.$folder.'/app.json');
|
||||
|
||||
if (!file_exists($json)) {
|
||||
$this->error('Could not find ' . $json);
|
||||
return;
|
||||
}
|
||||
|
||||
$app = json_decode(file_get_contents($json));
|
||||
|
||||
if (!isset($app->appid)) {
|
||||
$this->error('No App ID for - ' . $folder);
|
||||
return;
|
||||
}
|
||||
|
||||
$exists = Application::find($app->appid);
|
||||
|
||||
if ($exists) {
|
||||
if ($remove) {
|
||||
$exists->delete();
|
||||
$this->info('Application Removed - ' . $app->name . ' - ' . $app->appid);
|
||||
return;
|
||||
}
|
||||
$this->error('Application already registered - ' . $exists->name . ' - ' . $exists->appid);
|
||||
return;
|
||||
}
|
||||
|
||||
// Doesn't exist so add it
|
||||
SupportedApps::saveApp($app, new Application);
|
||||
$this->saveIcon($folder, $app->icon);
|
||||
$this->info('Application Added - ' . $app->name . ' - ' . $app->appid);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $appFolder
|
||||
* @param $icon
|
||||
*/
|
||||
private function saveIcon($appFolder, $icon): void
|
||||
{
|
||||
$iconPath = app_path('SupportedApps/' . $appFolder . '/' . $icon);
|
||||
$contents = file_get_contents($iconPath);
|
||||
Storage::disk('public')->put('icons/'.$icon, $contents);
|
||||
}
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Console;
|
||||
|
||||
use Illuminate\Console\Scheduling\Schedule;
|
||||
use Illuminate\Foundation\Console\Kernel as ConsoleKernel;
|
||||
|
||||
class Kernel extends ConsoleKernel
|
||||
{
|
||||
/**
|
||||
* The Artisan commands provided by your application.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $commands = [
|
||||
//
|
||||
];
|
||||
|
||||
/**
|
||||
* Define the application's command schedule.
|
||||
*
|
||||
* @param \Illuminate\Console\Scheduling\Schedule $schedule
|
||||
* @return void
|
||||
*/
|
||||
protected function schedule(Schedule $schedule)
|
||||
{
|
||||
// $schedule->command('inspire')
|
||||
// ->hourly();
|
||||
}
|
||||
|
||||
/**
|
||||
* Register the commands for the application.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function commands()
|
||||
{
|
||||
$this->load(__DIR__.'/Commands');
|
||||
|
||||
require base_path('routes/console.php');
|
||||
}
|
||||
}
|
||||
12
app/EnhancedApps.php
Normal file
12
app/EnhancedApps.php
Normal file
@@ -0,0 +1,12 @@
|
||||
<?php
|
||||
|
||||
namespace App;
|
||||
|
||||
interface EnhancedApps
|
||||
{
|
||||
public function test();
|
||||
|
||||
public function livestats();
|
||||
|
||||
public function url($endpoint);
|
||||
}
|
||||
@@ -1,53 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Exceptions;
|
||||
|
||||
use Exception;
|
||||
use Illuminate\Foundation\Exceptions\Handler as ExceptionHandler;
|
||||
|
||||
class Handler extends ExceptionHandler
|
||||
{
|
||||
/**
|
||||
* A list of the exception types that are not reported.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $dontReport = [
|
||||
//
|
||||
];
|
||||
|
||||
/**
|
||||
* A list of the inputs that are never flashed for validation exceptions.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $dontFlash = [
|
||||
'password',
|
||||
'password_confirmation',
|
||||
];
|
||||
|
||||
/**
|
||||
* Report or log an exception.
|
||||
*
|
||||
* This is a great spot to send exceptions to Sentry, Bugsnag, etc.
|
||||
*
|
||||
* @param \Exception $exception
|
||||
* @return void
|
||||
*/
|
||||
public function report(Exception $exception)
|
||||
{
|
||||
parent::report($exception);
|
||||
}
|
||||
|
||||
/**
|
||||
* Render an exception into an HTTP response.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @param \Exception $exception
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function render($request, Exception $exception)
|
||||
{
|
||||
return parent::render($request, $exception);
|
||||
}
|
||||
}
|
||||
13
app/Facades/Form.php
Normal file
13
app/Facades/Form.php
Normal file
@@ -0,0 +1,13 @@
|
||||
<?php
|
||||
|
||||
namespace App\Facades;
|
||||
|
||||
use Illuminate\Support\Facades\Facade;
|
||||
|
||||
class Form extends Facade
|
||||
{
|
||||
protected static function getFacadeAccessor()
|
||||
{
|
||||
return 'custom-form';
|
||||
}
|
||||
}
|
||||
147
app/Helper.php
147
app/Helper.php
@@ -1,12 +1,143 @@
|
||||
<?php
|
||||
|
||||
function format_bytes($bytes, $is_drive_size = true, $beforeunit = '', $afterunit = '')
|
||||
use Illuminate\Support\Str;
|
||||
use enshrined\svgSanitize\Sanitizer;
|
||||
|
||||
/**
|
||||
* @param $bytes
|
||||
* @param bool $is_drive_size
|
||||
* @param string $beforeunit
|
||||
* @param string $afterunit
|
||||
* @return string
|
||||
*/
|
||||
function format_bytes($bytes, bool $is_drive_size = true, string $beforeunit = '', string $afterunit = ''): string
|
||||
{
|
||||
$btype = ($is_drive_size === true) ? 1000 : 1024;
|
||||
$labels = array('B','KB','MB','GB','TB');
|
||||
for($x = 0; $bytes >= $btype && $x < (count($labels) - 1); $bytes /= $btype, $x++); // use 1000 rather than 1024 to simulate HD size not real size
|
||||
if($labels[$x] == "TB") return(round($bytes, 3).$beforeunit.$labels[$x].$afterunit);
|
||||
elseif($labels[$x] == "GB") return(round($bytes, 2).$beforeunit.$labels[$x].$afterunit);
|
||||
elseif($labels[$x] == "MB") return(round($bytes, 2).$beforeunit.$labels[$x].$afterunit);
|
||||
else return(round($bytes, 0).$beforeunit.$labels[$x].$afterunit);
|
||||
$btype = ($is_drive_size === true) ? 1000 : 1024;
|
||||
$labels = ['B', 'KB', 'MB', 'GB', 'TB'];
|
||||
// use 1000 rather than 1024 to simulate HD size not real size
|
||||
for ($x = 0; $bytes >= $btype && $x < (count($labels) - 1); $bytes /= $btype, $x++) ;
|
||||
if ($labels[$x] == 'TB') {
|
||||
return round($bytes, 3) . $beforeunit . $labels[$x] . $afterunit;
|
||||
} elseif ($labels[$x] == 'GB') {
|
||||
return round($bytes, 2) . $beforeunit . $labels[$x] . $afterunit;
|
||||
} elseif ($labels[$x] == 'MB') {
|
||||
return round($bytes, 2) . $beforeunit . $labels[$x] . $afterunit;
|
||||
} else {
|
||||
return round($bytes, 0) . $beforeunit . $labels[$x] . $afterunit;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $title
|
||||
* @param string $separator
|
||||
* @param string $language
|
||||
* @return string
|
||||
*/
|
||||
function str_slug($title, string $separator = '-', string $language = 'en'): string
|
||||
{
|
||||
return Str::slug($title, $separator, $language);
|
||||
}
|
||||
|
||||
if (!function_exists('str_is')) {
|
||||
/**
|
||||
* Determine if a given string matches a given pattern.
|
||||
*
|
||||
* @param string|array $pattern
|
||||
* @param string $value
|
||||
* @return bool
|
||||
*
|
||||
* @deprecated Str::is() should be used directly instead. Will be removed in Laravel 6.0.
|
||||
*/
|
||||
function str_is($pattern, string $value): bool
|
||||
{
|
||||
return Str::is($pattern, $value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $hex
|
||||
* @return float|int
|
||||
*/
|
||||
function get_brightness($hex)
|
||||
{
|
||||
// returns brightness value from 0 to 255
|
||||
// strip off any leading #
|
||||
// $hex = str_replace('#', '', $hex);
|
||||
$hex = preg_replace("/[^0-9A-Fa-f]/", '', $hex);
|
||||
if (strlen($hex) == 3) {
|
||||
$hex = $hex[0] . $hex[0] . $hex[1] . $hex[1] . $hex[2] . $hex[2];
|
||||
}
|
||||
|
||||
$c_r = hexdec(substr($hex, 0, 2));
|
||||
$c_g = hexdec(substr($hex, 2, 2));
|
||||
$c_b = hexdec(substr($hex, 4, 2));
|
||||
|
||||
return (($c_r * 299) + ($c_g * 587) + ($c_b * 114)) / 1000;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $hex
|
||||
* @return string
|
||||
*/
|
||||
function title_color($hex): string
|
||||
{
|
||||
if (get_brightness($hex) > 130) {
|
||||
return ' black';
|
||||
} else {
|
||||
return ' white';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string
|
||||
*/
|
||||
function getLinkTargetAttribute(): string
|
||||
{
|
||||
$target = \App\Setting::fetch('window_target');
|
||||
|
||||
if ($target === 'current') {
|
||||
return '';
|
||||
} else {
|
||||
return ' target="' . $target . '"';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $name
|
||||
* @return array|string|string[]|null
|
||||
*/
|
||||
function className($name)
|
||||
{
|
||||
return preg_replace('/[^\p{L}\p{N}]/u', '', $name);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $file
|
||||
* @param string $extension
|
||||
* @return bool
|
||||
*/
|
||||
function isImage(string $file, string $extension): bool
|
||||
{
|
||||
$allowedExtensions = ['jpg', 'jpeg', 'png', 'bmp', 'gif', 'svg', 'webp', 'ico'];
|
||||
|
||||
if (!in_array($extension, $allowedExtensions)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$tempFileName = @tempnam("/tmp", "image-check-");
|
||||
$handle = fopen($tempFileName, "w");
|
||||
|
||||
fwrite($handle, $file);
|
||||
fclose($handle);
|
||||
|
||||
if ($extension === 'svg') {
|
||||
$sanitizer = new Sanitizer();
|
||||
$sanitizedSvg = $sanitizer->sanitize(file_get_contents($tempFileName));
|
||||
file_put_contents($tempFileName, $sanitizedSvg);
|
||||
|
||||
return 'image/svg+xml' === mime_content_type($tempFileName);
|
||||
}
|
||||
|
||||
$size = @getimagesize($tempFileName);
|
||||
return is_array($size) && str_starts_with($size['mime'], 'image');
|
||||
}
|
||||
|
||||
@@ -3,7 +3,18 @@
|
||||
namespace App\Http\Controllers\Auth;
|
||||
|
||||
use App\Http\Controllers\Controller;
|
||||
use App\User;
|
||||
use Illuminate\Contracts\Foundation\Application;
|
||||
use Illuminate\Contracts\View\Factory;
|
||||
use Illuminate\Contracts\View\View;
|
||||
use Illuminate\Foundation\Auth\AuthenticatesUsers;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Facades\Session;
|
||||
use Illuminate\Support\Facades\URL;
|
||||
use Illuminate\Validation\ValidationException;
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
|
||||
class LoginController extends Controller
|
||||
{
|
||||
@@ -25,7 +36,7 @@ class LoginController extends Controller
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $redirectTo = '/home';
|
||||
protected string $redirectTo = '/';
|
||||
|
||||
/**
|
||||
* Create a new controller instance.
|
||||
@@ -34,6 +45,104 @@ class LoginController extends Controller
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
$this->middleware('guest')->except('logout');
|
||||
Session::put('backUrl', URL::previous());
|
||||
$this->middleware('guest')->except(['logout','autologin']);
|
||||
}
|
||||
|
||||
public function username(): string
|
||||
{
|
||||
return 'username';
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle a login request to the application.
|
||||
*
|
||||
*
|
||||
* @throws ValidationException
|
||||
*/
|
||||
public function login(Request $request): Response
|
||||
{
|
||||
$current_user = User::currentUser();
|
||||
$request->merge(['username' => $current_user->username, 'remember' => true]);
|
||||
//die(print_r($request->all()));
|
||||
$this->validateLogin($request);
|
||||
|
||||
// If the class is using the ThrottlesLogins trait, we can automatically throttle
|
||||
// the login attempts for this application. We'll key this by the username and
|
||||
// the IP address of the client making these requests into this application.
|
||||
if ($this->hasTooManyLoginAttempts($request)) {
|
||||
$this->fireLockoutEvent($request);
|
||||
|
||||
return $this->sendLockoutResponse($request);
|
||||
}
|
||||
|
||||
if ($this->attemptLogin($request)) {
|
||||
return $this->sendLoginResponse($request);
|
||||
}
|
||||
|
||||
// If the login attempt was unsuccessful we will increment the number of attempts
|
||||
// to login and redirect the user back to the login form. Of course, when this
|
||||
// user surpasses their maximum number of attempts they will get locked out.
|
||||
$this->incrementLoginAttempts($request);
|
||||
|
||||
return $this->sendFailedLoginResponse($request);
|
||||
}
|
||||
|
||||
public function index()
|
||||
{
|
||||
}
|
||||
|
||||
public function setUser(User $user): RedirectResponse
|
||||
{
|
||||
Auth::logout();
|
||||
session(['current_user' => $user]);
|
||||
|
||||
return redirect()->route('dash');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $uuid
|
||||
*/
|
||||
public function autologin($uuid): RedirectResponse
|
||||
{
|
||||
Auth::logout();
|
||||
|
||||
$user = User::where('autologin', $uuid)->first();
|
||||
|
||||
if (!$user) {
|
||||
return redirect()->route('dash');
|
||||
}
|
||||
|
||||
Auth::login($user, true);
|
||||
|
||||
session(['current_user' => $user]);
|
||||
|
||||
return redirect()->route('dash');
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the application's login form.
|
||||
*
|
||||
* @return Application|Factory|View
|
||||
*/
|
||||
public function showLoginForm(): \Illuminate\View\View
|
||||
{
|
||||
return view('auth.login');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $user
|
||||
*/
|
||||
protected function authenticated(Request $request, $user): RedirectResponse
|
||||
{
|
||||
return back();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed|string
|
||||
*/
|
||||
public function redirectTo()
|
||||
{
|
||||
return Session::get('url.intended') ? Session::get('url.intended') : $this->redirectTo;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,10 +2,10 @@
|
||||
|
||||
namespace App\Http\Controllers\Auth;
|
||||
|
||||
use App\User;
|
||||
use App\Http\Controllers\Controller;
|
||||
use Illuminate\Support\Facades\Validator;
|
||||
use App\User;
|
||||
use Illuminate\Foundation\Auth\RegistersUsers;
|
||||
use Illuminate\Support\Facades\Validator;
|
||||
|
||||
class RegisterController extends Controller
|
||||
{
|
||||
@@ -27,7 +27,7 @@ class RegisterController extends Controller
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $redirectTo = '/home';
|
||||
protected string $redirectTo = '/';
|
||||
|
||||
/**
|
||||
* Create a new controller instance.
|
||||
@@ -41,11 +41,8 @@ class RegisterController extends Controller
|
||||
|
||||
/**
|
||||
* Get a validator for an incoming registration request.
|
||||
*
|
||||
* @param array $data
|
||||
* @return \Illuminate\Contracts\Validation\Validator
|
||||
*/
|
||||
protected function validator(array $data)
|
||||
protected function validator(array $data): \Illuminate\Contracts\Validation\Validator
|
||||
{
|
||||
return Validator::make($data, [
|
||||
'name' => 'required|string|max:255',
|
||||
@@ -56,11 +53,8 @@ class RegisterController extends Controller
|
||||
|
||||
/**
|
||||
* Create a new user instance after a valid registration.
|
||||
*
|
||||
* @param array $data
|
||||
* @return \App\User
|
||||
*/
|
||||
protected function create(array $data)
|
||||
protected function create(array $data): User
|
||||
{
|
||||
return User::create([
|
||||
'name' => $data['name'],
|
||||
|
||||
@@ -25,7 +25,7 @@ class ResetPasswordController extends Controller
|
||||
*
|
||||
* @var string
|
||||
*/
|
||||
protected $redirectTo = '/home';
|
||||
protected string $redirectTo = '/';
|
||||
|
||||
/**
|
||||
* Create a new controller instance.
|
||||
|
||||
@@ -2,12 +2,28 @@
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use Illuminate\Foundation\Bus\DispatchesJobs;
|
||||
use Illuminate\Routing\Controller as BaseController;
|
||||
use Illuminate\Foundation\Validation\ValidatesRequests;
|
||||
use App\User;
|
||||
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
|
||||
use Illuminate\Foundation\Validation\ValidatesRequests;
|
||||
use Illuminate\Routing\Controller as BaseController;
|
||||
|
||||
class Controller extends BaseController
|
||||
{
|
||||
use AuthorizesRequests, DispatchesJobs, ValidatesRequests;
|
||||
use AuthorizesRequests, ValidatesRequests;
|
||||
|
||||
protected $user;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->middleware(function ($request, $next) {
|
||||
$this->user = $this->user();
|
||||
//print_r($this->user);
|
||||
return $next($request);
|
||||
});
|
||||
}
|
||||
|
||||
public function user()
|
||||
{
|
||||
return User::currentUser();
|
||||
}
|
||||
}
|
||||
|
||||
51
app/Http/Controllers/HealthController.php
Normal file
51
app/Http/Controllers/HealthController.php
Normal file
@@ -0,0 +1,51 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Item;
|
||||
use App\User;
|
||||
use Illuminate\Contracts\Container\BindingResolutionException;
|
||||
use Illuminate\Http\JsonResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Http\Response;
|
||||
use Illuminate\Support\Facades\RateLimiter;
|
||||
|
||||
class HealthController extends Controller
|
||||
{
|
||||
private static function getUsers(): int
|
||||
{
|
||||
return User::count();
|
||||
}
|
||||
|
||||
private static function getItems(): int
|
||||
{
|
||||
return Item::select('id')
|
||||
->where('deleted_at', null)
|
||||
->where('type', '0')
|
||||
->count();
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle the incoming request.
|
||||
*
|
||||
* @return JsonResponse|Response
|
||||
* @throws BindingResolutionException
|
||||
*/
|
||||
public function __invoke(Request $request)
|
||||
{
|
||||
$REQUESTS_MAX_PER_MIN = 30;
|
||||
$STATUS_TOO_MANY_REQUESTS = 429;
|
||||
|
||||
if (RateLimiter::remaining('health', $REQUESTS_MAX_PER_MIN) < 1) {
|
||||
return response()->make('Too many attempts.', $STATUS_TOO_MANY_REQUESTS);
|
||||
}
|
||||
|
||||
RateLimiter::hit('health');
|
||||
|
||||
return response()->json([
|
||||
'status' => 'ok',
|
||||
'items' => self::getItems(),
|
||||
'users' => self::getUsers(),
|
||||
]);
|
||||
}
|
||||
}
|
||||
27
app/Http/Controllers/HomeController.php
Normal file
27
app/Http/Controllers/HomeController.php
Normal file
@@ -0,0 +1,27 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
|
||||
class HomeController extends Controller
|
||||
{
|
||||
/**
|
||||
* Create a new controller instance.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
$this->middleware('auth');
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the application dashboard.
|
||||
*/
|
||||
public function index(): RedirectResponse
|
||||
{
|
||||
return redirect()->route('dash');
|
||||
}
|
||||
}
|
||||
28
app/Http/Controllers/ImportController.php
Normal file
28
app/Http/Controllers/ImportController.php
Normal file
@@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\View\View;
|
||||
|
||||
class ImportController extends Controller
|
||||
{
|
||||
/**
|
||||
* Instantiate a new controller instance.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
$this->middleware('allowed');
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle the incoming request.
|
||||
*/
|
||||
public function __invoke(Request $request): View
|
||||
{
|
||||
return view('items.import');
|
||||
}
|
||||
}
|
||||
@@ -2,238 +2,389 @@
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
use App\Application;
|
||||
use App\Item;
|
||||
use App\Setting;
|
||||
use App\SupportedApps\Nzbget;
|
||||
use App\Jobs\ProcessApps;
|
||||
use App\User;
|
||||
use GuzzleHttp\Client;
|
||||
use GuzzleHttp\Exception\ConnectException;
|
||||
use GuzzleHttp\Exception\GuzzleException;
|
||||
use GuzzleHttp\Exception\ServerException;
|
||||
use Illuminate\Contracts\View\View;
|
||||
use Illuminate\Database\Eloquent\Collection;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Routing\Redirector;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
use Illuminate\Support\Facades\URL;
|
||||
use Illuminate\Validation\ValidationException;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
use Psr\Http\Message\StreamInterface;
|
||||
use Illuminate\Http\Response;
|
||||
use enshrined\svgSanitize\Sanitizer;
|
||||
|
||||
class ItemController extends Controller
|
||||
{
|
||||
|
||||
/**
|
||||
* Display a listing of the resource on the dashboard.
|
||||
*
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function dash()
|
||||
public function __construct()
|
||||
{
|
||||
$data['apps'] = Item::doesntHave('parents')->pinned()->orderBy('order', 'asc')->get();
|
||||
$data['all_apps'] = Item::doesntHave('parents')->get();
|
||||
parent::__construct();
|
||||
$this->middleware('allowed');
|
||||
}
|
||||
|
||||
/**
|
||||
* Display a listing of the resource on the dashboard.
|
||||
*/
|
||||
public function dash(Request $request): View
|
||||
{
|
||||
$treat_tags_as = \App\Setting::fetch('treat_tags_as');
|
||||
|
||||
$data["treat_tags_as"] = $treat_tags_as;
|
||||
|
||||
if (config('app.auth_roles_enable')) {
|
||||
$roles = explode(config('app.auth_roles_delimiter'), $request->header(config('app.auth_roles_header')));
|
||||
if ($treat_tags_as == 'categories') {
|
||||
$data['categories'] = Item::whereHas('children')->with('children', function ($query) {
|
||||
$query->pinned()->orderBy('order', 'asc');
|
||||
})->pinned()->orderBy('order', 'asc')->get();
|
||||
} elseif ($treat_tags_as == 'tags') {
|
||||
$data['apps'] = Item::with('parents')->where('type', 0)->pinned()->orderBy('order', 'asc')->get();
|
||||
$data['all_apps'] = Item::where('type', 0)->orderBy('order', 'asc')->get();
|
||||
$data['taglist'] = Item::where('id', 0)->orWhere(function ($query) {
|
||||
$query->where('type', 1)->pinned();
|
||||
})->orderBy('order', 'asc')->get();
|
||||
} else {
|
||||
$data['apps'] = Item::whereHas('parents', function ($query) {
|
||||
$query->where('id', 0);
|
||||
})->whereIn('role', $roles)->orWhere('type', 1)->pinned()->orderBy('order', 'asc')->get();
|
||||
|
||||
$data['all_apps'] = Item::whereHas('parents', function ($query) {
|
||||
$query->where('id', 0);
|
||||
})->orWhere('type', 1)->orderBy('order', 'asc')->get();
|
||||
}
|
||||
} else {
|
||||
if ($treat_tags_as == 'categories') {
|
||||
$data['categories'] = Item::whereHas('children')->with('children', function ($query) {
|
||||
$query->pinned()->orderBy('order', 'asc');
|
||||
})->pinned()->orderBy('order', 'asc')->get();
|
||||
} elseif ($treat_tags_as == 'tags') {
|
||||
$data['apps'] = Item::with('parents')->where('type', 0)->pinned()->orderBy('order', 'asc')->get();
|
||||
$data['all_apps'] = Item::where('type', 0)->orderBy('order', 'asc')->get();
|
||||
$data['taglist'] = Item::where('id', 0)->orWhere(function ($query) {
|
||||
$query->where('type', 1)->pinned();
|
||||
})->orderBy('order', 'asc')->get();
|
||||
} else {
|
||||
$data['apps'] = Item::whereHas('parents', function ($query) {
|
||||
$query->where('id', 0);
|
||||
})->orWhere('type', 1)->pinned()->orderBy('order', 'asc')->get();
|
||||
|
||||
$data['all_apps'] = Item::whereHas('parents', function ($query) {
|
||||
$query->where('id', 0);
|
||||
})->orWhere(function ($query) {
|
||||
$query->where('type', 1)->whereNot('id', 0);
|
||||
})->orderBy('order', 'asc')->get();
|
||||
}
|
||||
}
|
||||
|
||||
//$data['all_apps'] = Item::doesntHave('parents')->get();
|
||||
// die(print_r($data));
|
||||
return view('welcome', $data);
|
||||
}
|
||||
|
||||
/**
|
||||
/**
|
||||
* Set order on the dashboard.
|
||||
*
|
||||
* @return \Illuminate\Http\Response
|
||||
* @return void
|
||||
*/
|
||||
public function setOrder(Request $request)
|
||||
{
|
||||
$order = array_filter($request->input('order'));
|
||||
foreach($order as $o => $id) {
|
||||
foreach ($order as $o => $id) {
|
||||
$item = Item::find($id);
|
||||
$item->order = $o;
|
||||
$item->save();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
/**
|
||||
* Pin item on the dashboard.
|
||||
*
|
||||
* @return \Illuminate\Http\Response
|
||||
* @param $id
|
||||
*/
|
||||
public function pin($id)
|
||||
public function pin($id): RedirectResponse
|
||||
{
|
||||
$item = Item::findOrFail($id);
|
||||
$item->pinned = true;
|
||||
$item->save();
|
||||
$route = route('dash', [], false);
|
||||
$route = route('dash', []);
|
||||
|
||||
return redirect($route);
|
||||
}
|
||||
|
||||
/**
|
||||
/**
|
||||
* Unpin item on the dashboard.
|
||||
*
|
||||
* @return \Illuminate\Http\Response
|
||||
* @param $id
|
||||
*/
|
||||
public function unpin($id)
|
||||
public function unpin($id): RedirectResponse
|
||||
{
|
||||
$item = Item::findOrFail($id);
|
||||
$item->pinned = false;
|
||||
$item->save();
|
||||
$route = route('dash', [], false);
|
||||
$route = route('dash', []);
|
||||
|
||||
return redirect($route);
|
||||
}
|
||||
|
||||
/**
|
||||
/**
|
||||
* Unpin item on the dashboard.
|
||||
*
|
||||
* @return \Illuminate\Http\Response
|
||||
* @return RedirectResponse|View
|
||||
*/
|
||||
public function pinToggle($id, $ajax=false)
|
||||
public function pinToggle($id, $ajax = false, $tag = false)
|
||||
{
|
||||
$item = Item::findOrFail($id);
|
||||
$new = ((bool)$item->pinned === true) ? false : true;
|
||||
$new = !(((bool)$item->pinned === true));
|
||||
$item->pinned = $new;
|
||||
$item->save();
|
||||
if($ajax) {
|
||||
$data['apps'] = Item::pinned()->get();
|
||||
|
||||
if ($ajax) {
|
||||
$item = Item::whereId($tag)->first();
|
||||
|
||||
$data['apps'] = new Collection;
|
||||
|
||||
if ((int)$tag === 0) {
|
||||
$tags = Item::where('type', 1)->pinned()->orderBy('order', 'asc')->get();
|
||||
$data['apps'] = $data['apps']->merge($tags);
|
||||
}
|
||||
|
||||
$apps = $item->children()->pinned()->orderBy('order', 'asc')->get();
|
||||
$data['apps'] = $data['apps']->merge($apps);
|
||||
|
||||
|
||||
$data['ajax'] = true;
|
||||
|
||||
return view('sortable', $data);
|
||||
} else {
|
||||
$route = route('dash', [], false);
|
||||
$route = route('dash', []);
|
||||
|
||||
return redirect($route);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Display a listing of the resource.
|
||||
*
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function index(Request $request)
|
||||
public function index(Request $request): View
|
||||
{
|
||||
$trash = (bool)$request->input('trash');
|
||||
|
||||
$data['apps'] = Item::ofType('item')->orderBy('title', 'asc')->get();
|
||||
$data['trash'] = Item::ofType('item')->onlyTrashed()->get();
|
||||
if($trash) {
|
||||
if ($trash) {
|
||||
return view('items.trash', $data);
|
||||
} else {
|
||||
return view('items.list', $data);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Show the form for creating a new resource.
|
||||
*
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function create()
|
||||
public function create(): View
|
||||
{
|
||||
//
|
||||
$data['item'] = new \App\Item();
|
||||
$data['tags'] = Item::ofType('tag')->orderBy('title', 'asc')->pluck('title', 'id');
|
||||
$data['current_tags'] = [];
|
||||
return view('items.create', $data);
|
||||
$data['tags']->prepend(__('app.dashboard'), 0);
|
||||
$data['current_tags'] = '0';
|
||||
|
||||
return view('items.create', $data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Store a newly created resource in storage.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @return \Illuminate\Http\Response
|
||||
* Show the form for editing the specified resource.
|
||||
*/
|
||||
public function store(Request $request)
|
||||
public function edit(int $id): View
|
||||
{
|
||||
//
|
||||
// Get the item
|
||||
$item = Item::find($id);
|
||||
if ($item->appid === null && $item->class !== null) { // old apps won't have an app id so set it
|
||||
$app = Application::where('class', $item->class)->first();
|
||||
if ($app) {
|
||||
$item->appid = $app->appid;
|
||||
}
|
||||
}
|
||||
$data['item'] = $item;
|
||||
$data['tags'] = Item::ofType('tag')->orderBy('title', 'asc')->pluck('title', 'id');
|
||||
$data['tags']->prepend(__('app.dashboard'), 0);
|
||||
$data['current_tags'] = $data['item']->tags();
|
||||
//$data['current_tags'] = $data['item']->parent;
|
||||
//die(print_r($data['current_tags']));
|
||||
// show the edit form and pass the nerd
|
||||
return view('items.edit', $data);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param null $id
|
||||
* @throws ValidationException
|
||||
*/
|
||||
public static function storelogic(Request $request, $id = null): Item
|
||||
{
|
||||
$application = Application::single($request->input('appid'));
|
||||
$validatedData = $request->validate([
|
||||
'title' => 'required|max:255',
|
||||
'url' => 'required|url',
|
||||
'url' => 'required',
|
||||
'file' => 'image'
|
||||
]);
|
||||
|
||||
if($request->hasFile('file')) {
|
||||
$path = $request->file('file')->store('icons');
|
||||
if ($request->hasFile('file')) {
|
||||
$image = $request->file('file');
|
||||
$extension = $image->getClientOriginalExtension();
|
||||
|
||||
if ($extension === 'svg') {
|
||||
$sanitizer = new Sanitizer();
|
||||
$sanitizedSvg = $sanitizer->sanitize(file_get_contents($image->getRealPath()));
|
||||
|
||||
// Verify that the sanitization removed malicious content
|
||||
if (strpos($sanitizedSvg, '<script>') !== false) {
|
||||
throw ValidationException::withMessages(['file' => 'SVG contains malicious content and cannot be uploaded.']);
|
||||
}
|
||||
|
||||
// Save the sanitized SVG back to the file
|
||||
file_put_contents($image->getRealPath(), $sanitizedSvg);
|
||||
}
|
||||
|
||||
$path = $image->store('icons', 'public');
|
||||
$request->merge([
|
||||
'icon' => $path
|
||||
'icon' => $path,
|
||||
]);
|
||||
} elseif (strpos($request->input('icon'), 'http') === 0) {
|
||||
$options = [
|
||||
"ssl" => [
|
||||
"verify_peer" => false,
|
||||
"verify_peer_name" => false,
|
||||
],
|
||||
];
|
||||
|
||||
$file = $request->input('icon');
|
||||
$path_parts = pathinfo($file);
|
||||
if (!isset($path_parts['extension'])) {
|
||||
throw ValidationException::withMessages(['file' => 'Icon URL must have a valid file extension.']);
|
||||
}
|
||||
$extension = $path_parts['extension'];
|
||||
|
||||
$contents = file_get_contents($request->input('icon'), false, stream_context_create($options));
|
||||
|
||||
if ($extension === 'svg') {
|
||||
$sanitizer = new Sanitizer();
|
||||
$contents = $sanitizer->sanitize($contents);
|
||||
|
||||
// Verify that the sanitization removed malicious content
|
||||
if (strpos($contents, '<script>') !== false) {
|
||||
throw ValidationException::withMessages(['file' => 'SVG contains malicious content and cannot be uploaded.']);
|
||||
}
|
||||
}
|
||||
|
||||
if (!isImage($contents, $extension)) {
|
||||
throw ValidationException::withMessages(['file' => 'Icon must be an image.']);
|
||||
}
|
||||
|
||||
$path = 'icons/' . ($application ? $application->icon : md5($contents) . '.' . $extension);
|
||||
|
||||
// Private apps could have here duplicated icons folder
|
||||
if (strpos($path, 'icons/icons/') !== false) {
|
||||
$path = str_replace('icons/icons/', 'icons/', $path);
|
||||
}
|
||||
if (!Storage::disk('public')->exists($path)) {
|
||||
Storage::disk('public')->put($path, $contents);
|
||||
}
|
||||
$request->merge([
|
||||
'icon' => $path,
|
||||
]);
|
||||
}
|
||||
|
||||
$config = Item::checkConfig($request->input('config'));
|
||||
|
||||
// Don't overwrite the stored password if it wasn't submitted when updating the item
|
||||
if ($id !== null && strpos($config, '"password":null') !== false) {
|
||||
$storedItem = Item::find($id);
|
||||
$storedConfigObject = json_decode($storedItem->getAttribute('description'));
|
||||
|
||||
$configObject = json_decode($config);
|
||||
$configObject->password = $storedConfigObject->password;
|
||||
|
||||
$config = json_encode($configObject);
|
||||
}
|
||||
|
||||
$current_user = User::currentUser();
|
||||
$request->merge([
|
||||
'description' => $config
|
||||
'description' => $config,
|
||||
'user_id' => $current_user->getId(),
|
||||
]);
|
||||
|
||||
//die(print_r($request->input('config')));
|
||||
|
||||
$item = Item::create($request->all());
|
||||
if ($request->input('appid') === 'null' || $request->input('appid') === null) {
|
||||
$request->merge([
|
||||
'class' => null,
|
||||
]);
|
||||
} else {
|
||||
$request->merge([
|
||||
'class' => Application::classFromName($application->name),
|
||||
]);
|
||||
}
|
||||
|
||||
if ($id === null) {
|
||||
$item = Item::create($request->all());
|
||||
} else {
|
||||
$item = Item::find($id);
|
||||
$item->update($request->all());
|
||||
}
|
||||
|
||||
$item->parents()->sync($request->tags);
|
||||
return $item;
|
||||
}
|
||||
|
||||
/**
|
||||
* Store a newly created resource in storage.
|
||||
*/
|
||||
public function store(Request $request): RedirectResponse
|
||||
{
|
||||
self::storelogic($request);
|
||||
|
||||
$route = route('dash', []);
|
||||
|
||||
$route = route('dash', [], false);
|
||||
return redirect($route)
|
||||
->with('success', __('app.alert.success.item_created'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Display the specified resource.
|
||||
*
|
||||
* @param int $id
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function show($id)
|
||||
public function show(int $id): void
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the form for editing the specified resource.
|
||||
*
|
||||
* @param int $id
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function edit($id)
|
||||
{
|
||||
// Get the item
|
||||
$data['item'] = Item::find($id);
|
||||
$data['tags'] = Item::ofType('tag')->orderBy('title', 'asc')->pluck('title', 'id');
|
||||
$data['current_tags'] = $data['item']->parents;
|
||||
|
||||
// show the edit form and pass the nerd
|
||||
return view('items.edit', $data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the specified resource in storage.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @param int $id
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function update(Request $request, $id)
|
||||
public function update(Request $request, int $id): RedirectResponse
|
||||
{
|
||||
$validatedData = $request->validate([
|
||||
'title' => 'required|max:255',
|
||||
'url' => 'required|url',
|
||||
]);
|
||||
//die(print_r($request->all()));
|
||||
if($request->hasFile('file')) {
|
||||
$path = $request->file('file')->store('icons');
|
||||
$request->merge([
|
||||
'icon' => $path
|
||||
]);
|
||||
}
|
||||
|
||||
$config = Item::checkConfig($request->input('config'));
|
||||
$request->merge([
|
||||
'description' => $config
|
||||
]);
|
||||
self::storelogic($request, $id);
|
||||
$route = route('dash', []);
|
||||
|
||||
$item = Item::find($id);
|
||||
$item->update($request->all());
|
||||
|
||||
$item->parents()->sync($request->tags);
|
||||
|
||||
$route = route('dash', [], false);
|
||||
return redirect($route)
|
||||
->with('success',__('app.alert.success.item_updated'));
|
||||
->with('success', __('app.alert.success.item_updated'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the specified resource from storage.
|
||||
*
|
||||
* @param int $id
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function destroy(Request $request, $id)
|
||||
public function destroy(Request $request, int $id): RedirectResponse
|
||||
{
|
||||
//
|
||||
$force = (bool)$request->input('force');
|
||||
if($force) {
|
||||
if ($force) {
|
||||
Item::withTrashed()
|
||||
->where('id', $id)
|
||||
->forceDelete();
|
||||
@@ -241,93 +392,205 @@ class ItemController extends Controller
|
||||
Item::find($id)->delete();
|
||||
}
|
||||
|
||||
$route = route('items.index', [], false);
|
||||
return redirect($route)
|
||||
->with('success',__('app.alert.success.item_deleted'));
|
||||
$route = route('items.index', []);
|
||||
|
||||
return redirect($route)
|
||||
->with('success', __('app.alert.success.item_deleted'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Restore the specified resource from soft deletion.
|
||||
*
|
||||
* @param int $id
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function restore($id)
|
||||
public function restore(int $id): RedirectResponse
|
||||
{
|
||||
//
|
||||
Item::withTrashed()
|
||||
->where('id', $id)
|
||||
->restore();
|
||||
|
||||
$route = route('items.inded', [], false);
|
||||
return redirect($route)
|
||||
->with('success',__('app.alert.success.item_restored'));
|
||||
}
|
||||
->where('id', $id)
|
||||
->restore();
|
||||
|
||||
public function isSupportedAppByKey($app)
|
||||
{
|
||||
$output = false;
|
||||
$all_supported = Item::supportedList();
|
||||
if(array_key_exists($app, $all_supported)) {
|
||||
$output = new $all_supported[$app];
|
||||
}
|
||||
return $output;
|
||||
$route = route('items.index', []);
|
||||
|
||||
return redirect($route)
|
||||
->with('success', __('app.alert.success.item_restored'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Return details for supported apps
|
||||
*
|
||||
* @return Json
|
||||
* @throws GuzzleException
|
||||
*/
|
||||
public function appload(Request $request)
|
||||
public function appload(Request $request): ?string
|
||||
{
|
||||
$output = [];
|
||||
$app = $request->input('app');
|
||||
$appid = $request->input('app');
|
||||
$itemId = $request->input('item_id');
|
||||
|
||||
if(($app_details = $this->isSupportedAppByKey($app)) !== false) {
|
||||
// basic details
|
||||
$output['icon'] = $app_details->icon();
|
||||
$output['colour'] = $app_details->defaultColour();
|
||||
|
||||
// live details
|
||||
if($app_details instanceof \App\SupportedApps\Contracts\Livestats) {
|
||||
$output['config'] = $app_details->configDetails();
|
||||
} else {
|
||||
$output['config'] = null;
|
||||
}
|
||||
if ($appid === 'null') {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
$output['config'] = null;
|
||||
$output['custom'] = null;
|
||||
|
||||
$app = Application::single($appid);
|
||||
$output = (array)$app;
|
||||
|
||||
$appdetails = Application::getApp($appid);
|
||||
|
||||
if ((bool)$app->enhanced === true) {
|
||||
$item = $itemId ? Item::find($itemId) : Item::where('appid', $appid)->first();
|
||||
// if(!isset($app->config)) { // class based config
|
||||
$output['custom'] = className($appdetails->name) . '.config';
|
||||
$output['appvalue'] = $item->description;
|
||||
// }
|
||||
}
|
||||
|
||||
$output['colour'] = ($app->tile_background == 'light') ? '#fafbfc' : '#161b1f';
|
||||
|
||||
if (strpos($app->icon, '://') !== false) {
|
||||
$output['iconview'] = $app->icon;
|
||||
} elseif (strpos($app->icon, 'icons/') !== false) {
|
||||
// Private apps have the icon locally
|
||||
$output['iconview'] = URL::to('/') . '/storage/' . $app->icon;
|
||||
$output['icon'] = str_replace('icons/', '', $output['icon']);
|
||||
} else {
|
||||
$output['iconview'] = config('app.appsource') . 'icons/' . $app->icon;
|
||||
}
|
||||
|
||||
|
||||
return json_encode($output);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function testConfig(Request $request)
|
||||
{
|
||||
$data = $request->input('data');
|
||||
//$url = $data[array_search('url', array_column($data, 'name'))]['value'];
|
||||
|
||||
$app = $data['type'];
|
||||
$single = Application::single($data['type']);
|
||||
$app = $single->class;
|
||||
|
||||
// If password is not resubmitted fill it from the database when in edit mode
|
||||
if (array_key_exists('password', $data) &&
|
||||
$data['password'] === null &&
|
||||
array_key_exists('id', $data)
|
||||
) {
|
||||
$item = Item::find($data['id']);
|
||||
if ($item) {
|
||||
$itemConfig = $item->getConfig();
|
||||
$data['password'] = $itemConfig->password;
|
||||
}
|
||||
}
|
||||
|
||||
$app_details = new $app();
|
||||
$app_details->config = (object)$data;
|
||||
$app_details->testConfig();
|
||||
$app_details->test();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $url
|
||||
* @param array|bool $overridevars
|
||||
* @throws GuzzleException
|
||||
*/
|
||||
public function execute($url, array $attrs = [], $overridevars = false): ?ResponseInterface
|
||||
{
|
||||
// Default Guzzle client configuration
|
||||
$clientOptions = [
|
||||
'http_errors' => false,
|
||||
'timeout' => 15,
|
||||
'connect_timeout' => 15,
|
||||
'verify' => false, // In production, set this to `true` and manage certs.
|
||||
];
|
||||
|
||||
// If the user provided overrides, use them.
|
||||
if ($overridevars !== false) {
|
||||
$clientOptions = $overridevars;
|
||||
}
|
||||
|
||||
// Resolve the hostname to an IP address
|
||||
$host = parse_url($url, PHP_URL_HOST);
|
||||
$ip = gethostbyname($host);
|
||||
|
||||
// Check if the IP is private or reserved
|
||||
$allowInternalIps = env('ALLOW_INTERNAL_REQUESTS', false);
|
||||
if (!$allowInternalIps && filter_var($ip, FILTER_VALIDATE_IP, FILTER_FLAG_NO_PRIV_RANGE | FILTER_FLAG_NO_RES_RANGE) === false) {
|
||||
Log::warning('Blocked access to private or reserved IPs.', ['ip' => $ip, 'host' => $host]);
|
||||
abort(Response::HTTP_FORBIDDEN, 'Access to private or reserved IPs is not allowed.');
|
||||
}
|
||||
|
||||
// Force Guzzle to use the resolved IP address
|
||||
$clientOptions['curl'][CURLOPT_RESOLVE] = ["{$host}:80:{$ip}", "{$host}:443:{$ip}"];
|
||||
|
||||
$client = new Client($clientOptions);
|
||||
$method = 'GET';
|
||||
|
||||
try {
|
||||
return $client->request($method, $url, $attrs);
|
||||
} catch (ConnectException $e) {
|
||||
Log::warning('SSRF Attempt Blocked: Connection to a private IP was prevented.', [
|
||||
'url' => $url,
|
||||
'error' => $e->getMessage()
|
||||
]);
|
||||
return null;
|
||||
} catch (ServerException $e) {
|
||||
Log::debug($e->getMessage());
|
||||
} catch (\Exception $e) {
|
||||
Log::error('General error: ' . $e->getMessage());
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $url
|
||||
* @throws GuzzleException
|
||||
*/
|
||||
public function websitelookup($url): StreamInterface
|
||||
{
|
||||
$decodedUrl = base64_decode($url);
|
||||
|
||||
// Validate the URL format.
|
||||
if (filter_var($decodedUrl, FILTER_VALIDATE_URL) === false) {
|
||||
abort(Response::HTTP_BAD_REQUEST, 'Invalid URL format provided.');
|
||||
}
|
||||
|
||||
$response = $this->execute($decodedUrl);
|
||||
|
||||
// If execute() returns null, it means the connection failed.
|
||||
// This can happen for many reasons, including our SSRF protection kicking in.
|
||||
if ($response === null) {
|
||||
abort(Response::HTTP_FORBIDDEN, 'Access to the requested resource is not allowed or the resource is unavailable.');
|
||||
}
|
||||
|
||||
return $response->getBody();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $id
|
||||
* @return void
|
||||
*/
|
||||
public function getStats($id)
|
||||
{
|
||||
$item = Item::find($id);
|
||||
|
||||
$config = json_decode($item->description);
|
||||
if(isset($config->type)) {
|
||||
$config->url = $item->url;
|
||||
if(isset($config->override_url) && !empty($config->override_url)) {
|
||||
$config->url = $config->override_url;
|
||||
}
|
||||
$app_details = new $config->type;
|
||||
$app_details->config = $config;
|
||||
echo $app_details->executeConfig();
|
||||
$config = $item->getconfig();
|
||||
if (isset($item->class)) {
|
||||
$application = new $item->class;
|
||||
$application->config = $config;
|
||||
echo $application->livestats();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @return \Illuminate\Contracts\Foundation\Application|RedirectResponse|Redirector
|
||||
*/
|
||||
public function checkAppList(): RedirectResponse
|
||||
{
|
||||
ProcessApps::dispatch();
|
||||
$route = route('items.index');
|
||||
|
||||
return redirect($route)
|
||||
->with('success', __('app.alert.success.updating'));
|
||||
}
|
||||
}
|
||||
|
||||
93
app/Http/Controllers/ItemRestController.php
Normal file
93
app/Http/Controllers/ItemRestController.php
Normal file
@@ -0,0 +1,93 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Item;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Http\Response;
|
||||
use Illuminate\Support\Collection;
|
||||
|
||||
class ItemRestController extends Controller
|
||||
{
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
$this->middleware('allowed');
|
||||
}
|
||||
|
||||
/**
|
||||
* Display a listing of the resource.
|
||||
*/
|
||||
public function index(): Collection
|
||||
{
|
||||
$columns = [
|
||||
'title',
|
||||
'colour',
|
||||
'url',
|
||||
'description',
|
||||
'appid',
|
||||
'appdescription',
|
||||
];
|
||||
|
||||
return Item::select($columns)
|
||||
->where('deleted_at', null)
|
||||
->where('type', '0')
|
||||
->orderBy('order', 'asc')
|
||||
->get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the form for creating a new resource.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function create()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Store a newly created resource in storage.
|
||||
*/
|
||||
public function store(Request $request): object
|
||||
{
|
||||
$item = ItemController::storelogic($request);
|
||||
|
||||
if ($item) {
|
||||
return (object) ['status' => 'OK'];
|
||||
}
|
||||
|
||||
return (object) ['status' => 'FAILED'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Display the specified resource.
|
||||
*/
|
||||
public function show(Item $item): Response
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the form for editing the specified resource.
|
||||
*/
|
||||
public function edit(Item $item): Response
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the specified resource in storage.
|
||||
*/
|
||||
public function update(Request $request, Item $item): Response
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the specified resource from storage.
|
||||
*/
|
||||
public function destroy(Item $item): Response
|
||||
{
|
||||
//
|
||||
}
|
||||
}
|
||||
40
app/Http/Controllers/SearchController.php
Normal file
40
app/Http/Controllers/SearchController.php
Normal file
@@ -0,0 +1,40 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\Search;
|
||||
use Illuminate\Contracts\Foundation\Application;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Routing\Redirector;
|
||||
|
||||
class SearchController extends Controller
|
||||
{
|
||||
/**
|
||||
* @return Application|RedirectResponse|Redirector|mixed|void
|
||||
*/
|
||||
public function index(Request $request)
|
||||
{
|
||||
$requestprovider = $request->input('provider');
|
||||
$query = $request->input('q');
|
||||
|
||||
// Validate the presence and non-emptiness of the query parameter
|
||||
if (!$query || trim($query) === '') {
|
||||
abort(400, 'Missing or empty query parameter');
|
||||
}
|
||||
|
||||
$provider = Search::providerDetails($requestprovider);
|
||||
|
||||
if (!$provider || !isset($provider->type)) {
|
||||
abort(404, 'Invalid provider');
|
||||
}
|
||||
|
||||
if ($provider->type == 'standard') {
|
||||
return redirect($provider->url.'?'.$provider->query.'='.urlencode($query));
|
||||
} elseif ($provider->type == 'external') {
|
||||
$class = new $provider->class;
|
||||
return $class->getResults($query, $provider);
|
||||
}
|
||||
|
||||
abort(404, 'Provider type not supported');}
|
||||
}
|
||||
@@ -2,17 +2,23 @@
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
use App\Setting;
|
||||
use App\SettingGroup;
|
||||
use App\Http\Controllers\Controller;
|
||||
use Exception;
|
||||
use enshrined\svgSanitize\Sanitizer;
|
||||
use Illuminate\Contracts\View\View;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class SettingsController extends Controller
|
||||
{
|
||||
/**
|
||||
* @return \Illuminate\View\View
|
||||
*/
|
||||
public function index()
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
$this->middleware('allowed');
|
||||
}
|
||||
|
||||
public function index(): View
|
||||
{
|
||||
$settings = SettingGroup::with([
|
||||
'settings',
|
||||
@@ -24,87 +30,112 @@ class SettingsController extends Controller
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $id
|
||||
*
|
||||
* @return \Illuminate\Http\RedirectResponse
|
||||
* @return RedirectResponse|View
|
||||
*/
|
||||
public function edit($id)
|
||||
public function edit(int $id)
|
||||
{
|
||||
$setting = Setting::find($id);
|
||||
//die("s: ".$setting->label);
|
||||
|
||||
if((bool)$setting->system === true) return abort(404);
|
||||
if ((bool) $setting->system === true) {
|
||||
return abort(404);
|
||||
}
|
||||
|
||||
if (!is_null($setting)) {
|
||||
if (! is_null($setting)) {
|
||||
return view('settings.edit')->with([
|
||||
'setting' => $setting,
|
||||
]);
|
||||
} else {
|
||||
$route = route('settings.list', [], false);
|
||||
return redirect($route)
|
||||
$route = route('settings.list', []);
|
||||
|
||||
return redirect($route)
|
||||
->with([
|
||||
'error' => __('app.alert.error.not_exist'),
|
||||
'errors' => collect([__('app.alert.error.not_exist')]),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $id
|
||||
*
|
||||
* @return \Illuminate\Http\RedirectResponse
|
||||
*/
|
||||
public function update(Request $request, $id)
|
||||
public function update(Request $request, int $id): RedirectResponse
|
||||
{
|
||||
$setting = Setting::find($id);
|
||||
$user = $this->user();
|
||||
$route = route('settings.index', []);
|
||||
|
||||
if (!is_null($setting)) {
|
||||
$data = Setting::getInput();
|
||||
|
||||
if ($setting->type == 'image') {
|
||||
|
||||
|
||||
if($request->hasFile('value')) {
|
||||
$path = $request->file('value')->store('backgrounds');
|
||||
$setting->value = $path;
|
||||
}
|
||||
|
||||
|
||||
|
||||
} else {
|
||||
$setting->value = $data->value;
|
||||
try {
|
||||
if (is_null($setting)) {
|
||||
throw new Exception('not_exists');
|
||||
}
|
||||
|
||||
$setting->save();
|
||||
if ($setting->type === 'image') {
|
||||
$validatedData = $request->validate([
|
||||
'value' => 'image',
|
||||
]);
|
||||
|
||||
$route = route('settings.index', [], false);
|
||||
return redirect($route)
|
||||
->with([
|
||||
'success' => __('app.alert.success.setting_updated'),
|
||||
]);
|
||||
} else {
|
||||
$route = route('settings.index', [], false);
|
||||
return redirect($route)
|
||||
->with([
|
||||
'error' => __('app.alert.error.not_exist'),
|
||||
]);
|
||||
if (!$request->hasFile('value')) {
|
||||
throw new \Exception('file_too_big');
|
||||
}
|
||||
|
||||
$image = $request->file('value');
|
||||
$extension = $image->getClientOriginalExtension();
|
||||
|
||||
if ($extension === 'svg') {
|
||||
$sanitizer = new Sanitizer();
|
||||
$sanitizedSvg = $sanitizer->sanitize(file_get_contents($image->getRealPath()));
|
||||
|
||||
// Verify that the sanitization removed malicious content
|
||||
if (strpos($sanitizedSvg, '<script>') !== false) {
|
||||
throw new \Exception('SVG contains malicious content and cannot be uploaded.');
|
||||
}
|
||||
|
||||
// Save the sanitized SVG back to the file
|
||||
file_put_contents($image->getRealPath(), $sanitizedSvg);
|
||||
}
|
||||
|
||||
$path = $image->store('backgrounds', 'public');
|
||||
|
||||
if ($path === null) {
|
||||
throw new \Exception('file_not_stored');
|
||||
}
|
||||
|
||||
$setting_value = $path;
|
||||
} else {
|
||||
$data = Setting::getInput($request);
|
||||
$setting_value = $data->value;
|
||||
}
|
||||
|
||||
$user->settings()->detach($setting->id);
|
||||
$user->settings()->save($setting, ['uservalue' => $setting_value]);
|
||||
|
||||
return redirect($route)
|
||||
->with([
|
||||
'success' => __('app.alert.success.setting_updated'),
|
||||
]);
|
||||
} catch (Exception $e) {
|
||||
return redirect($route)
|
||||
->with([
|
||||
'errors' => collect([__('app.alert.error.' . $e->getMessage())]),
|
||||
]);
|
||||
}
|
||||
}
|
||||
/**
|
||||
* @param int $id
|
||||
*
|
||||
* @return \Illuminate\Http\RedirectResponse
|
||||
*/
|
||||
public function clear($id)
|
||||
|
||||
public function clear(int $id): RedirectResponse
|
||||
{
|
||||
$user = $this->user();
|
||||
$setting = Setting::find($id);
|
||||
if((bool)$setting->system !== true) {
|
||||
$setting->value = '';
|
||||
$setting->save();
|
||||
if ((bool) $setting->system !== true) {
|
||||
$user->settings()->detach($setting->id);
|
||||
$user->settings()->save($setting, ['uservalue' => '']);
|
||||
}
|
||||
$route = route('settings.index', [], false);
|
||||
return redirect($route)
|
||||
$route = route('settings.index', []);
|
||||
|
||||
return redirect($route)
|
||||
->with([
|
||||
'success' => __('app.alert.success.setting_updated'),
|
||||
]);
|
||||
|
||||
}
|
||||
|
||||
public function search(Request $request)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,24 +2,33 @@
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use Illuminate\Http\Request;
|
||||
use App\Item;
|
||||
use DB;
|
||||
use App\User;
|
||||
use Illuminate\Contracts\Foundation\Application;
|
||||
use Illuminate\Contracts\View\Factory;
|
||||
use Illuminate\Contracts\View\View;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
use Illuminate\Http\Request;
|
||||
|
||||
class TagController extends Controller
|
||||
{
|
||||
public function __construct()
|
||||
{
|
||||
$this->middleware('allowed');
|
||||
}
|
||||
|
||||
/**
|
||||
* Display a listing of the resource.
|
||||
*
|
||||
* @return \Illuminate\Http\Response
|
||||
* @return Application|Factory|View
|
||||
*/
|
||||
public function index(Request $request)
|
||||
public function index(Request $request): \Illuminate\View\View
|
||||
{
|
||||
$trash = (bool)$request->input('trash');
|
||||
$trash = (bool) $request->input('trash');
|
||||
|
||||
$data['apps'] = Item::ofType('tag')->orderBy('title', 'asc')->get();
|
||||
$data['trash'] = Item::ofType('tag')->onlyTrashed()->get();
|
||||
if($trash) {
|
||||
$data['apps'] = Item::ofType('tag')->where('id', '>', 0)->orderBy('title', 'asc')->get();
|
||||
$data['trash'] = Item::ofType('tag')->where('id', '>', 0)->onlyTrashed()->get();
|
||||
if ($trash) {
|
||||
return view('tags.trash', $data);
|
||||
} else {
|
||||
return view('tags.list', $data);
|
||||
@@ -29,44 +38,47 @@ class TagController extends Controller
|
||||
/**
|
||||
* Show the form for creating a new resource.
|
||||
*
|
||||
* @return \Illuminate\Http\Response
|
||||
* @return Application|Factory|View
|
||||
*/
|
||||
public function create()
|
||||
public function create(): \Illuminate\View\View
|
||||
{
|
||||
$data = [];
|
||||
|
||||
return view('tags.create', $data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Store a newly created resource in storage.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function store(Request $request)
|
||||
public function store(Request $request): RedirectResponse
|
||||
{
|
||||
$validatedData = $request->validate([
|
||||
'title' => 'required|max:255',
|
||||
'file' => 'image'
|
||||
]);
|
||||
|
||||
if($request->hasFile('file')) {
|
||||
$path = $request->file('file')->store('icons');
|
||||
if ($request->hasFile('file')) {
|
||||
$path = $request->file('file')->store('icons', 'public');
|
||||
$request->merge([
|
||||
'icon' => $path
|
||||
'icon' => $path,
|
||||
]);
|
||||
}
|
||||
|
||||
$slug = str_slug($request->title, '-');
|
||||
$slug = str_slug($request->title, '-', 'en_US');
|
||||
|
||||
$current_user = User::currentUser();
|
||||
|
||||
// set item type to tag
|
||||
$request->merge([
|
||||
'type' => '1',
|
||||
'url' => $slug
|
||||
'url' => $slug,
|
||||
'user_id' => $current_user->getId(),
|
||||
]);
|
||||
//die(print_r($request->all()));
|
||||
Item::create($request->all());
|
||||
|
||||
$route = route('dash', [], false);
|
||||
$route = route('dash', []);
|
||||
|
||||
return redirect($route)
|
||||
->with('success', __('app.alert.success.tag_created'));
|
||||
}
|
||||
@@ -74,120 +86,124 @@ class TagController extends Controller
|
||||
/**
|
||||
* Display the specified resource.
|
||||
*
|
||||
* @param int $id
|
||||
* @return \Illuminate\Http\Response
|
||||
* @param $slug
|
||||
*/
|
||||
public function show($slug)
|
||||
public function show($slug, Request $request): View
|
||||
{
|
||||
$item = Item::whereUrl($slug)->first();
|
||||
//print_r($item);
|
||||
$data['apps'] = $item->children()->pinned()->orderBy('order', 'asc')->get();
|
||||
if (config('app.auth_roles_enable')) {
|
||||
$roles = explode(config('app.auth_roles_delimiter'), $request->header(config('app.auth_roles_header')));
|
||||
$data['apps'] = $item->children()->whereIn('role', $roles)->pinned()->orderBy('order', 'asc')->get();
|
||||
} else {
|
||||
$data['apps'] = $item->children()->pinned()->orderBy('order', 'asc')->get();
|
||||
}
|
||||
$data['tag'] = $item->id;
|
||||
$data['all_apps'] = $item->children;
|
||||
|
||||
return view('welcome', $data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the form for editing the specified resource.
|
||||
*
|
||||
* @param int $id
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function edit($id)
|
||||
public function edit(int $id): View
|
||||
{
|
||||
// Get the item
|
||||
$item = Item::find($id);
|
||||
|
||||
// show the edit form and pass the nerd
|
||||
return view('tags.edit')
|
||||
->with('item', $item);
|
||||
->with('item', $item);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the specified resource in storage.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @param int $id
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function update(Request $request, $id)
|
||||
public function update(Request $request, int $id): RedirectResponse
|
||||
{
|
||||
$validatedData = $request->validate([
|
||||
'title' => 'required|max:255',
|
||||
'file' => 'image'
|
||||
]);
|
||||
|
||||
if($request->hasFile('file')) {
|
||||
$path = $request->file('file')->store('icons');
|
||||
if ($request->hasFile('file')) {
|
||||
$path = $request->file('file')->store('icons', 'public');
|
||||
$request->merge([
|
||||
'icon' => $path
|
||||
'icon' => $path,
|
||||
]);
|
||||
}
|
||||
|
||||
$slug = str_slug($request->title, '-');
|
||||
$slug = str_slug($request->title, '-', 'en_US');
|
||||
// set item type to tag
|
||||
$request->merge([
|
||||
'url' => $slug
|
||||
'url' => $slug,
|
||||
]);
|
||||
|
||||
Item::find($id)->update($request->all());
|
||||
|
||||
$route = route('dash', [], false);
|
||||
$route = route('dash', []);
|
||||
|
||||
return redirect($route)
|
||||
->with('success',__('app.alert.success.tag_updated'));
|
||||
->with('success', __('app.alert.success.tag_updated'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the specified resource from storage.
|
||||
*
|
||||
* @param int $id
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function destroy(Request $request, $id)
|
||||
public function destroy(Request $request, int $id): RedirectResponse
|
||||
{
|
||||
//
|
||||
$force = (bool)$request->input('force');
|
||||
if($force) {
|
||||
$force = (bool) $request->input('force');
|
||||
if ($force) {
|
||||
Item::withTrashed()
|
||||
->where('id', $id)
|
||||
->forceDelete();
|
||||
} else {
|
||||
Item::find($id)->delete();
|
||||
}
|
||||
|
||||
$route = route('tags.index', [], false);
|
||||
|
||||
$route = route('tags.index', []);
|
||||
|
||||
return redirect($route)
|
||||
->with('success',__('app.alert.success.item_deleted'));
|
||||
->with('success', __('app.alert.success.item_deleted'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Restore the specified resource from soft deletion.
|
||||
*
|
||||
* @param int $id
|
||||
* @return \Illuminate\Http\Response
|
||||
*/
|
||||
public function restore($id)
|
||||
public function restore(int $id): RedirectResponse
|
||||
{
|
||||
//
|
||||
Item::withTrashed()
|
||||
->where('id', $id)
|
||||
->restore();
|
||||
$route = route('tags.index', [], false);
|
||||
->restore();
|
||||
$route = route('tags.index', []);
|
||||
|
||||
return redirect($route)
|
||||
->with('success',__('app.alert.success.item_restored'));
|
||||
->with('success', __('app.alert.success.item_restored'));
|
||||
}
|
||||
|
||||
public function add($tag, $item)
|
||||
/**
|
||||
* Add item to tag
|
||||
*
|
||||
* @param $tag
|
||||
* @param $item
|
||||
* @return int 1|0
|
||||
*/
|
||||
public function add($tag, $item): int
|
||||
{
|
||||
$output = 0;
|
||||
$tag = Item::find($tag);
|
||||
$item = Item::find($item);
|
||||
if($tag && $item) {
|
||||
if ($tag && $item) {
|
||||
// only add items, not cats
|
||||
if((int)$item->type === 0) {
|
||||
if ((int) $item->type === 0) {
|
||||
$tag->children()->attach($item);
|
||||
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
return $output;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
164
app/Http/Controllers/UserController.php
Normal file
164
app/Http/Controllers/UserController.php
Normal file
@@ -0,0 +1,164 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Controllers;
|
||||
|
||||
use App\User;
|
||||
use Illuminate\Contracts\View\View;
|
||||
use Illuminate\Http\RedirectResponse;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Str;
|
||||
|
||||
class UserController extends Controller
|
||||
{
|
||||
public function __construct()
|
||||
{
|
||||
parent::__construct();
|
||||
$this->middleware('allowed')->except(['selectUser']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Display a listing of the resource.
|
||||
*/
|
||||
public function index(): View
|
||||
{
|
||||
$data['users'] = User::all();
|
||||
|
||||
return view('users.index', $data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the form for creating a new resource.
|
||||
*/
|
||||
public function create(): View
|
||||
{
|
||||
$data = [];
|
||||
|
||||
return view('users.create', $data);
|
||||
}
|
||||
|
||||
public function selectUser(): \Illuminate\View\View
|
||||
{
|
||||
Auth::logout();
|
||||
$data['users'] = User::all();
|
||||
|
||||
return view('userselect', $data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Store a newly created resource in storage.
|
||||
*/
|
||||
public function store(Request $request): RedirectResponse
|
||||
{
|
||||
$validatedData = $request->validate([
|
||||
'username' => 'required|max:255|unique:users',
|
||||
'email' => 'required|email',
|
||||
'password' => 'nullable|confirmed',
|
||||
'password_confirmation' => 'nullable',
|
||||
'file' => 'image'
|
||||
]);
|
||||
$user = new User;
|
||||
$user->username = $request->input('username');
|
||||
$user->email = $request->input('email');
|
||||
$user->public_front = $request->input('public_front');
|
||||
|
||||
$password = $request->input('password');
|
||||
if (! empty($password)) {
|
||||
$user->password = bcrypt($password);
|
||||
}
|
||||
|
||||
if ($request->hasFile('file')) {
|
||||
$path = $request->file('file')->store('avatars', 'public');
|
||||
$user->avatar = $path;
|
||||
}
|
||||
|
||||
if ((bool) $request->input('autologin_allow') === true) {
|
||||
$user->autologin = (string) Str::uuid();
|
||||
}
|
||||
|
||||
$user->save();
|
||||
|
||||
$route = route('dash', []);
|
||||
|
||||
return redirect($route)
|
||||
->with('success', __('app.alert.success.user_updated'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Display the specified resource.
|
||||
*/
|
||||
public function show(int $id): void
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Show the form for editing the specified resource.
|
||||
*/
|
||||
public function edit(User $user): View
|
||||
{
|
||||
$data['user'] = $user;
|
||||
|
||||
return view('users.edit', $data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the specified resource in storage.
|
||||
*/
|
||||
public function update(Request $request, User $user): RedirectResponse
|
||||
{
|
||||
$validatedData = $request->validate([
|
||||
'username' => 'required|max:255|unique:users,username,'.$user->id,
|
||||
'email' => 'required|email',
|
||||
'password' => 'nullable|confirmed',
|
||||
'password_confirmation' => 'nullable',
|
||||
'file' => 'image'
|
||||
]);
|
||||
//die(print_r($request->all()));
|
||||
|
||||
$user->username = $request->input('username');
|
||||
$user->email = $request->input('email');
|
||||
$user->public_front = $request->input('public_front');
|
||||
|
||||
$password = $request->input('password');
|
||||
if (! empty($password)) {
|
||||
$user->password = bcrypt($password);
|
||||
} elseif ($password == 'null') {
|
||||
$user->password = null;
|
||||
}
|
||||
|
||||
if ($request->hasFile('file')) {
|
||||
$path = $request->file('file')->store('avatars', 'public');
|
||||
$user->avatar = $path;
|
||||
}
|
||||
|
||||
if ((bool) $request->input('autologin_allow') === true) {
|
||||
$user->autologin = (is_null($user->autologin)) ? (string) Str::uuid() : $user->autologin;
|
||||
} else {
|
||||
$user->autologin = null;
|
||||
}
|
||||
|
||||
$user->save();
|
||||
|
||||
$route = route('dash', []);
|
||||
|
||||
return redirect($route)
|
||||
->with('success', __('app.alert.success.user_updated'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove the specified resource from storage.
|
||||
*
|
||||
* @return RedirectResponse | void
|
||||
*/
|
||||
public function destroy(User $user): RedirectResponse
|
||||
{
|
||||
if ($user->id !== 1) {
|
||||
$user->delete();
|
||||
$route = route('dash', []);
|
||||
|
||||
return redirect($route)
|
||||
->with('success', __('app.alert.success.user_deleted'));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,61 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http;
|
||||
|
||||
use Illuminate\Foundation\Http\Kernel as HttpKernel;
|
||||
|
||||
class Kernel extends HttpKernel
|
||||
{
|
||||
/**
|
||||
* The application's global HTTP middleware stack.
|
||||
*
|
||||
* These middleware are run during every request to your application.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $middleware = [
|
||||
\Illuminate\Foundation\Http\Middleware\CheckForMaintenanceMode::class,
|
||||
\Illuminate\Foundation\Http\Middleware\ValidatePostSize::class,
|
||||
\App\Http\Middleware\TrimStrings::class,
|
||||
\Illuminate\Foundation\Http\Middleware\ConvertEmptyStringsToNull::class,
|
||||
\App\Http\Middleware\TrustProxies::class,
|
||||
];
|
||||
|
||||
/**
|
||||
* The application's route middleware groups.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $middlewareGroups = [
|
||||
'web' => [
|
||||
\App\Http\Middleware\EncryptCookies::class,
|
||||
\Illuminate\Cookie\Middleware\AddQueuedCookiesToResponse::class,
|
||||
\Illuminate\Session\Middleware\StartSession::class,
|
||||
// \Illuminate\Session\Middleware\AuthenticateSession::class,
|
||||
\Illuminate\View\Middleware\ShareErrorsFromSession::class,
|
||||
\App\Http\Middleware\VerifyCsrfToken::class,
|
||||
\Illuminate\Routing\Middleware\SubstituteBindings::class,
|
||||
],
|
||||
|
||||
'api' => [
|
||||
'throttle:60,1',
|
||||
'bindings',
|
||||
],
|
||||
];
|
||||
|
||||
/**
|
||||
* The application's route middleware.
|
||||
*
|
||||
* These middleware may be assigned to groups or used individually.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $routeMiddleware = [
|
||||
'auth' => \Illuminate\Auth\Middleware\Authenticate::class,
|
||||
'auth.basic' => \Illuminate\Auth\Middleware\AuthenticateWithBasicAuth::class,
|
||||
'bindings' => \Illuminate\Routing\Middleware\SubstituteBindings::class,
|
||||
'can' => \Illuminate\Auth\Middleware\Authorize::class,
|
||||
'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,
|
||||
'throttle' => \Illuminate\Routing\Middleware\ThrottleRequests::class,
|
||||
];
|
||||
}
|
||||
57
app/Http/Middleware/CheckAllowed.php
Normal file
57
app/Http/Middleware/CheckAllowed.php
Normal file
@@ -0,0 +1,57 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Middleware;
|
||||
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use App\User;
|
||||
use Closure;
|
||||
use Illuminate\Auth\AuthenticationException;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
use Illuminate\Support\Facades\Route;
|
||||
use Session;
|
||||
|
||||
class CheckAllowed
|
||||
{
|
||||
/**
|
||||
* Handle an incoming request.
|
||||
*
|
||||
* @throws AuthenticationException
|
||||
*/
|
||||
public function handle(Request $request, Closure $next): Response
|
||||
{
|
||||
$route = Route::currentRouteName();
|
||||
$current_user = User::currentUser();
|
||||
|
||||
// Non admin users can't access users management
|
||||
if (str_is('users*', $route)) {
|
||||
if ($current_user->getId() !== 1) {
|
||||
return redirect()->route('dash');
|
||||
}
|
||||
}
|
||||
|
||||
// Public access to frontpage
|
||||
if ($route === 'dash' || $route === 'tags.show') {
|
||||
if ((bool)$current_user->public_front === true) {
|
||||
return $next($request);
|
||||
}
|
||||
}
|
||||
|
||||
// Continue with passwordless user
|
||||
if (empty($current_user->password)) {
|
||||
return $next($request);
|
||||
}
|
||||
|
||||
// Check if user is logged in as $current_user
|
||||
if (Auth::check()) {
|
||||
$loggedin_user = Auth::user();
|
||||
if ($loggedin_user->id === $current_user->getId()) {
|
||||
return $next($request);
|
||||
}
|
||||
}
|
||||
|
||||
// Redirect to login
|
||||
Auth::authenticate();
|
||||
return redirect()->route('user.select');
|
||||
}
|
||||
}
|
||||
@@ -1,17 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Middleware;
|
||||
|
||||
use Illuminate\Cookie\Middleware\EncryptCookies as Middleware;
|
||||
|
||||
class EncryptCookies extends Middleware
|
||||
{
|
||||
/**
|
||||
* The names of the cookies that should not be encrypted.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $except = [
|
||||
//
|
||||
];
|
||||
}
|
||||
@@ -2,23 +2,20 @@
|
||||
|
||||
namespace App\Http\Middleware;
|
||||
|
||||
use Symfony\Component\HttpFoundation\Response;
|
||||
use Closure;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Support\Facades\Auth;
|
||||
|
||||
class RedirectIfAuthenticated
|
||||
{
|
||||
/**
|
||||
* Handle an incoming request.
|
||||
*
|
||||
* @param \Illuminate\Http\Request $request
|
||||
* @param \Closure $next
|
||||
* @param string|null $guard
|
||||
* @return mixed
|
||||
*/
|
||||
public function handle($request, Closure $next, $guard = null)
|
||||
public function handle(Request $request, Closure $next, string $guard = null): Response
|
||||
{
|
||||
if (Auth::guard($guard)->check()) {
|
||||
return redirect('/home');
|
||||
return redirect()->intended();
|
||||
}
|
||||
|
||||
return $next($request);
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Middleware;
|
||||
|
||||
use Illuminate\Foundation\Http\Middleware\TrimStrings as Middleware;
|
||||
|
||||
class TrimStrings extends Middleware
|
||||
{
|
||||
/**
|
||||
* The names of the attributes that should not be trimmed.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $except = [
|
||||
'password',
|
||||
'password_confirmation',
|
||||
];
|
||||
}
|
||||
@@ -2,8 +2,8 @@
|
||||
|
||||
namespace App\Http\Middleware;
|
||||
|
||||
use Illuminate\Http\Middleware\TrustProxies as Middleware;
|
||||
use Illuminate\Http\Request;
|
||||
use Fideloper\Proxy\TrustProxies as Middleware;
|
||||
|
||||
class TrustProxies extends Middleware
|
||||
{
|
||||
@@ -12,18 +12,12 @@ class TrustProxies extends Middleware
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $proxies;
|
||||
protected $proxies = ['192.168.0.0/16', '172.16.0.0/12', '10.0.0.0/8', '127.0.0.1'];
|
||||
|
||||
/**
|
||||
* The current proxy header mappings.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $headers = [
|
||||
Request::HEADER_FORWARDED => 'FORWARDED',
|
||||
Request::HEADER_X_FORWARDED_FOR => 'X_FORWARDED_FOR',
|
||||
Request::HEADER_X_FORWARDED_HOST => 'X_FORWARDED_HOST',
|
||||
Request::HEADER_X_FORWARDED_PORT => 'X_FORWARDED_PORT',
|
||||
Request::HEADER_X_FORWARDED_PROTO => 'X_FORWARDED_PROTO',
|
||||
];
|
||||
protected $headers = Request::HEADER_X_FORWARDED_FOR | Request::HEADER_X_FORWARDED_HOST | Request::HEADER_X_FORWARDED_PORT | Request::HEADER_X_FORWARDED_PROTO | Request::HEADER_X_FORWARDED_AWS_ELB;
|
||||
}
|
||||
|
||||
@@ -1,21 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Http\Middleware;
|
||||
|
||||
use Illuminate\Foundation\Http\Middleware\VerifyCsrfToken as Middleware;
|
||||
|
||||
class VerifyCsrfToken extends Middleware
|
||||
{
|
||||
/**
|
||||
* The URIs that should be excluded from CSRF verification.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $except = [
|
||||
//
|
||||
'order',
|
||||
'appload',
|
||||
'test_config',
|
||||
//'get_stats'
|
||||
];
|
||||
}
|
||||
423
app/Item.php
423
app/Item.php
@@ -2,181 +2,270 @@
|
||||
|
||||
namespace App;
|
||||
|
||||
use Illuminate\Contracts\Routing\UrlGenerator;
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Symfony\Component\ClassLoader\ClassMapGenerator;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
|
||||
use Illuminate\Database\Eloquent\SoftDeletes;
|
||||
use Illuminate\Database\Eloquent\Casts\Attribute;
|
||||
use stdClass;
|
||||
use Symfony\Component\ClassLoader\ClassMapGenerator;
|
||||
|
||||
|
||||
// @codingStandardsIgnoreStart
|
||||
/**
|
||||
* App\Item
|
||||
*
|
||||
* @property int $id
|
||||
* @property string $title
|
||||
* @property string|null $colour
|
||||
* @property string|null $icon
|
||||
* @property string $url
|
||||
* @property string|null $description
|
||||
* @property int $pinned
|
||||
* @property int $order
|
||||
* @property \Illuminate\Support\Carbon|null $deleted_at
|
||||
* @property \Illuminate\Support\Carbon|null $created_at
|
||||
* @property \Illuminate\Support\Carbon|null $updated_at
|
||||
* @property int $type
|
||||
* @property int $user_id
|
||||
* @property string|null $class
|
||||
* @property string|null $appid
|
||||
* @property string|null $appdescription
|
||||
* @property string|null $role
|
||||
* @property-read \Illuminate\Database\Eloquent\Collection|Item[] $children
|
||||
* @property-read int|null $children_count
|
||||
* @property-read string $droppable
|
||||
* @property-read \Illuminate\Contracts\Foundation\Application|\Illuminate\Contracts\Routing\UrlGenerator|mixed|string $link
|
||||
* @property-read string $link_icon
|
||||
* @property-read string $link_target
|
||||
* @property-read string $link_type
|
||||
* @property-read \Illuminate\Database\Eloquent\Collection|Item[] $parents
|
||||
* @property-read int|null $parents_count
|
||||
* @property-read \App\User|null $user
|
||||
* @method static \Database\Factories\ItemFactory factory(...$parameters)
|
||||
* @method static Builder|Item newModelQuery()
|
||||
* @method static Builder|Item newQuery()
|
||||
* @method static Builder|Item ofType($type)
|
||||
* @method static \Illuminate\Database\Query\Builder|Item onlyTrashed()
|
||||
* @method static Builder|Item pinned()
|
||||
* @method static Builder|Item query()
|
||||
* @method static Builder|Item whereAppdescription($value)
|
||||
* @method static Builder|Item whereRole($value)
|
||||
* @method static Builder|Item whereAppid($value)
|
||||
* @method static Builder|Item whereClass($value)
|
||||
* @method static Builder|Item whereColour($value)
|
||||
* @method static Builder|Item whereCreatedAt($value)
|
||||
* @method static Builder|Item whereDeletedAt($value)
|
||||
* @method static Builder|Item whereDescription($value)
|
||||
* @method static Builder|Item whereIcon($value)
|
||||
* @method static Builder|Item whereId($value)
|
||||
* @method static Builder|Item whereOrder($value)
|
||||
* @method static Builder|Item wherePinned($value)
|
||||
* @method static Builder|Item whereTitle($value)
|
||||
* @method static Builder|Item whereType($value)
|
||||
* @method static Builder|Item whereUpdatedAt($value)
|
||||
* @method static Builder|Item whereUrl($value)
|
||||
* @method static Builder|Item whereUserId($value)
|
||||
* @method static \Illuminate\Database\Query\Builder|Item withTrashed()
|
||||
* @method static \Illuminate\Database\Query\Builder|Item withoutTrashed()
|
||||
* @mixin \Eloquent
|
||||
*/
|
||||
// @codingStandardsIgnoreEnd
|
||||
class Item extends Model
|
||||
{
|
||||
use SoftDeletes;
|
||||
|
||||
//
|
||||
use HasFactory;
|
||||
|
||||
protected static function boot(): void
|
||||
{
|
||||
parent::boot();
|
||||
|
||||
static::addGlobalScope('user_id', function (Builder $builder) {
|
||||
$current_user = User::currentUser();
|
||||
if ($current_user) {
|
||||
$builder->where('user_id', $current_user->getId())->orWhere('user_id', 0);
|
||||
} else {
|
||||
$builder->where('user_id', 0);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
protected $fillable = [
|
||||
'title', 'url', 'colour', 'icon', 'description', 'pinned', 'order', 'type'
|
||||
'title',
|
||||
'url',
|
||||
'colour',
|
||||
'icon',
|
||||
'appdescription',
|
||||
'description',
|
||||
'pinned',
|
||||
'order',
|
||||
'type',
|
||||
'class',
|
||||
'user_id',
|
||||
'tag_id',
|
||||
'appid',
|
||||
'role',
|
||||
];
|
||||
|
||||
/**
|
||||
* The attributes that should be mutated to dates.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $dates = ['deleted_at'];
|
||||
|
||||
public static function supportedList()
|
||||
{
|
||||
return [
|
||||
'Deluge' => \App\SupportedApps\Deluge::class,
|
||||
'Dokuwiki' => \App\SupportedApps\Dokuwiki::class,
|
||||
'Duplicati' => \App\SupportedApps\Duplicati::class,
|
||||
'Emby' => \App\SupportedApps\Emby::class,
|
||||
'Gitea' => \App\SupportedApps\Gitea::class,
|
||||
'Graylog' => \App\SupportedApps\Graylog::class,
|
||||
'Home Assistant' => \App\SupportedApps\HomeAssistant::class,
|
||||
'Jackett' => \App\SupportedApps\Jackett::class,
|
||||
'Jdownloader' => \App\SupportedApps\Jdownloader::class,
|
||||
'Lidarr' => \App\SupportedApps\Lidarr::class,
|
||||
'Mcmyadmin' => \App\SupportedApps\Mcmyadmin::class,
|
||||
'Medusa' => \App\SupportedApps\Medusa::class,
|
||||
'NZBGet' => \App\SupportedApps\Nzbget::class,
|
||||
'Netdata' => \App\SupportedApps\Netdata::class,
|
||||
'Nextcloud' => \App\SupportedApps\Nextcloud::class,
|
||||
'Nzbhydra' => \App\SupportedApps\Nzbhydra::class,
|
||||
'OPNSense' => \App\SupportedApps\Opnsense::class,
|
||||
'Ombi' => \App\SupportedApps\Ombi::class,
|
||||
'Openhab' => \App\SupportedApps\Openhab::class,
|
||||
'Pihole' => \App\SupportedApps\Pihole::class,
|
||||
'Plex' => \App\SupportedApps\Plex::class,
|
||||
'Plexpy' => \App\SupportedApps\Plexpy::class,
|
||||
'Plexrequests' => \App\SupportedApps\Plexrequests::class,
|
||||
'Portainer' => \App\SupportedApps\Portainer::class,
|
||||
'Proxmox' => \App\SupportedApps\Proxmox::class,
|
||||
'Radarr' => \App\SupportedApps\Radarr::class,
|
||||
'Runeaudio' => \App\SupportedApps\Runeaudio::class,
|
||||
'Sabnzbd' => \App\SupportedApps\Sabnzbd::class,
|
||||
'Sickrage' => \App\SupportedApps\Sickrage::class,
|
||||
'Sonarr' => \App\SupportedApps\Sonarr::class,
|
||||
'Traefik' => \App\SupportedApps\Traefik::class,
|
||||
'Ttrss' => \App\SupportedApps\Ttrss::class,
|
||||
'UniFi' => \App\SupportedApps\Unifi::class,
|
||||
'pFsense' => \App\SupportedApps\Pfsense::class,
|
||||
'ruTorrent' => \App\SupportedApps\ruTorrent::class,
|
||||
];
|
||||
}
|
||||
public static function supportedOptions()
|
||||
{
|
||||
return array_keys(self::supportedList());
|
||||
}
|
||||
|
||||
/**
|
||||
* Scope a query to only include pinned items.
|
||||
*
|
||||
* @param \Illuminate\Database\Eloquent\Builder $query
|
||||
* @return \Illuminate\Database\Eloquent\Builder
|
||||
*/
|
||||
public function scopePinned($query)
|
||||
public function scopePinned(Builder $query): Builder
|
||||
{
|
||||
return $query->where('pinned', 1);
|
||||
}
|
||||
|
||||
public function getConfigAttribute()
|
||||
{
|
||||
$output = null;
|
||||
$view = null;
|
||||
if(isset($this->description) && !empty($this->description)){
|
||||
$output = json_decode($this->description);
|
||||
$output = is_object($output) ? $output : new \stdClass();
|
||||
if(isset($output->type) && !empty($output->type)) {
|
||||
$class = $output->type;
|
||||
$sap = new $class();
|
||||
$view = $sap->configDetails();
|
||||
$output->view = $view;
|
||||
}
|
||||
if(!isset($output->dataonly)) $output->dataonly = '0';
|
||||
|
||||
}
|
||||
return (object)$output;
|
||||
}
|
||||
public static function checkConfig($config)
|
||||
{
|
||||
if(empty($config)) {
|
||||
// die(print_r($config));
|
||||
if (empty($config)) {
|
||||
$config = null;
|
||||
} else {
|
||||
$store = false;
|
||||
//die(var_dump($config));
|
||||
foreach($config as $key => $check) {
|
||||
if($key == 'type') continue;
|
||||
if($key == 'dataonly') continue;
|
||||
if(!empty($check) && $check != '0') {
|
||||
$store = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
//die(var_dump($store))
|
||||
|
||||
$config['enabled'] = ($store) ? true : false;
|
||||
$config = json_encode($config);
|
||||
}
|
||||
|
||||
return $config;
|
||||
|
||||
}
|
||||
|
||||
public function parents()
|
||||
public function tags()
|
||||
{
|
||||
return $this->belongsToMany('App\Item', 'item_tag', 'item_id', 'tag_id');
|
||||
}
|
||||
public function children()
|
||||
{
|
||||
return $this->belongsToMany('App\Item', 'item_tag', 'tag_id', 'item_id');
|
||||
$id = $this->id;
|
||||
$tags = ItemTag::select('tag_id')->where('item_id', $id)->pluck('tag_id')->toArray();
|
||||
$tagdetails = self::select('id', 'title', 'url', 'pinned')->whereIn('id', $tags)->get();
|
||||
|
||||
return $tagdetails;
|
||||
}
|
||||
|
||||
protected function title(): Attribute
|
||||
{
|
||||
return Attribute::make(
|
||||
get: fn (mixed $value) => ($value === 'app.dashboard' ? __('app.dashboard') : $value),
|
||||
);
|
||||
}
|
||||
|
||||
protected function tagUrl(): Attribute
|
||||
{
|
||||
return Attribute::make(
|
||||
get: fn (mixed $value, array $attributes) => ($attributes['id'] === 0 ? '0-dash' : $attributes['url']),
|
||||
);
|
||||
}
|
||||
|
||||
public function getTagClass(): string
|
||||
{
|
||||
$tags = $this->tags();
|
||||
$slugs = [];
|
||||
|
||||
foreach ($tags as $tag) {
|
||||
if ($tag->id === 0) {
|
||||
$tag->url = '0-dash';
|
||||
}
|
||||
if ($tag->url) {
|
||||
$slugs[] = 'tag-'.$tag->url;
|
||||
}
|
||||
}
|
||||
|
||||
return implode(' ', $slugs);
|
||||
}
|
||||
|
||||
public function getTagList(): string
|
||||
{
|
||||
$tags = $this->tags();
|
||||
$titles = [];
|
||||
// print_r($tags);
|
||||
foreach ($tags as $tag) {
|
||||
if ($tag->title) {
|
||||
$titles[] = $tag->title;
|
||||
}
|
||||
}
|
||||
|
||||
return implode(', ', $titles);
|
||||
}
|
||||
|
||||
public function parents(): BelongsToMany
|
||||
{
|
||||
return $this->belongsToMany(Item::class, 'item_tag', 'item_id', 'tag_id');
|
||||
}
|
||||
|
||||
public function children(): BelongsToMany
|
||||
{
|
||||
return $this->belongsToMany(Item::class, 'item_tag', 'tag_id', 'item_id');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \Illuminate\Contracts\Foundation\Application|UrlGenerator|mixed|string
|
||||
*/
|
||||
public function getLinkAttribute()
|
||||
{
|
||||
if((int)$this->type === 1) {
|
||||
return '/tag/'.$this->url;
|
||||
if ((int) $this->type === 1) {
|
||||
return url('tag/'.$this->url);
|
||||
} else {
|
||||
return $this->url;
|
||||
}
|
||||
}
|
||||
|
||||
public function getDroppableAttribute()
|
||||
public function getDroppableAttribute(): string
|
||||
{
|
||||
if((int)$this->type === 1) {
|
||||
if ((int) $this->type === 1) {
|
||||
return ' droppable';
|
||||
} else {
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
public function getLinkTargetAttribute()
|
||||
public function getLinkTargetAttribute(): string
|
||||
{
|
||||
if((int)$this->type === 1) {
|
||||
$target = Setting::fetch('window_target');
|
||||
|
||||
if ((int) $this->type === 1 || $target === 'current') {
|
||||
return '';
|
||||
} else {
|
||||
return ' target="_blank"';
|
||||
return ' target="'.$target.'"';
|
||||
}
|
||||
}
|
||||
|
||||
public function getLinkIconAttribute()
|
||||
public function getLinkIconAttribute(): string
|
||||
{
|
||||
if((int)$this->type === 1) {
|
||||
if ((int) $this->type === 1) {
|
||||
return 'fa-tag';
|
||||
} else {
|
||||
return 'fa-arrow-alt-to-right';
|
||||
}
|
||||
}
|
||||
public function getLinkTypeAttribute()
|
||||
|
||||
public function getLinkTypeAttribute(): string
|
||||
{
|
||||
if((int)$this->type === 1) {
|
||||
if ((int) $this->type === 1) {
|
||||
return 'tags';
|
||||
} else {
|
||||
return 'items';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $class
|
||||
* @return false|mixed|string
|
||||
*/
|
||||
public static function nameFromClass($class)
|
||||
{
|
||||
$explode = explode('\\', $class);
|
||||
$name = end($explode);
|
||||
|
||||
return $name;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $query
|
||||
* @param $type
|
||||
* @return mixed
|
||||
*/
|
||||
public function scopeOfType($query, $type)
|
||||
{
|
||||
switch($type) {
|
||||
switch ($type) {
|
||||
case 'item':
|
||||
$typeid = 0;
|
||||
break;
|
||||
@@ -188,5 +277,121 @@ class Item extends Model
|
||||
return $query->where('type', $typeid);
|
||||
}
|
||||
|
||||
public function enhanced(): bool
|
||||
{
|
||||
/*if(isset($this->class) && !empty($this->class)) {
|
||||
$app = new $this->class;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
return (bool)($app instanceof \App\EnhancedApps);*/
|
||||
return $this->description !== null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $class
|
||||
*/
|
||||
public static function isEnhanced($class): bool
|
||||
{
|
||||
if (!class_exists($class, false) || $class === null || $class === 'null') {
|
||||
return false;
|
||||
}
|
||||
$app = new $class;
|
||||
|
||||
return (bool) ($app instanceof EnhancedApps);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $class
|
||||
* @return false|mixed
|
||||
*/
|
||||
public static function isSearchProvider($class)
|
||||
{
|
||||
if (!class_exists($class, false) || $class === null || $class === 'null') {
|
||||
return false;
|
||||
}
|
||||
$app = new $class;
|
||||
|
||||
return ((bool) ($app instanceof SearchInterface)) ? $app : false;
|
||||
}
|
||||
|
||||
public function enabled(): bool
|
||||
{
|
||||
if ($this->enhanced()) {
|
||||
$config = $this->getconfig();
|
||||
if ($config) {
|
||||
return (bool) $config->enabled;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed|stdClass
|
||||
*/
|
||||
public function getconfig()
|
||||
{
|
||||
// $explode = explode('\\', $this->class);
|
||||
|
||||
if (! isset($this->description) || empty($this->description)) {
|
||||
$config = new stdClass;
|
||||
// $config->name = end($explode);
|
||||
$config->enabled = false;
|
||||
$config->override_url = null;
|
||||
$config->apikey = null;
|
||||
|
||||
return $config;
|
||||
}
|
||||
|
||||
$config = json_decode($this->description);
|
||||
|
||||
// $config->name = end($explode);
|
||||
|
||||
$config->url = $this->url;
|
||||
if (isset($config->override_url) && ! empty($config->override_url)) {
|
||||
$config->url = $config->override_url;
|
||||
} else {
|
||||
$config->override_url = null;
|
||||
}
|
||||
|
||||
return $config;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $class
|
||||
*/
|
||||
public static function applicationDetails($class): ?Application
|
||||
{
|
||||
if (! empty($class)) {
|
||||
$name = self::nameFromClass($class);
|
||||
$application = Application::where('name', $name)->first();
|
||||
if ($application) {
|
||||
return $application;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $class
|
||||
*/
|
||||
public static function getApplicationDescription($class): string
|
||||
{
|
||||
$details = self::applicationDetails($class);
|
||||
if ($details !== null) {
|
||||
return $details->description.' - '.$details->license;
|
||||
}
|
||||
|
||||
return '';
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the user that owns the item.
|
||||
*/
|
||||
public function user(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo(User::class);
|
||||
}
|
||||
}
|
||||
|
||||
27
app/ItemTag.php
Normal file
27
app/ItemTag.php
Normal file
@@ -0,0 +1,27 @@
|
||||
<?php
|
||||
|
||||
namespace App;
|
||||
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Relations\Pivot;
|
||||
|
||||
/**
|
||||
* App\ItemTag
|
||||
*
|
||||
* @property int $item_id
|
||||
* @property int $tag_id
|
||||
* @property \Illuminate\Support\Carbon|null $created_at
|
||||
* @property \Illuminate\Support\Carbon|null $updated_at
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|ItemTag newModelQuery()
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|ItemTag newQuery()
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|ItemTag query()
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|ItemTag whereCreatedAt($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|ItemTag whereItemId($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|ItemTag whereTagId($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|ItemTag whereUpdatedAt($value)
|
||||
* @mixin \Eloquent
|
||||
*/
|
||||
class ItemTag extends Pivot
|
||||
{
|
||||
use HasFactory;
|
||||
}
|
||||
60
app/Jobs/ProcessApps.php
Normal file
60
app/Jobs/ProcessApps.php
Normal file
@@ -0,0 +1,60 @@
|
||||
<?php
|
||||
|
||||
namespace App\Jobs;
|
||||
|
||||
use App\Application;
|
||||
use App\Item;
|
||||
use App\SupportedApps;
|
||||
use GuzzleHttp\Exception\GuzzleException;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldBeUnique;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Foundation\Bus\Dispatchable;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Illuminate\Support\Facades\Storage;
|
||||
|
||||
class ProcessApps implements ShouldQueue, ShouldBeUnique
|
||||
{
|
||||
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
||||
|
||||
/**
|
||||
* Create a new job instance.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the job.
|
||||
*
|
||||
* @throws GuzzleException
|
||||
*/
|
||||
public function handle(): void
|
||||
{
|
||||
Log::debug('Process Apps dispatched');
|
||||
$localapps = Application::whereNull('class')->get();
|
||||
$json = SupportedApps::getList()->getBody();
|
||||
|
||||
Storage::disk('local')->put('supportedapps.json', $json);
|
||||
|
||||
foreach ($localapps as $app) {
|
||||
$app->class = $app->class();
|
||||
$app->save();
|
||||
}
|
||||
|
||||
$items = Item::whereNotNull('class')->get();
|
||||
foreach ($items as $item) {
|
||||
if (! file_exists(app_path('SupportedApps/'.Item::nameFromClass($item->class)))) {
|
||||
$app = Application::where('class', $item->class)->first();
|
||||
if ($app) {
|
||||
Application::getApp($app->appid);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
56
app/Jobs/UpdateApps.php
Normal file
56
app/Jobs/UpdateApps.php
Normal file
@@ -0,0 +1,56 @@
|
||||
<?php
|
||||
|
||||
namespace App\Jobs;
|
||||
|
||||
use App\Application;
|
||||
use GuzzleHttp\Exception\GuzzleException;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldBeUnique;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Foundation\Bus\Dispatchable;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
|
||||
class UpdateApps implements ShouldQueue, ShouldBeUnique
|
||||
{
|
||||
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
||||
|
||||
/**
|
||||
* Create a new job instance.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function __construct()
|
||||
{
|
||||
//
|
||||
}
|
||||
|
||||
/**
|
||||
* Execute the job.
|
||||
*
|
||||
* @throws GuzzleException
|
||||
*/
|
||||
public function handle(): void
|
||||
{
|
||||
Log::debug('Update of all apps triggered!');
|
||||
$apps = Application::all('appid')->toArray();
|
||||
|
||||
// We onl update the apps that are actually in use by items
|
||||
// 1 sec delay after each update to throttle the requests
|
||||
foreach ($apps as $appKey => $app) {
|
||||
Application::getApp($app['appid']);
|
||||
sleep(1);
|
||||
}
|
||||
|
||||
Log::debug('Update of all apps finished!');
|
||||
|
||||
Cache::lock('updateApps')->forceRelease();
|
||||
}
|
||||
|
||||
public function failed($exception): void
|
||||
{
|
||||
Cache::lock('updateApps')->forceRelease();
|
||||
}
|
||||
}
|
||||
@@ -2,69 +2,197 @@
|
||||
|
||||
namespace App\Providers;
|
||||
|
||||
use Illuminate\Support\ServiceProvider;
|
||||
use Artisan;
|
||||
use Schema;
|
||||
use App\Application;
|
||||
use App\Jobs\ProcessApps;
|
||||
use App\Jobs\UpdateApps;
|
||||
use App\Setting;
|
||||
use App\User;
|
||||
use Barryvdh\LaravelIdeHelper\IdeHelperServiceProvider;
|
||||
use Illuminate\Support\Facades\Artisan;
|
||||
use Illuminate\Support\Facades\Cache;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Illuminate\Support\Facades\Schema;
|
||||
use Illuminate\Support\ServiceProvider;
|
||||
use Psr\Container\ContainerExceptionInterface;
|
||||
use Psr\Container\NotFoundExceptionInterface;
|
||||
use App\Services\CustomFormBuilder;
|
||||
use Spatie\Html\Html;
|
||||
|
||||
class AppServiceProvider extends ServiceProvider
|
||||
{
|
||||
/**
|
||||
* Bootstrap any application services.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function boot()
|
||||
public function boot(): void
|
||||
{
|
||||
$alt_bg = '';
|
||||
|
||||
if(!is_file(base_path('.env'))) {
|
||||
touch(base_path('.env'));
|
||||
Artisan::call('key:generate');
|
||||
if (! class_exists('ZipArchive')) {
|
||||
die('You are missing php-zip');
|
||||
}
|
||||
if(!is_file(database_path('app.sqlite'))) {
|
||||
// first time setup
|
||||
touch(database_path('app.sqlite'));
|
||||
Artisan::call('migrate', array('--path' => 'database/migrations', '--force' => true, '--seed' => true));
|
||||
//Cache
|
||||
//Artisan::call('config:cache');
|
||||
//Artisan::call('route:cache');
|
||||
}
|
||||
if(is_file(database_path('app.sqlite'))) {
|
||||
if(Schema::hasTable('settings')) {
|
||||
if($bg_image = Setting::fetch('background_image')) {
|
||||
$alt_bg = ' style="background-image: url(/storage/'.$bg_image.')"';
|
||||
}
|
||||
|
||||
// check version to see if an upgrade is needed
|
||||
$db_version = Setting::fetch('version');
|
||||
$app_version = config('app.version');
|
||||
if(version_compare($app_version, $db_version) == 1) { // app is higher than db, so need to run migrations etc
|
||||
Artisan::call('migrate', array('--path' => 'database/migrations', '--force' => true, '--seed' => true));
|
||||
}
|
||||
} else {
|
||||
Artisan::call('migrate', array('--path' => 'database/migrations', '--force' => true, '--seed' => true));
|
||||
}
|
||||
$lang = Setting::fetch('language');
|
||||
\App::setLocale($lang);
|
||||
$this->createEnvFile();
|
||||
|
||||
}
|
||||
if(!is_file(public_path('storage'))) {
|
||||
$this->setupDatabase();
|
||||
|
||||
if (! is_file(public_path('storage/.gitignore'))) {
|
||||
Artisan::call('storage:link');
|
||||
\Session::put('current_user', null);
|
||||
}
|
||||
view()->share('alt_bg', $alt_bg);
|
||||
|
||||
$applications = Application::all();
|
||||
|
||||
if ($applications->count() <= 0) {
|
||||
ProcessApps::dispatch();
|
||||
}
|
||||
|
||||
$lang = Setting::fetch('language');
|
||||
\App::setLocale($lang);
|
||||
|
||||
// User specific settings need to go here as session isn't available at this point in the app
|
||||
view()->composer('*', function ($view) {
|
||||
if (isset($_SERVER['HTTP_AUTHORIZATION']) && ! empty($_SERVER['HTTP_AUTHORIZATION'])) {
|
||||
list($_SERVER['PHP_AUTH_USER'], $_SERVER['PHP_AUTH_PW']) =
|
||||
explode(':', base64_decode(substr($_SERVER['HTTP_AUTHORIZATION'], 6)));
|
||||
}
|
||||
if (! \Auth::check()) {
|
||||
if (isset($_SERVER['PHP_AUTH_USER']) && isset($_SERVER['PHP_AUTH_PW'])
|
||||
&& ! empty($_SERVER['PHP_AUTH_USER']) && ! empty($_SERVER['PHP_AUTH_PW'])) {
|
||||
$credentials = ['username' => $_SERVER['PHP_AUTH_USER'], 'password' => $_SERVER['PHP_AUTH_PW']];
|
||||
|
||||
if (\Auth::attempt($credentials, true)) {
|
||||
// Authentication passed...
|
||||
$user = \Auth::user();
|
||||
//\Session::put('current_user', $user);
|
||||
session(['current_user' => $user]);
|
||||
}
|
||||
} elseif (isset($_SERVER['REMOTE_USER']) && ! empty($_SERVER['REMOTE_USER'])) {
|
||||
$user = User::where('username', $_SERVER['REMOTE_USER'])->first();
|
||||
if ($user) {
|
||||
\Auth::login($user, true);
|
||||
session(['current_user' => $user]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$alt_bg = '';
|
||||
$trianglify = 'false';
|
||||
$trianglify_seed = null;
|
||||
if (Setting::fetch('trianglify')) {
|
||||
$trianglify = 'true';
|
||||
$trianglify_seed = Setting::fetch('trianglify_seed');
|
||||
} elseif ($bg_image = Setting::fetch('background_image')) {
|
||||
$alt_bg = ' style="background-image: url(storage/'.$bg_image.')"';
|
||||
}
|
||||
|
||||
$allusers = User::all();
|
||||
$current_user = User::currentUser();
|
||||
|
||||
$view->with('alt_bg', $alt_bg);
|
||||
$view->with('trianglify', $trianglify);
|
||||
$view->with('trianglify_seed', $trianglify_seed);
|
||||
$view->with('allusers', $allusers);
|
||||
$view->with('current_user', $current_user);
|
||||
if (config('app.auth_roles_enable')) {
|
||||
$view->with('enable_auth_admin_controls', in_array(config('app.auth_roles_admin'), explode(config('app.auth_roles_delimiter'), $_SERVER[config('app.auth_roles_http_header')])));
|
||||
} else {
|
||||
$view->with('enable_auth_admin_controls', true);
|
||||
}
|
||||
});
|
||||
|
||||
$this->app['view']->addNamespace('SupportedApps', app_path('SupportedApps'));
|
||||
|
||||
if (env('FORCE_HTTPS') === true) {
|
||||
\URL::forceScheme('https');
|
||||
}
|
||||
|
||||
if (env('APP_URL') != 'http://localhost') {
|
||||
\URL::forceRootUrl(env('APP_URL'));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate app key if missing and .env exists
|
||||
*/
|
||||
public function genKey(): void
|
||||
{
|
||||
if (is_file(base_path('.env'))) {
|
||||
if (empty(env('APP_KEY'))) {
|
||||
Artisan::call('key:generate', ['--force' => true, '--no-interaction' => true]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Register any application services.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function register()
|
||||
public function register(): void
|
||||
{
|
||||
if ($this->app->isLocal()) {
|
||||
$this->app->register(IdeHelperServiceProvider::class);
|
||||
}
|
||||
|
||||
$this->app->singleton('custom-form', function ($app) {
|
||||
return new CustomFormBuilder($app->make(Html::class));
|
||||
});
|
||||
|
||||
$this->app->singleton('settings', function () {
|
||||
return new Setting();
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if database needs an update or do first time database setup
|
||||
*
|
||||
* @throws ContainerExceptionInterface
|
||||
* @throws NotFoundExceptionInterface
|
||||
*/
|
||||
public function setupDatabase(): void
|
||||
{
|
||||
$db_type = config()->get('database.default');
|
||||
|
||||
if ($db_type == 'sqlite') {
|
||||
$db_file = database_path(env('DB_DATABASE', 'app.sqlite'));
|
||||
Log::debug('SQLite Database Path: ' . $db_file);
|
||||
if (! is_file($db_file)) {
|
||||
touch($db_file);
|
||||
}
|
||||
}
|
||||
|
||||
if ($this->needsDBUpdate()) {
|
||||
Artisan::call('migrate', ['--path' => 'database/migrations', '--force' => true, '--seed' => true]);
|
||||
ProcessApps::dispatchSync();
|
||||
$this->updateApps();
|
||||
}
|
||||
}
|
||||
|
||||
public function createEnvFile(): void
|
||||
{
|
||||
if (!is_file(base_path('.env'))) {
|
||||
copy(base_path('.env.example'), base_path('.env'));
|
||||
}
|
||||
|
||||
$this->genKey();
|
||||
}
|
||||
|
||||
private function needsDBUpdate(): bool
|
||||
{
|
||||
if (!Schema::hasTable('settings')) {
|
||||
return true;
|
||||
}
|
||||
|
||||
$db_version = Setting::_fetch('version');
|
||||
$app_version = config('app.version');
|
||||
|
||||
return version_compare($app_version, $db_version) === 1;
|
||||
}
|
||||
|
||||
private function updateApps(): void
|
||||
{
|
||||
// This lock ensures that the job is not invoked multiple times.
|
||||
// In 5 minutes all app updates should be finished.
|
||||
$lock = Cache::lock('updateApps', 5*60);
|
||||
|
||||
if ($lock->get()) {
|
||||
UpdateApps::dispatchAfterResponse();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,30 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Providers;
|
||||
|
||||
use Illuminate\Support\Facades\Gate;
|
||||
use Illuminate\Foundation\Support\Providers\AuthServiceProvider as ServiceProvider;
|
||||
|
||||
class AuthServiceProvider extends ServiceProvider
|
||||
{
|
||||
/**
|
||||
* The policy mappings for the application.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $policies = [
|
||||
'App\Model' => 'App\Policies\ModelPolicy',
|
||||
];
|
||||
|
||||
/**
|
||||
* Register any authentication / authorization services.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function boot()
|
||||
{
|
||||
$this->registerPolicies();
|
||||
|
||||
//
|
||||
}
|
||||
}
|
||||
@@ -1,21 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Providers;
|
||||
|
||||
use Illuminate\Support\ServiceProvider;
|
||||
use Illuminate\Support\Facades\Broadcast;
|
||||
|
||||
class BroadcastServiceProvider extends ServiceProvider
|
||||
{
|
||||
/**
|
||||
* Bootstrap any application services.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function boot()
|
||||
{
|
||||
Broadcast::routes();
|
||||
|
||||
require base_path('routes/channels.php');
|
||||
}
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Providers;
|
||||
|
||||
use Illuminate\Support\Facades\Event;
|
||||
use Illuminate\Foundation\Support\Providers\EventServiceProvider as ServiceProvider;
|
||||
|
||||
class EventServiceProvider extends ServiceProvider
|
||||
{
|
||||
/**
|
||||
* The event listener mappings for the application.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $listen = [
|
||||
'App\Events\Event' => [
|
||||
'App\Listeners\EventListener',
|
||||
],
|
||||
];
|
||||
|
||||
/**
|
||||
* Register any events for your application.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function boot()
|
||||
{
|
||||
parent::boot();
|
||||
|
||||
//
|
||||
}
|
||||
}
|
||||
@@ -2,8 +2,8 @@
|
||||
|
||||
namespace App\Providers;
|
||||
|
||||
use Illuminate\Support\Facades\Route;
|
||||
use Illuminate\Foundation\Support\Providers\RouteServiceProvider as ServiceProvider;
|
||||
use Illuminate\Support\Facades\Route;
|
||||
|
||||
class RouteServiceProvider extends ServiceProvider
|
||||
{
|
||||
@@ -12,16 +12,13 @@ class RouteServiceProvider extends ServiceProvider
|
||||
*
|
||||
* In addition, it is set as the URL generator's root namespace.
|
||||
*
|
||||
* @var string
|
||||
* REMOVED WITH LARAVEL 8 UPGRADE
|
||||
*/
|
||||
protected $namespace = 'App\Http\Controllers';
|
||||
|
||||
/**
|
||||
* Define your route model bindings, pattern filters, etc.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function boot()
|
||||
public function boot(): void
|
||||
{
|
||||
//
|
||||
|
||||
@@ -30,10 +27,8 @@ class RouteServiceProvider extends ServiceProvider
|
||||
|
||||
/**
|
||||
* Define the routes for the application.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function map()
|
||||
public function map(): void
|
||||
{
|
||||
$this->mapApiRoutes();
|
||||
|
||||
@@ -46,13 +41,10 @@ class RouteServiceProvider extends ServiceProvider
|
||||
* Define the "web" routes for the application.
|
||||
*
|
||||
* These routes all receive session state, CSRF protection, etc.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function mapWebRoutes()
|
||||
protected function mapWebRoutes(): void
|
||||
{
|
||||
Route::middleware('web')
|
||||
->namespace($this->namespace)
|
||||
->group(base_path('routes/web.php'));
|
||||
}
|
||||
|
||||
@@ -60,14 +52,11 @@ class RouteServiceProvider extends ServiceProvider
|
||||
* Define the "api" routes for the application.
|
||||
*
|
||||
* These routes are typically stateless.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
protected function mapApiRoutes()
|
||||
protected function mapApiRoutes(): void
|
||||
{
|
||||
Route::prefix('api')
|
||||
->middleware('api')
|
||||
->namespace($this->namespace)
|
||||
->group(base_path('routes/api.php'));
|
||||
}
|
||||
}
|
||||
|
||||
134
app/Search.php
Normal file
134
app/Search.php
Normal file
@@ -0,0 +1,134 @@
|
||||
<?php
|
||||
|
||||
namespace App;
|
||||
|
||||
use Illuminate\Support\Collection;
|
||||
use Illuminate\Support\Facades\Request as Input;
|
||||
use Yaml;
|
||||
|
||||
abstract class Search
|
||||
{
|
||||
/**
|
||||
* List of all search providers
|
||||
*
|
||||
* @return Collection
|
||||
*/
|
||||
public static function providers(): Collection
|
||||
{
|
||||
$providers = self::standardProviders();
|
||||
$providers = $providers + self::appProviders();
|
||||
|
||||
return collect($providers);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets details for a single provider
|
||||
*
|
||||
* @return false|object
|
||||
*/
|
||||
public static function providerDetails($provider)
|
||||
{
|
||||
$providers = self::providers();
|
||||
if (! isset($providers[$provider])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return (object) $providers[$provider] ?? false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Array of the standard providers
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function standardProviders(): array
|
||||
{
|
||||
// $providers = json_decode(file_get_contents(storage_path('app/searchproviders.json')));
|
||||
// print_r($providers);
|
||||
$providers = Yaml::parseFile(storage_path('app/searchproviders.yaml'));
|
||||
$all = [];
|
||||
foreach ($providers as $key => $provider) {
|
||||
$all[$key] = $provider;
|
||||
$all[$key]['type'] = 'standard';
|
||||
}
|
||||
|
||||
return $all;
|
||||
}
|
||||
|
||||
/**
|
||||
* Loops through users apps to see if app is a search provider, might be worth
|
||||
* looking into caching this at some point
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public static function appProviders(): array
|
||||
{
|
||||
$providers = [];
|
||||
$userapps = Item::all();
|
||||
foreach ($userapps as $app) {
|
||||
if (empty($app->class)) {
|
||||
continue;
|
||||
}
|
||||
if (($provider = Item::isSearchProvider($app->class)) !== false) {
|
||||
$name = Item::nameFromClass($app->class);
|
||||
$providers[$app->id] = [
|
||||
'id' => $app->id,
|
||||
'type' => $provider->type,
|
||||
'class' => $app->class,
|
||||
'url' => $app->url,
|
||||
'name' => $app->title,
|
||||
'colour' => $app->colour,
|
||||
'icon' => $app->icon,
|
||||
'description' => $app->description,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
return $providers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Outputs the search form
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function form(): string
|
||||
{
|
||||
$output = '';
|
||||
$homepage_search = Setting::fetch('homepage_search');
|
||||
$search_provider = Setting::where('key', '=', 'search_provider')->first();
|
||||
$user_search_provider = Setting::fetch('search_provider');
|
||||
//die(print_r($search_provider));
|
||||
|
||||
//die(var_dump($user_search_provider));
|
||||
// return early if search isn't applicable
|
||||
if ((bool) $homepage_search !== true) {
|
||||
return $output;
|
||||
}
|
||||
$user_search_provider = Input::get('p') ?? $user_search_provider ?? 'none';
|
||||
|
||||
if ((bool) $search_provider) {
|
||||
if ((bool) $user_search_provider) {
|
||||
$name = 'app.options.'.$user_search_provider;
|
||||
$provider = self::providerDetails($user_search_provider);
|
||||
|
||||
$output .= '<div class="searchform">';
|
||||
$output .= '<form action="'.url('search').'"'.getLinkTargetAttribute().' method="get">';
|
||||
$output .= '<div id="search-container" class="input-container">';
|
||||
$output .= '<select name="provider">';
|
||||
foreach (self::providers() as $key => $searchprovider) {
|
||||
$selected = ((string) $key === (string) $user_search_provider) ? ' selected="selected"' : '';
|
||||
$output .= '<option value="'.$key.'"'.$selected.'>'.$searchprovider['name'].'</option>';
|
||||
}
|
||||
$output .= '</select>';
|
||||
$output .= '<input type="text" name="q" value="'.(Input::get('q') ?? '').'" class="homesearch" autofocus placeholder="'.__('app.settings.search').'..." />';
|
||||
$output .= '<button type="submit">'.ucwords(__('app.settings.search')).'</button>';
|
||||
$output .= '</div>';
|
||||
$output .= '</form>';
|
||||
$output .= '</div>';
|
||||
}
|
||||
}
|
||||
|
||||
return $output;
|
||||
}
|
||||
}
|
||||
8
app/SearchInterface.php
Normal file
8
app/SearchInterface.php
Normal file
@@ -0,0 +1,8 @@
|
||||
<?php
|
||||
|
||||
namespace App;
|
||||
|
||||
interface SearchInterface
|
||||
{
|
||||
public function getResults($query, $providerdetails);
|
||||
}
|
||||
46
app/Services/CustomFormBuilder.php
Normal file
46
app/Services/CustomFormBuilder.php
Normal file
@@ -0,0 +1,46 @@
|
||||
<?php
|
||||
|
||||
namespace App\Services;
|
||||
|
||||
use Spatie\Html\Html;
|
||||
use Illuminate\Support\HtmlString;
|
||||
|
||||
class CustomFormBuilder
|
||||
{
|
||||
protected Html $html;
|
||||
|
||||
public function __construct(Html $html)
|
||||
{
|
||||
$this->html = $html;
|
||||
}
|
||||
|
||||
public function text($name, $value = null, $options = [])
|
||||
{
|
||||
return new HtmlString(
|
||||
$this->html->input('text', $name, $value)->attributes($options)
|
||||
);
|
||||
}
|
||||
|
||||
public function select($name, $list = [], $selected = null, $options = [])
|
||||
{
|
||||
return new HtmlString(
|
||||
$this->html->select($name)->options($list, $selected)->attributes($options)
|
||||
);
|
||||
}
|
||||
|
||||
public function textarea($name, $value = null, $options = [])
|
||||
{
|
||||
return new HtmlString(
|
||||
$this->html->textarea($name, $value)->attributes($options)
|
||||
);
|
||||
}
|
||||
|
||||
public function input($type, $name, $value = null, $options = [])
|
||||
{
|
||||
return new HtmlString(
|
||||
$this->html->input($type, $name, $value)->attributes($options)
|
||||
);
|
||||
}
|
||||
|
||||
// Add other methods as needed
|
||||
}
|
||||
282
app/Setting.php
282
app/Setting.php
@@ -2,10 +2,46 @@
|
||||
|
||||
namespace App;
|
||||
|
||||
use Illuminate\Database\Eloquent\Builder;
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Support\Facades\Input;
|
||||
use Form;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsTo;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
|
||||
use Illuminate\Http\Request;
|
||||
use Illuminate\Session\SessionManager;
|
||||
use Illuminate\Session\Store;
|
||||
use enshrined\svgSanitize\Sanitizer;
|
||||
|
||||
/**
|
||||
* App\Setting
|
||||
*
|
||||
* @mixin Builder
|
||||
* @property int $id
|
||||
* @property int $group_id
|
||||
* @property string $key
|
||||
* @property string $type
|
||||
* @property string|null $options
|
||||
* @property string $label
|
||||
* @property string|null $value
|
||||
* @property string $order
|
||||
* @property int $system
|
||||
* @property-read mixed $edit_value
|
||||
* @property-read mixed $list_value
|
||||
* @property-read \App\SettingGroup|null $group
|
||||
* @property-read \Illuminate\Database\Eloquent\Collection|\App\User[] $users
|
||||
* @property-read int|null $users_count
|
||||
* @method static Builder|Setting newModelQuery()
|
||||
* @method static Builder|Setting newQuery()
|
||||
* @method static Builder|Setting query()
|
||||
* @method static Builder|Setting whereGroupId($value)
|
||||
* @method static Builder|Setting whereId($value)
|
||||
* @method static Builder|Setting whereKey($value)
|
||||
* @method static Builder|Setting whereLabel($value)
|
||||
* @method static Builder|Setting whereOptions($value)
|
||||
* @method static Builder|Setting whereOrder($value)
|
||||
* @method static Builder|Setting whereSystem($value)
|
||||
* @method static Builder|Setting whereType($value)
|
||||
* @method static Builder|Setting whereValue($value)
|
||||
*/
|
||||
class Setting extends Model
|
||||
{
|
||||
/**
|
||||
@@ -16,7 +52,7 @@ class Setting extends Model
|
||||
protected $table = 'settings';
|
||||
|
||||
protected $fillable = [
|
||||
'id', 'group_id', 'key', 'type', 'options', 'label', 'value', 'order', 'system'
|
||||
'id', 'group_id', 'key', 'type', 'options', 'label', 'value', 'order', 'system',
|
||||
];
|
||||
|
||||
/**
|
||||
@@ -33,41 +69,67 @@ class Setting extends Model
|
||||
*/
|
||||
protected static $cache = [];
|
||||
|
||||
/**
|
||||
* @return array
|
||||
*/
|
||||
public static function getInput()
|
||||
public static function getInput(Request $request): object
|
||||
{
|
||||
$image = $request->file('value');
|
||||
if ($image && $image->getClientOriginalExtension() === 'svg') {
|
||||
$sanitizer = new Sanitizer();
|
||||
$sanitizedSvg = $sanitizer->sanitize(file_get_contents($image->getRealPath()));
|
||||
|
||||
// Verify that the sanitization removed malicious content
|
||||
if (strpos($sanitizedSvg, '<script>') !== false) {
|
||||
throw new \Exception('SVG contains malicious content and cannot be uploaded.');
|
||||
}
|
||||
|
||||
// Save the sanitized SVG back to the file
|
||||
file_put_contents($image->getRealPath(), $sanitizedSvg);
|
||||
}
|
||||
|
||||
return (object) [
|
||||
'value' => Input::get('value'),
|
||||
'image' => Input::file('value'),
|
||||
'value' => $request->input('value'),
|
||||
'image' => $image,
|
||||
];
|
||||
}
|
||||
|
||||
public function getListValueAttribute()
|
||||
{
|
||||
switch($this->type) {
|
||||
if ((bool) $this->system === true) {
|
||||
$value = self::_fetch($this->key);
|
||||
} else {
|
||||
$value = self::fetch($this->key);
|
||||
}
|
||||
$this->value = $value;
|
||||
switch ($this->type) {
|
||||
case 'image':
|
||||
if(!empty($this->value)) {
|
||||
$value = '<a href="'.asset('storage/'.$this->value).'" title="'.__('app.settings.view').'" target="_blank">'.__('app.settings.view').'</a>';
|
||||
if (! empty($this->value)) {
|
||||
$value = '<a href="'.asset('storage/'.$this->value).'" title="'.
|
||||
__('app.settings.view').
|
||||
'" target="_blank">'.
|
||||
__('app.settings.view').
|
||||
'</a>';
|
||||
} else {
|
||||
$value = __('app.options.none');
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'boolean':
|
||||
if((bool)$this->value === true) {
|
||||
if ((bool) $this->value === true) {
|
||||
$value = __('app.options.yes');
|
||||
} else {
|
||||
$value = __('app.options.no');
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 'select':
|
||||
if(!empty($this->value) && $this->value !== 'none') {
|
||||
$options = (array)json_decode($this->options);
|
||||
$value = __($options[$this->value]);
|
||||
if (! empty($this->value) && $this->value !== 'none') {
|
||||
$options = (array) json_decode($this->options);
|
||||
if ($this->key === 'search_provider') {
|
||||
$options = Search::providers()->pluck('name', 'id')->toArray();
|
||||
}
|
||||
$value = (array_key_exists($this->value, $options))
|
||||
? __($options[$this->value])
|
||||
: __('app.options.none');
|
||||
} else {
|
||||
$value = __('app.options.none');
|
||||
}
|
||||
}
|
||||
break;
|
||||
default:
|
||||
$value = __($this->value);
|
||||
@@ -75,26 +137,46 @@ class Setting extends Model
|
||||
}
|
||||
|
||||
return $value;
|
||||
|
||||
}
|
||||
|
||||
public function getEditValueAttribute()
|
||||
{
|
||||
switch($this->type) {
|
||||
if ((bool) $this->system === true) {
|
||||
$value = self::_fetch($this->key);
|
||||
} else {
|
||||
$value = self::fetch($this->key);
|
||||
}
|
||||
$this->value = $value;
|
||||
switch ($this->type) {
|
||||
case 'image':
|
||||
$value = '';
|
||||
if(isset($this->value) && !empty($this->value)) {
|
||||
$value .= '<a class="setting-view-image" href="'.asset('storage/'.$this->value).'" title="'.__('app.settings.view').'" target="_blank"><img src="'.asset('storage/'.$this->value).'" /></a>';
|
||||
if (isset($this->value) && ! empty($this->value)) {
|
||||
$value .= '<a class="setting-view-image" href="'.
|
||||
asset('storage/'.$this->value).
|
||||
'" title="'.
|
||||
__('app.settings.view').
|
||||
'" target="_blank"><img src="'.
|
||||
asset('storage/'.
|
||||
$this->value).
|
||||
'" /></a>';
|
||||
}
|
||||
$value .= Form::file('value', ['class' => 'form-control']);
|
||||
if(isset($this->value) && !empty($this->value)) {
|
||||
$value .= '<a class="settinglink" href="'.route('settings.clear', $this->id).'" title="'.__('app.settings.remove').'">'.__('app.settings.reset').'</a>';
|
||||
$value .= '<input type="file" name="value" class="form-control" />';
|
||||
if (isset($this->value) && ! empty($this->value)) {
|
||||
$value .= '<a class="settinglink" href="'.
|
||||
route('settings.clear', $this->id).
|
||||
'" title="'.
|
||||
__('app.settings.remove').
|
||||
'">'.
|
||||
__('app.settings.reset').
|
||||
'</a>';
|
||||
}
|
||||
|
||||
|
||||
break;
|
||||
case 'boolean':
|
||||
$checked = false;
|
||||
if(isset($this->value) && (bool)$this->value === true) $checked = true;
|
||||
if (isset($this->value) && (bool) $this->value === true) {
|
||||
$checked = true;
|
||||
}
|
||||
$set_checked = ($checked) ? ' checked="checked"' : '';
|
||||
$value = '
|
||||
<input type="hidden" name="value" value="0" />
|
||||
@@ -106,113 +188,115 @@ class Setting extends Model
|
||||
break;
|
||||
case 'select':
|
||||
$options = json_decode($this->options);
|
||||
foreach($options as $key => $opt) {
|
||||
$options->$key = __($opt);
|
||||
if ($this->key === 'search_provider') {
|
||||
$options = Search::providers()->pluck('name', 'id');
|
||||
}
|
||||
$value = Form::select('value', $options, null, ['class' => 'form-control']);
|
||||
$value = '<select name="value" class="form-control">';
|
||||
foreach ($options as $key => $opt) {
|
||||
$value .= '<option value="'.$key.'" '.(($this->value == $key) ? 'selected' : '').'>'.__($opt).'</option>';
|
||||
}
|
||||
$value .= '</select>';
|
||||
break;
|
||||
case 'textarea':
|
||||
$value = '<textarea name="value" class="form-control" cols="44" rows="15"></textarea>';
|
||||
break;
|
||||
default:
|
||||
$value = Form::text('value', null, ['class' => 'form-control']);
|
||||
$value = '<input type="text" name="value" class="form-control" />';
|
||||
break;
|
||||
}
|
||||
|
||||
return $value;
|
||||
|
||||
}
|
||||
|
||||
public function group()
|
||||
public function group(): BelongsTo
|
||||
{
|
||||
return $this->belongsTo('App\SettingGroup', 'group_id');
|
||||
return $this->belongsTo(\App\SettingGroup::class, 'group_id');
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $key
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public static function fetch($key)
|
||||
public static function fetch(string $key)
|
||||
{
|
||||
if (Setting::cached($key)) {
|
||||
return Setting::$cache[$key];
|
||||
} else {
|
||||
$find = self::where('key', '=', $key)->first();
|
||||
$user = self::user();
|
||||
|
||||
if (!is_null($find)) {
|
||||
return self::_fetch($key, $user);
|
||||
}
|
||||
|
||||
// @codingStandardsIgnoreStart
|
||||
/**
|
||||
*
|
||||
* @return mixed
|
||||
*/
|
||||
public static function _fetch(string $key, $user = null)
|
||||
{
|
||||
// @codingStandardsIgnoreEnd
|
||||
//$cachekey = ($user === null) ? $key : $key.'-'.$user->id;
|
||||
//if (Setting::cached($cachekey)) {
|
||||
// return Setting::$cache[$cachekey];
|
||||
//} else {
|
||||
$find = self::where('key', '=', $key)->first();
|
||||
|
||||
if (! is_null($find)) {
|
||||
if ((bool) $find->system === true) { // if system variable use global value
|
||||
$value = $find->value;
|
||||
Setting::add($key, $value);
|
||||
|
||||
return $value;
|
||||
} else {
|
||||
return false;
|
||||
} else { // not system variable so use user specific value
|
||||
// check if user specified value has been set
|
||||
//print_r($user);
|
||||
$usersetting = $user->settings()->where('id', $find->id)->first();
|
||||
//print_r($user->settings);
|
||||
//die(var_dump($usersetting));
|
||||
//->pivot->value;
|
||||
//echo "user: ".$user->id." --- ".$usersettings;
|
||||
if (isset($usersetting) && ! empty($usersetting)) {
|
||||
$value = $usersetting->pivot->uservalue;
|
||||
} else { // if not get default from base setting
|
||||
//$user->settings()->save($find, ['value' => $find->value]);
|
||||
//$has_setting = $user->settings()->where('id', $find->id)->exists();
|
||||
//if($has_setting) {
|
||||
// $user->settings()->updateExistingPivot($find->id, ['uservalue' => (string)$find->value]);
|
||||
//} else {
|
||||
// $user->settings()->save($find, ['uservalue' => (string)$find->value]);
|
||||
//}
|
||||
$value = $find->value;
|
||||
}
|
||||
}
|
||||
//Setting::add($cachekey, $value);
|
||||
|
||||
return $value;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
//}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $key
|
||||
* @param $value
|
||||
*/
|
||||
public static function add($key, $value)
|
||||
public static function add(string $key, $value)
|
||||
{
|
||||
Setting::$cache[$key] = $value;
|
||||
self::$cache[$key] = $value;
|
||||
}
|
||||
|
||||
public static function cached(string $key): bool
|
||||
{
|
||||
return array_key_exists($key, self::$cache);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $key
|
||||
*
|
||||
* @return bool
|
||||
* The users that belong to the setting.
|
||||
*/
|
||||
public static function cached($key)
|
||||
public function users(): BelongsToMany
|
||||
{
|
||||
return array_key_exists($key, Setting::$cache);
|
||||
return $this->belongsToMany(\App\User::class)->using(\App\SettingUser::class)->withPivot('uservalue');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return html
|
||||
* @return \Illuminate\Contracts\Foundation\Application|SessionManager|Store|mixed
|
||||
*/
|
||||
public static function search()
|
||||
public static function user()
|
||||
{
|
||||
$output = '';
|
||||
$homepage_search = self::fetch('homepage_search');
|
||||
$search_provider = self::where('key', '=', 'search_provider')->first();
|
||||
|
||||
//die(var_dump($search_provider->value));
|
||||
// return early if search isn't applicable
|
||||
if((bool)$homepage_search !== true) return $output;
|
||||
if($search_provider->value === 'none') return $output;
|
||||
if(empty($search_provider->value)) return $output;
|
||||
if(is_null($search_provider->value)) return $output;
|
||||
|
||||
|
||||
if((bool)$homepage_search && (bool)$search_provider) {
|
||||
|
||||
$options = (array)json_decode($search_provider->options);
|
||||
$name = $options[$search_provider->value];
|
||||
if((bool)$search_provider->value) {
|
||||
switch($search_provider->value) {
|
||||
case 'google':
|
||||
$url = 'https://www.google.com/search';
|
||||
$var = 'q';
|
||||
break;
|
||||
case 'ddg':
|
||||
$url = 'https://duckduckgo.com/';
|
||||
$var = 'q';
|
||||
break;
|
||||
case 'bing':
|
||||
$url = 'https://www.bing.com/search';
|
||||
$var = 'q';
|
||||
break;
|
||||
}
|
||||
$output .= '<div class="searchform">';
|
||||
$output .= Form::open(['url' => $url, 'method' => 'get']);
|
||||
$output .= '<div class="input-container">';
|
||||
$output .= Form::text($var, null, ['class' => 'homesearch', 'placeholder' => __($name).' '.__('app.settings.search').'...']);
|
||||
$output .= '<button type="submit">'.ucwords(__('app.settings.search')).'</button>';
|
||||
$output .= '</div>';
|
||||
$output .= Form::close();
|
||||
$output .= '</div>';
|
||||
}
|
||||
}
|
||||
return $output;
|
||||
return User::currentUser();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,24 @@
|
||||
namespace App;
|
||||
|
||||
use Illuminate\Database\Eloquent\Model;
|
||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||
|
||||
/**
|
||||
* App\SettingGroup
|
||||
*
|
||||
* @property int $id
|
||||
* @property string $title
|
||||
* @property int $order
|
||||
* @property-read \Illuminate\Database\Eloquent\Collection|\App\Setting[] $settings
|
||||
* @property-read int|null $settings_count
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|SettingGroup newModelQuery()
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|SettingGroup newQuery()
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|SettingGroup query()
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|SettingGroup whereId($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|SettingGroup whereOrder($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|SettingGroup whereTitle($value)
|
||||
* @mixin \Eloquent
|
||||
*/
|
||||
class SettingGroup extends Model
|
||||
{
|
||||
/**
|
||||
@@ -20,8 +37,8 @@ class SettingGroup extends Model
|
||||
*/
|
||||
public $timestamps = false;
|
||||
|
||||
public function settings()
|
||||
public function settings(): HasMany
|
||||
{
|
||||
return $this->hasMany('App\Setting', 'group_id');
|
||||
return $this->hasMany(\App\Setting::class, 'group_id');
|
||||
}
|
||||
}
|
||||
|
||||
24
app/SettingUser.php
Normal file
24
app/SettingUser.php
Normal file
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
namespace App;
|
||||
|
||||
use Illuminate\Database\Eloquent\Relations\Pivot;
|
||||
|
||||
/**
|
||||
* App\SettingUser
|
||||
*
|
||||
* @property int $setting_id
|
||||
* @property int $user_id
|
||||
* @property string|null $uservalue
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|SettingUser newModelQuery()
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|SettingUser newQuery()
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|SettingUser query()
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|SettingUser whereSettingId($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|SettingUser whereUserId($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|SettingUser whereUservalue($value)
|
||||
* @mixin \Eloquent
|
||||
*/
|
||||
class SettingUser extends Pivot
|
||||
{
|
||||
//
|
||||
}
|
||||
232
app/SupportedApps.php
Normal file
232
app/SupportedApps.php
Normal file
@@ -0,0 +1,232 @@
|
||||
<?php
|
||||
|
||||
namespace App;
|
||||
|
||||
use GuzzleHttp\Client;
|
||||
use GuzzleHttp\Exception\ConnectException;
|
||||
use GuzzleHttp\Exception\GuzzleException;
|
||||
use GuzzleHttp\Exception\ServerException;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Psr\Http\Message\ResponseInterface;
|
||||
|
||||
abstract class SupportedApps
|
||||
{
|
||||
protected $jar = false;
|
||||
|
||||
protected $method = 'GET';
|
||||
|
||||
protected $error;
|
||||
|
||||
/**
|
||||
* @param $url
|
||||
* @param array $attrs
|
||||
* @return object
|
||||
* @throws GuzzleException
|
||||
*/
|
||||
public function appTest($url, array $attrs = []): object
|
||||
{
|
||||
if (empty($this->config->url)) {
|
||||
return (object) [
|
||||
'code' => 404,
|
||||
'status' => 'No URL has been specified',
|
||||
'response' => 'No URL has been specified',
|
||||
];
|
||||
}
|
||||
$res = $this->execute($url, $attrs);
|
||||
if ($res == null) {
|
||||
return (object) [
|
||||
'code' => null,
|
||||
'status' => $this->error,
|
||||
'response' => 'Connection failed',
|
||||
];
|
||||
}
|
||||
switch ($res->getStatusCode()) {
|
||||
case 200:
|
||||
$status = 'Successfully communicated with the API';
|
||||
break;
|
||||
case 401:
|
||||
$status = 'Failed: Invalid credentials';
|
||||
break;
|
||||
case 404:
|
||||
$status = 'Failed: Please make sure your URL is correct and that there is a trailing slash';
|
||||
break;
|
||||
default:
|
||||
$status = 'Something went wrong... Code: '.$res->getStatusCode();
|
||||
break;
|
||||
}
|
||||
|
||||
return (object) [
|
||||
'code' => $res->getStatusCode(),
|
||||
'status' => $status,
|
||||
'response' => $res->getBody(),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $url
|
||||
* @param array $attrs
|
||||
* @param array|bool|null $overridevars
|
||||
* @param string|bool|null $overridemethod
|
||||
* @return ResponseInterface|null
|
||||
* @throws GuzzleException
|
||||
*/
|
||||
public function execute(
|
||||
$url,
|
||||
array $attrs = [],
|
||||
$overridevars = null,
|
||||
$overridemethod = null
|
||||
): ?ResponseInterface {
|
||||
$res = null;
|
||||
|
||||
$vars = ($overridevars === null || $overridevars === false) ?
|
||||
[
|
||||
'http_errors' => false,
|
||||
'timeout' => 15,
|
||||
'connect_timeout' => 15,
|
||||
] : $overridevars;
|
||||
|
||||
$client = new Client($vars);
|
||||
|
||||
$method = ($overridemethod === null || $overridemethod === false) ? $this->method : $overridemethod;
|
||||
|
||||
|
||||
try {
|
||||
return $client->request($method, $url, $attrs);
|
||||
} catch (ConnectException $e) {
|
||||
Log::error('Connection refused');
|
||||
Log::debug($e->getMessage());
|
||||
$this->error = 'Connection refused - '.(string) $e->getMessage();
|
||||
} catch (ServerException $e) {
|
||||
Log::debug($e->getMessage());
|
||||
$this->error = (string) $e->getResponse()->getBody();
|
||||
}
|
||||
$this->error = 'General error connecting with API';
|
||||
|
||||
return $res;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function login()
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $url
|
||||
* @param bool $addslash
|
||||
* @return string
|
||||
*/
|
||||
public function normaliseurl(string $url, bool $addslash = true): string
|
||||
{
|
||||
$url = rtrim($url, '/');
|
||||
if ($addslash) {
|
||||
$url .= '/';
|
||||
}
|
||||
|
||||
return $url;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $status
|
||||
* @param $data
|
||||
* @return false|string
|
||||
*/
|
||||
public function getLiveStats($status, $data)
|
||||
{
|
||||
$className = $this::class;
|
||||
$explode = explode('\\', $className);
|
||||
$name = end($explode);
|
||||
|
||||
$html = view('SupportedApps::'.$name.'.livestats', $data)->with('data', $data)->render();
|
||||
|
||||
return json_encode(['status' => $status, 'html' => $html]);
|
||||
//return
|
||||
}
|
||||
|
||||
/**
|
||||
* @return ResponseInterface
|
||||
* @throws GuzzleException
|
||||
*/
|
||||
public static function getList(): ResponseInterface
|
||||
{
|
||||
// $list_url = 'https://apps.heimdall.site/list';
|
||||
$list_url = config('app.appsource').'list.json';
|
||||
$client = new Client(['http_errors' => false, 'verify' => false, 'timeout' => 15, 'connect_timeout' => 15]);
|
||||
|
||||
return $client->request('GET', $list_url);
|
||||
}
|
||||
|
||||
public static function configValue($item = null, $key = null)
|
||||
{
|
||||
if (isset($item) && ! empty($item)) {
|
||||
return $item->getconfig()->$key;
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $app
|
||||
* @return bool|false
|
||||
* @throws GuzzleException
|
||||
*/
|
||||
public static function getFiles($app): bool
|
||||
{
|
||||
Log::debug("Download triggered for ".print_r($app, true));
|
||||
|
||||
$zipurl = config('app.appsource').'files/'.$app->sha.'.zip';
|
||||
|
||||
$client = new Client(['http_errors' => false, 'timeout' => 60, 'connect_timeout' => 15, 'verify' => false]);
|
||||
$res = $client->request('GET', $zipurl);
|
||||
|
||||
// Something went wrong?
|
||||
if ($res->getStatusCode() !== 200) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (! file_exists(app_path('SupportedApps'))) {
|
||||
mkdir(app_path('SupportedApps'), 0777, true);
|
||||
}
|
||||
|
||||
$src = app_path('SupportedApps/'.className($app->name).'.zip');
|
||||
file_put_contents($src, $res->getBody());
|
||||
|
||||
$zip = new \ZipArchive();
|
||||
$x = $zip->open($src); // open the zip file to extract
|
||||
if ($x === true) {
|
||||
$zip->extractTo(app_path('SupportedApps')); // place in the directory with same name
|
||||
$zip->close();
|
||||
unlink($src); //Deleting the Zipped file
|
||||
} else {
|
||||
var_dump($x);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param $details
|
||||
* @param $app
|
||||
* @return mixed
|
||||
*/
|
||||
public static function saveApp($details, $app)
|
||||
{
|
||||
$app->appid = $details->appid;
|
||||
$app->name = $details->name;
|
||||
$app->sha = $details->sha ?? null;
|
||||
$app->icon = 'icons/'.$details->icon;
|
||||
$app->website = $details->website;
|
||||
$app->license = $details->license;
|
||||
|
||||
$appclass = $app->class();
|
||||
$application = new $appclass;
|
||||
$enhanced = (bool) ($application instanceof \App\EnhancedApps);
|
||||
$app->class = $appclass;
|
||||
$app->enhanced = $enhanced;
|
||||
$app->tile_background = $details->tile_background;
|
||||
$app->save();
|
||||
|
||||
return $app;
|
||||
}
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
<?php namespace App\SupportedApps\Contracts;
|
||||
|
||||
interface Applications {
|
||||
|
||||
public function defaultColour();
|
||||
|
||||
public function icon();
|
||||
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
<?php namespace App\SupportedApps\Contracts;
|
||||
|
||||
interface Livestats {
|
||||
|
||||
public function configDetails();
|
||||
|
||||
public function testConfig();
|
||||
|
||||
public function executeConfig();
|
||||
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
<?php namespace App\SupportedApps;
|
||||
|
||||
class Deluge implements Contracts\Applications {
|
||||
public function defaultColour()
|
||||
{
|
||||
return '#357';
|
||||
}
|
||||
public function icon()
|
||||
{
|
||||
return 'supportedapps/deluge.png';
|
||||
}
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
<?php namespace App\SupportedApps;
|
||||
|
||||
class Dokuwiki implements Contracts\Applications {
|
||||
public function defaultColour()
|
||||
{
|
||||
return '#9d7056';
|
||||
}
|
||||
public function icon()
|
||||
{
|
||||
return 'supportedapps/dokuwiki.png';
|
||||
}
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
<?php namespace App\SupportedApps;
|
||||
|
||||
class Duplicati implements Contracts\Applications {
|
||||
public function defaultColour()
|
||||
{
|
||||
return '#2c3744';
|
||||
}
|
||||
public function icon()
|
||||
{
|
||||
return 'supportedapps/duplicati.png';
|
||||
}
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
<?php namespace App\SupportedApps;
|
||||
|
||||
class Emby implements Contracts\Applications {
|
||||
public function defaultColour()
|
||||
{
|
||||
return '#222';
|
||||
}
|
||||
public function icon()
|
||||
{
|
||||
return 'supportedapps/emby.png';
|
||||
}
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
<?php namespace App\SupportedApps;
|
||||
|
||||
class Gitea implements Contracts\Applications {
|
||||
public function defaultColour()
|
||||
{
|
||||
return '#585e52';
|
||||
}
|
||||
public function icon()
|
||||
{
|
||||
return 'supportedapps/gitea.png';
|
||||
}
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
<?php namespace App\SupportedApps;
|
||||
|
||||
class Graylog implements Contracts\Applications {
|
||||
public function defaultColour()
|
||||
{
|
||||
return '#158';
|
||||
}
|
||||
public function icon()
|
||||
{
|
||||
return 'supportedapps/graylog.png';
|
||||
}
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
<?php namespace App\SupportedApps;
|
||||
|
||||
class HomeAssistant implements Contracts\Applications {
|
||||
public function defaultColour()
|
||||
{
|
||||
return '#073c52';
|
||||
}
|
||||
public function icon()
|
||||
{
|
||||
return 'supportedapps/homeassistant.png';
|
||||
}
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
<?php namespace App\SupportedApps;
|
||||
|
||||
class Jackett implements Contracts\Applications {
|
||||
public function defaultColour()
|
||||
{
|
||||
return '#484814';
|
||||
}
|
||||
public function icon()
|
||||
{
|
||||
return 'supportedapps/jackett.png';
|
||||
}
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
<?php namespace App\SupportedApps;
|
||||
|
||||
class Jdownloader implements Contracts\Applications {
|
||||
public function defaultColour()
|
||||
{
|
||||
return '#2b494f';
|
||||
}
|
||||
public function icon()
|
||||
{
|
||||
return 'supportedapps/jdownloader.png';
|
||||
}
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
<?php namespace App\SupportedApps;
|
||||
|
||||
class Lidarr implements Contracts\Applications {
|
||||
public function defaultColour()
|
||||
{
|
||||
return '#183c18';
|
||||
}
|
||||
public function icon()
|
||||
{
|
||||
return 'supportedapps/lidarr.png';
|
||||
}
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
<?php namespace App\SupportedApps;
|
||||
|
||||
class Mcmyadmin implements Contracts\Applications {
|
||||
public function defaultColour()
|
||||
{
|
||||
return '#30404b';
|
||||
}
|
||||
public function icon()
|
||||
{
|
||||
return 'supportedapps/mcmyadmin.png';
|
||||
}
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
<?php namespace App\SupportedApps;
|
||||
|
||||
class Medusa implements Contracts\Applications {
|
||||
public function defaultColour()
|
||||
{
|
||||
return '#4b5e55';
|
||||
}
|
||||
public function icon()
|
||||
{
|
||||
return 'supportedapps/medusa.png';
|
||||
}
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
<?php namespace App\SupportedApps;
|
||||
|
||||
class Netdata implements Contracts\Applications {
|
||||
public function defaultColour()
|
||||
{
|
||||
return '#543737';
|
||||
}
|
||||
public function icon()
|
||||
{
|
||||
return 'supportedapps/netdata.png';
|
||||
}
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
<?php namespace App\SupportedApps;
|
||||
|
||||
class Nextcloud implements Contracts\Applications {
|
||||
public function defaultColour()
|
||||
{
|
||||
return '#0e2c3e';
|
||||
}
|
||||
public function icon()
|
||||
{
|
||||
return 'supportedapps/nextcloud.png';
|
||||
}
|
||||
}
|
||||
@@ -1,82 +0,0 @@
|
||||
<?php namespace App\SupportedApps;
|
||||
|
||||
use GuzzleHttp\Exception\GuzzleException;
|
||||
use GuzzleHttp\Client;
|
||||
|
||||
class Nzbget implements Contracts\Applications, Contracts\Livestats {
|
||||
|
||||
public $config;
|
||||
|
||||
public function defaultColour()
|
||||
{
|
||||
return '#253827';
|
||||
}
|
||||
public function icon()
|
||||
{
|
||||
return 'supportedapps/nzbget.png';
|
||||
}
|
||||
public function configDetails()
|
||||
{
|
||||
return 'nzbget';
|
||||
}
|
||||
public function testConfig()
|
||||
{
|
||||
$res = $this->buildRequest('status');
|
||||
switch($res->getStatusCode()) {
|
||||
case 200:
|
||||
echo 'Successfully connected to the API';
|
||||
break;
|
||||
case 401:
|
||||
echo 'Failed: Invalid credentials';
|
||||
break;
|
||||
case 404:
|
||||
echo 'Failed: Please make sure your URL is correct and that there is a trailing slash';
|
||||
break;
|
||||
default:
|
||||
echo 'Something went wrong... Code: '.$res->getStatusCode();
|
||||
break;
|
||||
}
|
||||
}
|
||||
public function executeConfig()
|
||||
{
|
||||
$output = '';
|
||||
$res = $this->buildRequest('status');
|
||||
$data = json_decode($res->getBody());
|
||||
//$data->result->RemainingSizeMB = '10000000';
|
||||
//$data->result->DownloadRate = '100000000';
|
||||
$size = $data->result->RemainingSizeMB;
|
||||
$rate = $data->result->DownloadRate;
|
||||
$queue_size = format_bytes($size*1000*1000, false, ' <span>', '</span>');
|
||||
$current_speed = format_bytes($rate, false, ' <span>');
|
||||
|
||||
if($size > 0 || $rate > 0) {
|
||||
$output = '
|
||||
<ul class="livestats">
|
||||
<li><span class="title">Queue</span><strong>'.$queue_size.'</strong></li>
|
||||
<li><span class="title">Speed</span><strong>'.$current_speed.'/s</span></strong></li>
|
||||
</ul>
|
||||
';
|
||||
}
|
||||
return $output;
|
||||
}
|
||||
public function buildRequest($endpoint)
|
||||
{
|
||||
$config = $this->config;
|
||||
$url = $config->url;
|
||||
$username = $config->username;
|
||||
$password = $config->password;
|
||||
|
||||
$rebuild_url = str_replace('http://', 'http://'.$username.':'.$password.'@', $url);
|
||||
$rebuild_url = str_replace('https://', 'https://'.$username.':'.$password.'@', $rebuild_url);
|
||||
$rebuild_url = rtrim($rebuild_url, '/');
|
||||
|
||||
|
||||
$api_url = $rebuild_url.'/jsonrpc/'.$endpoint;
|
||||
|
||||
$client = new Client(['http_errors' => false]);
|
||||
$res = $client->request('GET', $api_url);
|
||||
return $res;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
<?php namespace App\SupportedApps;
|
||||
|
||||
class Nzbhydra implements Contracts\Applications {
|
||||
public function defaultColour()
|
||||
{
|
||||
return '#53644d';
|
||||
}
|
||||
public function icon()
|
||||
{
|
||||
return 'supportedapps/nzbhydra.png';
|
||||
}
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
<?php namespace App\SupportedApps;
|
||||
|
||||
class Ombi implements Contracts\Applications {
|
||||
public function defaultColour()
|
||||
{
|
||||
return '#150f09';
|
||||
}
|
||||
public function icon()
|
||||
{
|
||||
return 'supportedapps/ombi.png';
|
||||
}
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
<?php namespace App\SupportedApps;
|
||||
|
||||
class Openhab implements Contracts\Applications {
|
||||
public function defaultColour()
|
||||
{
|
||||
return '#2b2525';
|
||||
}
|
||||
public function icon()
|
||||
{
|
||||
return 'supportedapps/openhab.png';
|
||||
}
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
<?php namespace App\SupportedApps;
|
||||
|
||||
class Opnsense implements Contracts\Applications {
|
||||
public function defaultColour()
|
||||
{
|
||||
return '#211914';
|
||||
}
|
||||
public function icon()
|
||||
{
|
||||
return 'supportedapps/opnsense.png';
|
||||
}
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
<?php namespace App\SupportedApps;
|
||||
|
||||
class Pfsense implements Contracts\Applications {
|
||||
public function defaultColour()
|
||||
{
|
||||
return '#4e4742';
|
||||
}
|
||||
public function icon()
|
||||
{
|
||||
return 'supportedapps/pfsense.png';
|
||||
}
|
||||
}
|
||||
@@ -1,72 +0,0 @@
|
||||
<?php namespace App\SupportedApps;
|
||||
|
||||
use GuzzleHttp\Exception\GuzzleException;
|
||||
use GuzzleHttp\Client;
|
||||
|
||||
class Pihole implements Contracts\Applications, Contracts\Livestats {
|
||||
public function defaultColour()
|
||||
{
|
||||
return '#352222';
|
||||
}
|
||||
public function icon()
|
||||
{
|
||||
return 'supportedapps/pihole.png';
|
||||
}
|
||||
|
||||
public function configDetails()
|
||||
{
|
||||
return 'pihole';
|
||||
}
|
||||
|
||||
public function testConfig()
|
||||
{
|
||||
$res = $this->buildRequest();
|
||||
switch($res->getStatusCode()) {
|
||||
case 200:
|
||||
echo 'Successfully connected to the API';
|
||||
break;
|
||||
case 401:
|
||||
echo 'Failed: Invalid credentials';
|
||||
break;
|
||||
case 404:
|
||||
echo 'Failed: Please make sure your URL is correct and that there is a trailing slash';
|
||||
break;
|
||||
default:
|
||||
echo 'Something went wrong... Code: '.$res->getStatusCode();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public function executeConfig()
|
||||
{
|
||||
$output = '';
|
||||
$res = $this->buildRequest();
|
||||
$data = json_decode($res->getBody());
|
||||
|
||||
$output = '
|
||||
<ul class="livestats">
|
||||
<li><span class="title">Domains<br />Blocked</span><strong>'.$data->domains_being_blocked.'</strong></li>
|
||||
<li><span class="title">Blocked<br />Today</span><strong>'.$data->ads_blocked_today.'</span></strong></li>
|
||||
</ul>
|
||||
';
|
||||
return $output;
|
||||
}
|
||||
|
||||
public function buildRequest()
|
||||
{
|
||||
$config = $this->config;
|
||||
$url = $config->url;
|
||||
|
||||
$url = rtrim($url, '/');
|
||||
|
||||
$api_url = $url.'/api.php';
|
||||
//die( $api_url.' --- ');
|
||||
|
||||
$client = new Client(['http_errors' => false]);
|
||||
$res = $client->request('GET', $api_url);
|
||||
return $res;
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
<?php namespace App\SupportedApps;
|
||||
|
||||
class Plex implements Contracts\Applications {
|
||||
public function defaultColour()
|
||||
{
|
||||
return '#222';
|
||||
}
|
||||
public function icon()
|
||||
{
|
||||
return 'supportedapps/plex.png';
|
||||
}
|
||||
}
|
||||
@@ -1,76 +0,0 @@
|
||||
<?php namespace App\SupportedApps;
|
||||
|
||||
use GuzzleHttp\Exception\GuzzleException;
|
||||
use GuzzleHttp\Client;
|
||||
|
||||
class Plexpy implements Contracts\Applications, Contracts\Livestats {
|
||||
|
||||
public $config;
|
||||
|
||||
public function defaultColour()
|
||||
{
|
||||
return '#2d2208';
|
||||
}
|
||||
public function icon()
|
||||
{
|
||||
return 'supportedapps/plexpy.png';
|
||||
}
|
||||
public function configDetails()
|
||||
{
|
||||
return 'plexpy';
|
||||
}
|
||||
public function testConfig()
|
||||
{
|
||||
$res = $this->buildRequest('arnold');
|
||||
switch($res->getStatusCode()) {
|
||||
case 200:
|
||||
$data = json_decode($res->getBody());
|
||||
if(isset($data->error) && !empty($data->error)) {
|
||||
echo 'Failed: '.$data->error;
|
||||
} else {
|
||||
echo 'Successfully connected to the API';
|
||||
}
|
||||
break;
|
||||
case 401:
|
||||
echo 'Failed: Invalid credentials';
|
||||
break;
|
||||
case 404:
|
||||
echo 'Failed: Please make sure your URL is correct and that there is a trailing slash';
|
||||
break;
|
||||
default:
|
||||
echo 'Something went wrong... Code: '.$res->getStatusCode();
|
||||
break;
|
||||
}
|
||||
}
|
||||
public function executeConfig()
|
||||
{
|
||||
$output = '';
|
||||
$res = $this->buildRequest('get_activity');
|
||||
$data = json_decode($res->getBody());
|
||||
$stream_count = $data->response->data->stream_count;
|
||||
|
||||
$output = '
|
||||
<ul class="livestats">
|
||||
<li><span class="title">Stream Count</span><strong>'.$stream_count.'</strong></li>
|
||||
</ul>
|
||||
';
|
||||
|
||||
return $output;
|
||||
}
|
||||
public function buildRequest($endpoint)
|
||||
{
|
||||
$config = $this->config;
|
||||
$url = $config->url;
|
||||
$apikey = $config->apikey;
|
||||
|
||||
$url = rtrim($url, '/');
|
||||
|
||||
$api_url = $url.'/api/v2?apikey='.$apikey.'&cmd='.$endpoint;
|
||||
|
||||
$client = new Client(['http_errors' => false]);
|
||||
$res = $client->request('GET', $api_url);
|
||||
return $res;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
<?php namespace App\SupportedApps;
|
||||
|
||||
class Plexrequests implements Contracts\Applications {
|
||||
public function defaultColour()
|
||||
{
|
||||
return '#3c2d1c';
|
||||
}
|
||||
public function icon()
|
||||
{
|
||||
return 'supportedapps/plexrequests.png';
|
||||
}
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
<?php namespace App\SupportedApps;
|
||||
|
||||
class Portainer implements Contracts\Applications {
|
||||
public function defaultColour()
|
||||
{
|
||||
return '#283f44';
|
||||
}
|
||||
public function icon()
|
||||
{
|
||||
return 'supportedapps/portainer.png';
|
||||
}
|
||||
}
|
||||
@@ -1,80 +0,0 @@
|
||||
<?php namespace App\SupportedApps;
|
||||
|
||||
use GuzzleHttp\Exception\GuzzleException;
|
||||
use GuzzleHttp\Client;
|
||||
|
||||
class Proxmox implements Contracts\Applications, Contracts\Livestats {
|
||||
public function defaultColour()
|
||||
{
|
||||
return '#542e0a';
|
||||
}
|
||||
public function icon()
|
||||
{
|
||||
return 'supportedapps/proxmox.png';
|
||||
}
|
||||
|
||||
public function configDetails()
|
||||
{
|
||||
//return 'proxmox';
|
||||
return null;
|
||||
}
|
||||
|
||||
public function testConfig()
|
||||
{
|
||||
/*$res = $this->buildRequest();
|
||||
switch($res->getStatusCode()) {
|
||||
case 200:
|
||||
echo 'Successfully connected to the API';
|
||||
break;
|
||||
case 401:
|
||||
echo 'Failed: Invalid credentials';
|
||||
break;
|
||||
case 404:
|
||||
echo 'Failed: Please make sure your URL is correct and that there is a trailing slash';
|
||||
break;
|
||||
default:
|
||||
echo 'Something went wrong... Code: '.$res->getStatusCode();
|
||||
break;
|
||||
}*/
|
||||
return null;
|
||||
}
|
||||
|
||||
public function executeConfig()
|
||||
{
|
||||
/*
|
||||
$output = '';
|
||||
$res = $this->buildRequest();
|
||||
$data = json_decode($res->getBody());
|
||||
|
||||
$output = '
|
||||
<ul class="livestats">
|
||||
<li><span class="title">Domains<br />Blocked</span><strong>'.$data->domains_being_blocked.'</strong></li>
|
||||
<li><span class="title">Blocked<br />Today</span><strong>'.$data->ads_blocked_today.'</span></strong></li>
|
||||
</ul>
|
||||
';
|
||||
return $output;
|
||||
*/
|
||||
return null;
|
||||
}
|
||||
|
||||
public function buildRequest($endpoint='')
|
||||
{
|
||||
$config = $this->config;
|
||||
|
||||
$username = $config->username;
|
||||
$password = $config->password;
|
||||
|
||||
$url = $config->url;
|
||||
$url = rtrim($url, '/');
|
||||
|
||||
$api_url = $url.'/api2/json/'.$endpoint.'?username='.$username.'&password='.$password;
|
||||
//die( $api_url.' --- ');
|
||||
|
||||
$client = new Client(['http_errors' => false, 'verify' => false ]);
|
||||
$res = $client->request('GET', $api_url);
|
||||
return $res;
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
<?php namespace App\SupportedApps;
|
||||
|
||||
class Radarr implements Contracts\Applications {
|
||||
public function defaultColour()
|
||||
{
|
||||
return '#3e3726';
|
||||
}
|
||||
public function icon()
|
||||
{
|
||||
return 'supportedapps/radarr.png';
|
||||
}
|
||||
}
|
||||
@@ -1,94 +0,0 @@
|
||||
<?php namespace App\SupportedApps;
|
||||
|
||||
use GuzzleHttp\Exception\GuzzleException;
|
||||
use GuzzleHttp\Client;
|
||||
|
||||
class Runeaudio implements Contracts\Applications, Contracts\Livestats {
|
||||
public function defaultColour()
|
||||
{
|
||||
return '#05A';
|
||||
}
|
||||
public function icon()
|
||||
{
|
||||
return 'supportedapps/runeaudio.png';
|
||||
}
|
||||
|
||||
public function configDetails()
|
||||
{
|
||||
return 'runeaudio';
|
||||
}
|
||||
|
||||
public function testConfig()
|
||||
{
|
||||
$res = $this->buildRequest('status');
|
||||
switch($res->getStatusCode()) {
|
||||
case 200:
|
||||
echo 'Successfully connected to the API';
|
||||
break;
|
||||
case 401:
|
||||
echo 'Failed: Invalid credentials';
|
||||
break;
|
||||
case 404:
|
||||
echo 'Failed: Please make sure your URL is correct and that there is a trailing slash';
|
||||
break;
|
||||
default:
|
||||
echo 'Something went wrong... Code: '.$res->getStatusCode();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
public function executeConfig()
|
||||
{
|
||||
$output = '';
|
||||
$artist = '';
|
||||
$song_title = '';
|
||||
$res = $this->buildRequest('currentsong');
|
||||
$array = explode("\n", $res->getBody());
|
||||
foreach($array as $item) {
|
||||
$item_array = explode(": ", $item);
|
||||
if ($item_array[0] == 'Artist') {
|
||||
$artist = $item_array[1];
|
||||
} elseif ($item_array[0] == 'Title') {
|
||||
$song_title = $item_array[1];
|
||||
}
|
||||
}
|
||||
|
||||
$output = '<ul class="livestats">';
|
||||
|
||||
if (strlen($artist) > 12) {
|
||||
$output = $output.'<li><span class="title-marquee"><span>'.$artist.'</span></span></li>';
|
||||
} else {
|
||||
$output = $output.'<li><span class="title">'.$artist.'</span></li>';
|
||||
}
|
||||
|
||||
$output = $output.'</ul><ul class="livestats">';
|
||||
|
||||
if (strlen($song_title) > 12) {
|
||||
$output = $output.'<li><span class="title-marquee"><span>'.$song_title.'</span></span></li>';
|
||||
} else {
|
||||
$output = $output.'<li><span class="title">'.$song_title.'</span></li>';
|
||||
}
|
||||
|
||||
$output = $output.'</ul>';
|
||||
|
||||
return $output;
|
||||
}
|
||||
|
||||
public function buildRequest($endpoint)
|
||||
{
|
||||
$config = $this->config;
|
||||
$url = $config->url;
|
||||
|
||||
$url = rtrim($url, '/');
|
||||
|
||||
$api_url = $url.'/command/?cmd='.$endpoint;
|
||||
//die( $api_url.' --- ');
|
||||
|
||||
$client = new Client(['http_errors' => false]);
|
||||
$res = $client->request('GET', $api_url);
|
||||
return $res;
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@@ -1,84 +0,0 @@
|
||||
<?php namespace App\SupportedApps;
|
||||
|
||||
use GuzzleHttp\Exception\GuzzleException;
|
||||
use GuzzleHttp\Client;
|
||||
|
||||
class Sabnzbd implements Contracts\Applications, Contracts\Livestats {
|
||||
|
||||
public $config;
|
||||
|
||||
public function defaultColour()
|
||||
{
|
||||
return '#3e3924';
|
||||
}
|
||||
public function icon()
|
||||
{
|
||||
return 'supportedapps/sabnzbd.png';
|
||||
}
|
||||
public function configDetails()
|
||||
{
|
||||
return 'sabnzbd';
|
||||
}
|
||||
public function testConfig()
|
||||
{
|
||||
$res = $this->buildRequest('queue');
|
||||
switch($res->getStatusCode()) {
|
||||
case 200:
|
||||
$data = json_decode($res->getBody());
|
||||
if(isset($data->error) && !empty($data->error)) {
|
||||
echo 'Failed: '.$data->error;
|
||||
} else {
|
||||
echo 'Successfully connected to the API';
|
||||
}
|
||||
break;
|
||||
case 401:
|
||||
echo 'Failed: Invalid credentials';
|
||||
break;
|
||||
case 404:
|
||||
echo 'Failed: Please make sure your URL is correct and that there is a trailing slash';
|
||||
break;
|
||||
default:
|
||||
echo 'Something went wrong... Code: '.$res->getStatusCode();
|
||||
break;
|
||||
}
|
||||
}
|
||||
public function executeConfig()
|
||||
{
|
||||
$output = '';
|
||||
$res = $this->buildRequest('queue');
|
||||
$data = json_decode($res->getBody());
|
||||
//$data->result->RemainingSizeMB = '10000000';
|
||||
//$data->result->DownloadRate = '100000000';
|
||||
$size = $data->queue->mbleft;
|
||||
$rate = $data->queue->kbpersec;
|
||||
$queue_size = format_bytes($size*1000*1000, false, ' <span>', '</span>');
|
||||
$current_speed = format_bytes($rate*1000, false, ' <span>');
|
||||
|
||||
if($size > 0 || $rate > 0) {
|
||||
$output = '
|
||||
<ul class="livestats">
|
||||
<li><span class="title">Queue</span><strong>'.$queue_size.'</strong></li>
|
||||
<li><span class="title">Speed</span><strong>'.$current_speed.'/s</span></strong></li>
|
||||
</ul>
|
||||
';
|
||||
}
|
||||
return $output;
|
||||
}
|
||||
public function buildRequest($endpoint)
|
||||
{
|
||||
$config = $this->config;
|
||||
$url = $config->url;
|
||||
$apikey = $config->apikey;
|
||||
|
||||
$url = rtrim($url, '/');
|
||||
|
||||
$api_url = $url.'/api?output=json&apikey='.$apikey.'&mode='.$endpoint;
|
||||
//die( $api_url.' --- ');
|
||||
|
||||
$client = new Client(['http_errors' => false]);
|
||||
$res = $client->request('GET', $api_url);
|
||||
return $res;
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
<?php namespace App\SupportedApps;
|
||||
|
||||
class Sickrage implements Contracts\Applications {
|
||||
public function defaultColour()
|
||||
{
|
||||
return '#6185a6';
|
||||
}
|
||||
public function icon()
|
||||
{
|
||||
return 'supportedapps/sickrage.png';
|
||||
}
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
<?php namespace App\SupportedApps;
|
||||
|
||||
class Sonarr implements Contracts\Applications {
|
||||
public function defaultColour()
|
||||
{
|
||||
return '#163740';
|
||||
}
|
||||
public function icon()
|
||||
{
|
||||
return 'supportedapps/sonarr.png';
|
||||
}
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
<?php namespace App\SupportedApps;
|
||||
|
||||
class Traefik implements Contracts\Applications {
|
||||
public function defaultColour()
|
||||
{
|
||||
return '#28434a';
|
||||
}
|
||||
public function icon()
|
||||
{
|
||||
return 'supportedapps/traefik.png';
|
||||
}
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
<?php namespace App\SupportedApps;
|
||||
|
||||
class Ttrss implements Contracts\Applications {
|
||||
public function defaultColour()
|
||||
{
|
||||
return '#9d704c';
|
||||
}
|
||||
public function icon()
|
||||
{
|
||||
return 'supportedapps/tt-rss.png';
|
||||
}
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
<?php namespace App\SupportedApps;
|
||||
|
||||
class Unifi implements Contracts\Applications {
|
||||
public function defaultColour()
|
||||
{
|
||||
return '#363840';
|
||||
}
|
||||
public function icon()
|
||||
{
|
||||
return 'supportedapps/unifi.png';
|
||||
}
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
<?php namespace App\SupportedApps;
|
||||
|
||||
class ruTorrent implements Contracts\Applications {
|
||||
public function defaultColour()
|
||||
{
|
||||
return '#004';
|
||||
}
|
||||
public function icon()
|
||||
{
|
||||
return 'supportedapps/rutorrent.png';
|
||||
}
|
||||
}
|
||||
85
app/User.php
85
app/User.php
@@ -2,20 +2,61 @@
|
||||
|
||||
namespace App;
|
||||
|
||||
use Illuminate\Notifications\Notifiable;
|
||||
use Illuminate\Database\Eloquent\Factories\HasFactory;
|
||||
use Illuminate\Database\Eloquent\Relations\BelongsToMany;
|
||||
use Illuminate\Database\Eloquent\Relations\HasMany;
|
||||
use Illuminate\Foundation\Auth\User as Authenticatable;
|
||||
use Illuminate\Notifications\Notifiable;
|
||||
|
||||
// @codingStandardsIgnoreStart
|
||||
/**
|
||||
* App\User
|
||||
*
|
||||
* @property int $id
|
||||
* @property string $username
|
||||
* @property string $email
|
||||
* @property string|null $avatar
|
||||
* @property string|null $password
|
||||
* @property string|null $autologin
|
||||
* @property int $public_front
|
||||
* @property string|null $remember_token
|
||||
* @property \Illuminate\Support\Carbon|null $created_at
|
||||
* @property \Illuminate\Support\Carbon|null $updated_at
|
||||
* @property-read \Illuminate\Database\Eloquent\Collection|\App\Item[] $items
|
||||
* @property-read int|null $items_count
|
||||
* @property-read \Illuminate\Notifications\DatabaseNotificationCollection|\Illuminate\Notifications\DatabaseNotification[] $notifications
|
||||
* @property-read int|null $notifications_count
|
||||
* @property-read \Illuminate\Database\Eloquent\Collection|\App\Setting[] $settings
|
||||
* @property-read int|null $settings_count
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|User newModelQuery()
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|User newQuery()
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|User query()
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|User whereAutologin($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|User whereAvatar($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|User whereCreatedAt($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|User whereEmail($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|User whereId($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|User wherePassword($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|User wherePublicFront($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|User whereRememberToken($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|User whereUpdatedAt($value)
|
||||
* @method static \Illuminate\Database\Eloquent\Builder|User whereUsername($value)
|
||||
* @mixin \Eloquent
|
||||
*/
|
||||
// @codingStandardsIgnoreEnd
|
||||
class User extends Authenticatable
|
||||
{
|
||||
use Notifiable;
|
||||
|
||||
use HasFactory;
|
||||
|
||||
/**
|
||||
* The attributes that are mass assignable.
|
||||
*
|
||||
* @var array
|
||||
*/
|
||||
protected $fillable = [
|
||||
'name', 'email', 'password',
|
||||
'username', 'email', 'password',
|
||||
];
|
||||
|
||||
/**
|
||||
@@ -26,4 +67,44 @@ class User extends Authenticatable
|
||||
protected $hidden = [
|
||||
'password', 'remember_token',
|
||||
];
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
public function getId()
|
||||
{
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the items for the user.
|
||||
*/
|
||||
public function items(): HasMany
|
||||
{
|
||||
return $this->hasMany(Item::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* The settings that belong to the user.
|
||||
*/
|
||||
public function settings(): BelongsToMany
|
||||
{
|
||||
return $this->belongsToMany(Setting::class)->withPivot('uservalue');
|
||||
}
|
||||
|
||||
public static function currentUser()
|
||||
{
|
||||
$current_user = session('current_user');
|
||||
if ($current_user) { // if logged in, set this user
|
||||
return $current_user;
|
||||
} else { // not logged in, get first user
|
||||
$user = self::where('public_front', true)->first();
|
||||
if (! $user) {
|
||||
$user = self::first();
|
||||
}
|
||||
session(['current_user' => $user]);
|
||||
|
||||
return $user;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
50
artisan
50
artisan
@@ -1,53 +1,15 @@
|
||||
#!/usr/bin/env php
|
||||
<?php
|
||||
|
||||
use Symfony\Component\Console\Input\ArgvInput;
|
||||
|
||||
define('LARAVEL_START', microtime(true));
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Register The Auto Loader
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Composer provides a convenient, automatically generated class loader
|
||||
| for our application. We just need to utilize it! We'll require it
|
||||
| into the script here so that we do not have to worry about the
|
||||
| loading of any our classes "manually". Feels great to relax.
|
||||
|
|
||||
*/
|
||||
|
||||
// Register the Composer autoloader...
|
||||
require __DIR__.'/vendor/autoload.php';
|
||||
|
||||
$app = require_once __DIR__.'/bootstrap/app.php';
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Run The Artisan Application
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| When we run the console application, the current CLI command will be
|
||||
| executed in this console and the response sent back to a terminal
|
||||
| or another output device for the developers. Here goes nothing!
|
||||
|
|
||||
*/
|
||||
|
||||
$kernel = $app->make(Illuminate\Contracts\Console\Kernel::class);
|
||||
|
||||
$status = $kernel->handle(
|
||||
$input = new Symfony\Component\Console\Input\ArgvInput,
|
||||
new Symfony\Component\Console\Output\ConsoleOutput
|
||||
);
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Shutdown The Application
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Once Artisan has finished running, we will fire off the shutdown events
|
||||
| so that any final work may be done by the application before we shut
|
||||
| down the process. This is the last thing to happen to the request.
|
||||
|
|
||||
*/
|
||||
|
||||
$kernel->terminate($input, $status);
|
||||
// Bootstrap Laravel and handle the command...
|
||||
$status = (require_once __DIR__.'/bootstrap/app.php')
|
||||
->handleCommand(new ArgvInput);
|
||||
|
||||
exit($status);
|
||||
|
||||
@@ -1,55 +1,43 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Create The Application
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| The first thing we will do is create a new Laravel application instance
|
||||
| which serves as the "glue" for all the components of Laravel, and is
|
||||
| the IoC container for the system binding all of the various parts.
|
||||
|
|
||||
*/
|
||||
use Illuminate\Foundation\Application;
|
||||
use Illuminate\Foundation\Configuration\Exceptions;
|
||||
use Illuminate\Foundation\Configuration\Middleware;
|
||||
|
||||
$app = new Illuminate\Foundation\Application(
|
||||
realpath(__DIR__.'/../')
|
||||
);
|
||||
return Application::configure(basePath: dirname(__DIR__))
|
||||
->withProviders([
|
||||
\Spatie\Html\HtmlServiceProvider::class,
|
||||
])
|
||||
->withRouting(
|
||||
web: __DIR__.'/../routes/web.php',
|
||||
api: __DIR__.'/../routes/api.php',
|
||||
commands: __DIR__.'/../routes/console.php',
|
||||
channels: __DIR__.'/../routes/channels.php',
|
||||
health: '/up',
|
||||
)
|
||||
->withMiddleware(function (Middleware $middleware) {
|
||||
$middleware->redirectGuestsTo(fn () => route('login'));
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Bind Important Interfaces
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Next, we need to bind some important interfaces into the container so
|
||||
| we will be able to resolve them when needed. The kernels serve the
|
||||
| incoming requests to this application from both the web and CLI.
|
||||
|
|
||||
*/
|
||||
$middleware->validateCsrfTokens(except: [
|
||||
//
|
||||
'order',
|
||||
'appload',
|
||||
'test_config',
|
||||
//'get_stats'
|
||||
]);
|
||||
|
||||
$app->singleton(
|
||||
Illuminate\Contracts\Http\Kernel::class,
|
||||
App\Http\Kernel::class
|
||||
);
|
||||
$middleware->append(\Illuminate\Foundation\Http\Middleware\CheckForMaintenanceMode::class);
|
||||
|
||||
$app->singleton(
|
||||
Illuminate\Contracts\Console\Kernel::class,
|
||||
App\Console\Kernel::class
|
||||
);
|
||||
$middleware->throttleApi('60,1');
|
||||
|
||||
$app->singleton(
|
||||
Illuminate\Contracts\Debug\ExceptionHandler::class,
|
||||
App\Exceptions\Handler::class
|
||||
);
|
||||
$middleware->replace(\Illuminate\Http\Middleware\TrustProxies::class, \App\Http\Middleware\TrustProxies::class);
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Return The Application
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| This script returns the application instance. The instance is given to
|
||||
| the calling script so we can separate the building of the instances
|
||||
| from the actual running of the application and sending responses.
|
||||
|
|
||||
*/
|
||||
|
||||
return $app;
|
||||
$middleware->alias([
|
||||
'allowed' => \App\Http\Middleware\CheckAllowed::class,
|
||||
'bindings' => \Illuminate\Routing\Middleware\SubstituteBindings::class,
|
||||
'guest' => \App\Http\Middleware\RedirectIfAuthenticated::class,
|
||||
]);
|
||||
})
|
||||
->withExceptions(function (Exceptions $exceptions) {
|
||||
//
|
||||
})->create();
|
||||
|
||||
7
bootstrap/providers.php
Normal file
7
bootstrap/providers.php
Normal file
@@ -0,0 +1,7 @@
|
||||
<?php
|
||||
|
||||
return [
|
||||
App\Providers\AppServiceProvider::class,
|
||||
App\Providers\FormMacroServiceProvider::class,
|
||||
App\Providers\RouteServiceProvider::class,
|
||||
];
|
||||
@@ -1,34 +1,49 @@
|
||||
{
|
||||
"name": "laravel/laravel",
|
||||
"description": "The Laravel Framework.",
|
||||
"keywords": ["framework", "laravel"],
|
||||
"keywords": [
|
||||
"framework",
|
||||
"laravel"
|
||||
],
|
||||
"license": "MIT",
|
||||
"type": "project",
|
||||
"require": {
|
||||
"php": ">=7.0.0",
|
||||
"fideloper/proxy": "~3.3",
|
||||
"guzzlehttp/guzzle": "^6.3",
|
||||
"laravel/framework": "5.5.*",
|
||||
"laravel/tinker": "~1.0",
|
||||
"laravelcollective/html": "^5.5"
|
||||
"php": "^8.2",
|
||||
"ext-intl": "*",
|
||||
"ext-json": "*",
|
||||
"enshrined/svg-sanitize": "^0.21.0",
|
||||
"graham-campbell/github": "^12.5",
|
||||
"guzzlehttp/guzzle": "^7.8",
|
||||
"laravel/framework": "^11.45",
|
||||
"laravel/tinker": "^2.9",
|
||||
"laravel/ui": "^4.4",
|
||||
"league/flysystem-aws-s3-v3": "^3.0",
|
||||
"nunomaduro/collision": "^8.0",
|
||||
"spatie/laravel-html": "^3.11",
|
||||
"spatie/laravel-ignition": "^2.4",
|
||||
"symfony/yaml": "^7.0"
|
||||
},
|
||||
"require-dev": {
|
||||
"filp/whoops": "~2.0",
|
||||
"fzaninotto/faker": "~1.4",
|
||||
"mockery/mockery": "~1.0",
|
||||
"phpunit/phpunit": "~6.0",
|
||||
"symfony/thanks": "^1.0"
|
||||
"barryvdh/laravel-ide-helper": "^3.0",
|
||||
"filp/whoops": "^2.8",
|
||||
"mockery/mockery": "^1.6",
|
||||
"phpunit/phpunit": "^10.5",
|
||||
"squizlabs/php_codesniffer": "3.*",
|
||||
"symfony/thanks": "^1.2",
|
||||
"fakerphp/faker": "^1.23"
|
||||
},
|
||||
"autoload": {
|
||||
"classmap": [
|
||||
"database/seeds",
|
||||
"database/seeders",
|
||||
"database/factories"
|
||||
],
|
||||
"files": [
|
||||
"app/Helper.php"
|
||||
],
|
||||
"psr-4": {
|
||||
"App\\": "app/"
|
||||
"App\\": "app/",
|
||||
"Database\\Factories\\": "database/factories/",
|
||||
"Database\\Seeders\\": "database/seeders/"
|
||||
}
|
||||
},
|
||||
"autoload-dev": {
|
||||
@@ -39,6 +54,7 @@
|
||||
"extra": {
|
||||
"laravel": {
|
||||
"dont-discover": [
|
||||
"barryvdh/laravel-ide-helper"
|
||||
]
|
||||
}
|
||||
},
|
||||
@@ -52,11 +68,24 @@
|
||||
"post-autoload-dump": [
|
||||
"Illuminate\\Foundation\\ComposerScripts::postAutoloadDump",
|
||||
"@php artisan package:discover"
|
||||
],
|
||||
"post-update-cmd": [
|
||||
"Illuminate\\Foundation\\ComposerScripts::postUpdate",
|
||||
"@php artisan ide-helper:generate",
|
||||
"@php artisan ide-helper:meta",
|
||||
"@php artisan vendor:publish --tag=laravel-assets --ansi --force"
|
||||
]
|
||||
},
|
||||
"config": {
|
||||
"preferred-install": "dist",
|
||||
"sort-packages": true,
|
||||
"optimize-autoloader": true
|
||||
}
|
||||
"optimize-autoloader": true,
|
||||
"allow-plugins": {
|
||||
"kylekatarnls/update-helper": true,
|
||||
"symfony/thanks": true,
|
||||
"php-http/discovery": true
|
||||
}
|
||||
},
|
||||
"minimum-stability": "stable",
|
||||
"prefer-stable": true
|
||||
}
|
||||
|
||||
12162
composer.lock
generated
12162
composer.lock
generated
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user