Compare commits
986 Commits
Author | SHA1 | Date | |
---|---|---|---|
|
43fd2fff2b | ||
|
b37c64f5a5 | ||
|
8abadd1070 | ||
|
1483ef83d9 | ||
|
dbed799e20 | ||
|
4602b68425 | ||
|
2d3eaedaa7 | ||
|
4d60b21a5f | ||
|
50ee8374ee | ||
|
65eb23e8ce | ||
|
5c76b18ddd | ||
|
fc6f5d2535 | ||
|
6a0348e9dc | ||
|
90e6409b1e | ||
|
96b28a8e9b | ||
|
a818e87e96 | ||
|
dc715758a6 | ||
|
87069329d8 | ||
|
8a380a4545 | ||
|
a5e18cb761 | ||
|
e9da3e7b6a | ||
|
289fa23728 | ||
|
c117ee61d5 | ||
|
7385932e52 | ||
|
cb90b69b3f | ||
|
8d0e31872a | ||
|
881925fd43 | ||
|
3a2f744f0a | ||
|
9dc9e668c5 | ||
|
39477c8de8 | ||
|
1b0bb95e81 | ||
|
cd5a4bec52 | ||
|
43070ab809 | ||
|
532fedbb62 | ||
|
585bf37783 | ||
|
cad364e407 | ||
|
45457c5b38 | ||
|
4e9142b5be | ||
|
207b365d40 | ||
|
1ec95a0d86 | ||
|
db4b9ccc7a | ||
|
a86d0c74d3 | ||
|
354f4e47df | ||
|
84167650b8 | ||
|
15c12a81f1 | ||
|
249ae584c3 | ||
|
b04f7b2d2c | ||
|
630ce459cb | ||
|
a7a30fb282 | ||
|
ab5abe9bcf | ||
|
7834fff541 | ||
|
ef8849e8a9 | ||
|
78bedf9ad6 | ||
|
6ec757ab66 | ||
|
7d71299c51 | ||
|
bea37aee7f | ||
|
5323687ea5 | ||
|
d1372a4c43 | ||
|
c9249a164a | ||
|
6f105f2626 | ||
|
e4c08896f4 | ||
|
e85a0df9b7 | ||
|
a5b7eabd97 | ||
|
f3688431a3 | ||
|
44e714352d | ||
|
60da68c994 | ||
|
11288fac20 | ||
|
fe9d373444 | ||
|
cce31f9b0b | ||
|
ca779ed5ad | ||
|
eb0eaaae2e | ||
|
9631b97694 | ||
|
6f036876c8 | ||
|
bd47179ea4 | ||
|
418c6bd88b | ||
|
ada4c4f816 | ||
|
91b499fb14 | ||
|
1f73adffd6 | ||
|
82bd313e7a | ||
|
14cbfe47b9 | ||
|
f2d4ff6dc4 | ||
|
bf33a70727 | ||
|
e1bdcbd581 | ||
|
051c1e7622 | ||
|
3b176474ff | ||
|
15f1b33ea6 | ||
|
0603d4076a | ||
|
ac94f10dc3 | ||
|
bbb92490e9 | ||
|
2cb63092c0 | ||
|
b9bcb59592 | ||
|
e083adc022 | ||
|
c3cd38fe9f | ||
|
a7c2db5e99 | ||
|
4926ee5117 | ||
|
e7723ac3db | ||
|
8dbfb93e4e | ||
|
6096366756 | ||
|
8e4cf12512 | ||
|
b61fa1f870 | ||
|
ac3cf9e4b1 | ||
|
e18463f059 | ||
|
ee54e08d18 | ||
|
3c07df6496 | ||
|
2117b828c8 | ||
|
28d9694432 | ||
|
7d977700e6 | ||
|
33942945d0 | ||
|
8d7d78db46 | ||
|
3268cc30ea | ||
|
8830ebe34f | ||
|
1d9adba6dd | ||
|
71be73777e | ||
|
7709f70ef1 | ||
|
9b528b84e1 | ||
|
c53a4d4861 | ||
|
766173df3d | ||
|
7f65cae891 | ||
|
bc56ecb85c | ||
|
50c3151301 | ||
|
d49ec0a81e | ||
|
6692028762 | ||
|
b71c357958 | ||
|
03b7621f3e | ||
|
2bcf24bd84 | ||
|
cc5aa05b12 | ||
|
3232c5c4ce | ||
|
72a52f5cd6 | ||
|
bda48a56e0 | ||
|
09cdcf8e53 | ||
|
a4d5b41ca7 | ||
|
9fa0d91d06 | ||
|
510f60bdeb | ||
|
30fe827253 | ||
|
8f0f4b168b | ||
|
cd11c4beb6 | ||
|
4d49cc413a | ||
|
3d50a58a31 | ||
|
d5701230fa | ||
|
ab945d6afe | ||
|
b4f8a36d43 | ||
|
608c1b4eb6 | ||
|
edf3c42157 | ||
|
c523cec113 | ||
|
6f8b987d42 | ||
|
d0d0642bdf | ||
|
924d760e3b | ||
|
f8c207ca2b | ||
|
ada1edd0b7 | ||
|
c79333db61 | ||
|
13778bed87 | ||
|
83b4d96f42 | ||
|
6cf96de0b4 | ||
|
481fadc7fc | ||
|
44f2c59e56 | ||
|
83589a912f | ||
|
fab9f03e7d | ||
|
928c83b13c | ||
|
72f20bc69b | ||
|
8293d5379d | ||
|
1d29445a8b | ||
|
5c308d757f | ||
|
5bf16004c9 | ||
|
417fa437b7 | ||
|
43f893c921 | ||
|
8ad9af4bc2 | ||
|
b3931bdc9d | ||
|
7ac153848b | ||
|
7689ebcac5 | ||
|
bf7adf9ca9 | ||
|
51ab8c556a | ||
|
149a8e910d | ||
|
310746b8cd | ||
|
6681303450 | ||
|
3228f37f09 | ||
|
421785bf6a | ||
|
308e8ca8c7 | ||
|
cb068ef70e | ||
|
acd3ec782f | ||
|
12d1e5b8d0 | ||
|
b165cdbd79 | ||
|
66f4507dee | ||
|
a724e1cb58 | ||
|
f942809de0 | ||
|
8043f77e02 | ||
|
1db9ba90d8 | ||
|
464fa59cb6 | ||
|
a8e2cbf55b | ||
|
fecd8dab38 | ||
|
13a833e3ed | ||
|
8d4784052a | ||
|
d9e9b41861 | ||
|
f0c3ef0aa1 | ||
|
3888831679 | ||
|
1a32fad324 | ||
|
d842ae9540 | ||
|
d0177b7504 | ||
|
e3842b25f3 | ||
|
a29b59c9cd | ||
|
f94eb97aa4 | ||
|
86017b79eb | ||
|
bc22fa5fad | ||
|
d6b70028ff | ||
|
0f7f9acd58 | ||
|
20633a6d1a | ||
|
a23856270d | ||
|
55543e370e | ||
|
8137a46c68 | ||
|
aeb9597c71 | ||
|
5067485e94 | ||
|
dfd456c7dc | ||
|
0ccb07e683 | ||
|
ca67a6897f | ||
|
0390227641 | ||
|
395b0982db | ||
|
f096ab4da7 | ||
|
d5ec9f7640 | ||
|
5732867407 | ||
|
31842f4c12 | ||
|
d5168d2da6 | ||
|
cac5ec836b | ||
|
ee9569e7d4 | ||
|
95df7de026 | ||
|
d3a5bd374d | ||
|
43ac3dddf1 | ||
|
1174328de3 | ||
|
dda54fb907 | ||
|
92ea808a5d | ||
|
2bc3a75c94 | ||
|
07ef97ce7c | ||
|
5fe3539331 | ||
|
4bc3bd5f13 | ||
|
4abce854d7 | ||
|
3b5c73992e | ||
|
c5abea2944 | ||
|
8e5cf14ebc | ||
|
7c8410ab86 | ||
|
369b4b92cc | ||
|
c50bb70383 | ||
|
d84b2060f0 | ||
|
afcf6024e6 | ||
|
3542bd6668 | ||
|
69b9116dd5 | ||
|
a2db4f06b1 | ||
|
b60b0fb511 | ||
|
0c1e9a6bb5 | ||
|
2692f92cb9 | ||
|
a43c8b4b00 | ||
|
6f15389411 | ||
|
f055241802 | ||
|
ac77c3a390 | ||
|
5cd99f2edc | ||
|
7c70fbec30 | ||
|
ac0dc3196f | ||
|
7f4da826b1 | ||
|
cc2af4371f | ||
|
2a79a03d38 | ||
|
47aac7fe33 | ||
|
d4055884b1 | ||
|
61658e847a | ||
|
bd714223ce | ||
|
060154cb89 | ||
|
99a1bfca9d | ||
|
4379f30628 | ||
|
b501244577 | ||
|
6f7b9815ca | ||
|
227bd088f7 | ||
|
b1d6ecb07c | ||
|
41772f28bd | ||
|
393dac1c99 | ||
|
db9d0be6c7 | ||
|
f0774ec273 | ||
|
d4a4d28b58 | ||
|
3ec97021bd | ||
|
56cf972373 | ||
|
2f3ae4c1af | ||
|
bc3dd04e12 | ||
|
4e8fc1b431 | ||
|
cd39aa2968 | ||
|
87ea3fc982 | ||
|
f74a511778 | ||
|
202461fe48 | ||
|
2af6687351 | ||
|
e603af5f24 | ||
|
84069ee882 | ||
|
edbb5cef92 | ||
|
a62c54b4ed | ||
|
41df7c04c3 | ||
|
9b783a8322 | ||
|
57db4df618 | ||
|
9d1081bd56 | ||
|
cc3773817b | ||
|
8956355e57 | ||
|
645db97c14 | ||
|
07a04dc507 | ||
|
a7317af413 | ||
|
ba6d6b8851 | ||
|
3726810108 | ||
|
4f92a7edf3 | ||
|
5d84b61f18 | ||
|
cd1329ec67 | ||
|
48a58b2b69 | ||
|
9b64aba8bf | ||
|
cae8264d98 | ||
|
95d8985336 | ||
|
b26ae90807 | ||
|
66d171c432 | ||
|
40463d9831 | ||
|
eb7dee013d | ||
|
1a878599b1 | ||
|
b9e25abdd9 | ||
|
c612022717 | ||
|
31e7f02b8d | ||
|
75747a2979 | ||
|
a85a8668a7 | ||
|
cca5fd859c | ||
|
2ed49abb1b | ||
|
409e6d49b2 | ||
|
93cfc482b8 | ||
|
85be6d53d0 | ||
|
bf6f58eb5e | ||
|
cd9d17ab18 | ||
|
e0bc6a10d0 | ||
|
ccfc1ad166 | ||
|
812060240f | ||
|
715a266ca4 | ||
|
fa4b3ece56 | ||
|
aac2a002bb | ||
|
8574acaf6e | ||
|
ddf4639354 | ||
|
9bd394f351 | ||
|
6899d48aae | ||
|
514e1ca8d0 | ||
|
eba5be010a | ||
|
f578d5c1c9 | ||
|
e58b1d670b | ||
|
c2642259b4 | ||
|
c47b0c9741 | ||
|
f425156cad | ||
|
7d7e31120e | ||
|
ccd247d154 | ||
|
53df6849f7 | ||
|
c2080bd1b3 | ||
|
9e93f8c2a5 | ||
|
907a142c8d | ||
|
d7bc8cd8e4 | ||
|
bb3e00a695 | ||
|
af5e7974c3 | ||
|
6a88959ec4 | ||
|
7b59149f90 | ||
|
3e01079caf | ||
|
d92e62e40b | ||
|
b4952dea7b | ||
|
00acb04329 | ||
|
939dd0591e | ||
|
febdb2a9e0 | ||
|
bcfd9fc1c9 | ||
|
e4964da5d4 | ||
|
a042298a4a | ||
|
908be168a9 | ||
|
d780bb3937 | ||
|
461e7e8913 | ||
|
819e8b73c3 | ||
|
627b7087a1 | ||
|
f23cf555e1 | ||
|
000978e4fb | ||
|
74782483bd | ||
|
c5f9387b92 | ||
|
57583b6747 | ||
|
434c236210 | ||
|
807bb97b6a | ||
|
da53bd44d1 | ||
|
3b01943649 | ||
|
3340f9c6ee | ||
|
b21cfe8504 | ||
|
97ab6ec299 | ||
|
7fda58e5c8 | ||
|
db6e820d1d | ||
|
8c2e1875ca | ||
|
bd95fe9af1 | ||
|
a517f89234 | ||
|
f994e7bfa8 | ||
|
28716924c9 | ||
|
b597f90f5b | ||
|
5912420467 | ||
|
90f35fd680 | ||
|
3e2d6e71b9 | ||
|
cbffdd829a | ||
|
d77e092948 | ||
|
1f5e10e784 | ||
|
68e3813c6c | ||
|
c9d78e3f67 | ||
|
b4f3fb3b30 | ||
|
bf7fb898f9 | ||
|
6c5e0543b4 | ||
|
578a1db62f | ||
|
4524a55b23 | ||
|
f942eaf1b6 | ||
|
6a4d16fae9 | ||
|
53b234252f | ||
|
9287e81ef1 | ||
|
5462326f79 | ||
|
b8b3992159 | ||
|
8214000713 | ||
|
94337a33d4 | ||
|
8ddee03338 | ||
|
8e2934533b | ||
|
71ee784003 | ||
|
597528e9b7 | ||
|
45fbbf9218 | ||
|
c7a4a01fee | ||
|
fa04ad1395 | ||
|
abdd85bdd5 | ||
|
dba55d57e8 | ||
|
9819c96717 | ||
|
1b0885bffe | ||
|
c0a8540ab8 | ||
|
f3b95f03a1 | ||
|
5f06f3da52 | ||
|
10a7fb809b | ||
|
d3ea1f3da2 | ||
|
74d0471a6f | ||
|
a1c1805409 | ||
|
b843145bed | ||
|
119920b665 | ||
|
6190e2d290 | ||
|
79de99d146 | ||
|
56a9b3df0a | ||
|
4df6413dba | ||
|
fcd0ada639 | ||
|
6b6bd155aa | ||
|
9c91b1bca5 | ||
|
1a5c0e5b24 | ||
|
981b30e4af | ||
|
30c84d8cbf | ||
|
4c538c54a8 | ||
|
ad7a8f1d08 | ||
|
f02a07dab4 | ||
|
c61b0fc1e1 | ||
|
fed7dfcb73 | ||
|
0d69c811ab | ||
|
83274add22 | ||
|
7497d1b6d4 | ||
|
239bc144e3 | ||
|
1ece64abe1 | ||
|
915783699b | ||
|
43f6bd41c9 | ||
|
98c017dd56 | ||
|
811ed3251b | ||
|
665b0f1484 | ||
|
9e26d845da | ||
|
6ddf20f5ce | ||
|
27ed0710cc | ||
|
cb87c9f345 | ||
|
e1e147c8f0 | ||
|
b37d889de9 | ||
|
b61c9bfc5e | ||
|
e963788a81 | ||
|
3ef4798e09 | ||
|
21730102ae | ||
|
b1ae05152e | ||
|
cc87f01e2f | ||
|
85dfa1b078 | ||
|
fdff54e11e | ||
|
0894f38660 | ||
|
f643d8ef25 | ||
|
e437284980 | ||
|
f6e0d330ac | ||
|
890cbf0f36 | ||
|
36cf2ea2e8 | ||
|
a8f7bd0280 | ||
|
aa6517046d | ||
|
42e4d8c7ba | ||
|
3d1da38cbc | ||
|
5ae8490999 | ||
|
715675b2f1 | ||
|
0d7198a26c | ||
|
d8b7ce6d9b | ||
|
d032ca31e3 | ||
|
67b49c0585 | ||
|
c23242f18e | ||
|
c23f7a3ee2 | ||
|
7e3e957649 | ||
|
b8f304dfd3 | ||
|
e895ac9245 | ||
|
77579a71e8 | ||
|
cc7e13cdbf | ||
|
5df3886e42 | ||
|
cc179a05d0 | ||
|
ec5f4f2778 | ||
|
084c95f0e5 | ||
|
3b78cfe0fd | ||
|
160ce3338d | ||
|
d425cfe1aa | ||
|
6ff286f9b4 | ||
|
b930461434 | ||
|
ee6bb24506 | ||
|
b69124c89f | ||
|
f089e4a1ce | ||
|
7dedb5c076 | ||
|
3276c9a84c | ||
|
04ef2ea3b6 | ||
|
04a2f27a75 | ||
|
d962f59884 | ||
|
fd3bd4567a | ||
|
29e7a5ecdb | ||
|
4956b0d89d | ||
|
3bc54a4e16 | ||
|
29ecd602ec | ||
|
1425dec564 | ||
|
2bb1c0c999 | ||
|
97f3daae70 | ||
|
c53a7ef6fe | ||
|
b97fc16ad9 | ||
|
eaac12ddc8 | ||
|
183be5da0e | ||
|
ace0953c87 | ||
|
1fd7e7833d | ||
|
aa5801d73b | ||
|
d1ea8081e4 | ||
|
49467c906d | ||
|
79bd575aca | ||
|
e533a0e405 | ||
|
94b597d29a | ||
|
504fefff94 | ||
|
ff794a3638 | ||
|
efb9a34cb8 | ||
|
a93291b38f | ||
|
dfc9eab9d3 | ||
|
3c74b0b1ef | ||
|
9b981fcea8 | ||
|
9d3a189d77 | ||
|
01c0175e8f | ||
|
d7e5e2f381 | ||
|
c27de8b9a5 | ||
|
c3a55f8b69 | ||
|
78b65a799e | ||
|
6a30cee611 | ||
|
bc19e3a6ff | ||
|
5a8f2fa2be | ||
|
81168c27c6 | ||
|
a606626053 | ||
|
adeb57864b | ||
|
747f1a6fae | ||
|
3ac9c23573 | ||
|
5ad9c0e77a | ||
|
ba5ba2f1d6 | ||
|
4902b5f351 | ||
|
166fcda193 | ||
|
83560bc775 | ||
|
4ffb00c9f5 | ||
|
fbac41a774 | ||
|
c837ab8693 | ||
|
1cc321ddff | ||
|
7861cffb91 | ||
|
82a472f368 | ||
|
cfe59774e7 | ||
|
1098475473 | ||
|
4546e795ef | ||
|
bb0aba586b | ||
|
204b995e6c | ||
|
321b7b4cee | ||
|
413377fbb9 | ||
|
e2e821881c | ||
|
51712ed2a8 | ||
|
27de7ddbf8 | ||
|
bb700f3a3d | ||
|
563268558b | ||
|
56744cec7b | ||
|
87696a2e6c | ||
|
79f1c20f8a | ||
|
4e5638df36 | ||
|
71349f35e4 | ||
|
2cb06bb4bb | ||
|
5e1769b81f | ||
|
e344f28265 | ||
|
2297d6b85b | ||
|
baaecdbd8c | ||
|
31d8af642d | ||
|
27c67ec202 | ||
|
4833a29e57 | ||
|
4a2f3e0372 | ||
|
f3eeff4ec8 | ||
|
221e03ecfa | ||
|
392a9c6f53 | ||
|
145d12b2c8 | ||
|
0c5033ff79 | ||
|
5f46f54dfd | ||
|
d4819b13eb | ||
|
9dd9be3b8c | ||
|
dd38809866 | ||
|
c414725f12 | ||
|
d3c8a350a4 | ||
|
e04b748b91 | ||
|
1eee8c4725 | ||
|
2b7a4b170c | ||
|
7c0bf4f137 | ||
|
54bae43d2e | ||
|
25be1155a6 | ||
|
4905d61a1a | ||
|
ff0147bebb | ||
|
7fe4889b6e | ||
|
3c9b1b1833 | ||
|
2a46c873b8 | ||
|
6ed09324ad | ||
|
d1f9c10790 | ||
|
5d041b2fd3 | ||
|
5cea0fa87b | ||
|
6f140eef29 | ||
|
e5850adfd7 | ||
|
6921833ce2 | ||
|
57b9accd97 | ||
|
de94f5b233 | ||
|
b5519a73fb | ||
|
c5d8c2c355 | ||
|
c5b02a426c | ||
|
4cae910b68 | ||
|
b664fccce2 | ||
|
21672f99d1 | ||
|
c269e46892 | ||
|
8534e8cf5b | ||
|
94bf1c2484 | ||
|
b3b30470fc | ||
|
41d91e75fc | ||
|
a97a91b844 | ||
|
f1c577ab76 | ||
|
17a9fe5024 | ||
|
d74a76dee3 | ||
|
c01201b88e | ||
|
833bdf878a | ||
|
891990b2f1 | ||
|
e9ab7029c9 | ||
|
6f681dba09 | ||
|
b3edff947d | ||
|
ce6d80d601 | ||
|
a80a2866de | ||
|
02c2221970 | ||
|
d35bd6e75b | ||
|
f3a2f98864 | ||
|
a3a312e3db | ||
|
f5cb5c4516 | ||
|
d458a28337 | ||
|
a102a780f8 | ||
|
4dbbc32108 | ||
|
b6aedb43ee | ||
|
ec50dcbbd9 | ||
|
bcc983f11f | ||
|
809651054e | ||
|
2e965ceb9e | ||
|
e35f942964 | ||
|
40e6fce281 | ||
|
2139865876 | ||
|
c3cda05d98 | ||
|
548f3db33d | ||
|
a76e9ed98b | ||
|
c0ef41a9bb | ||
|
f6e5d9675a | ||
|
ef028659d8 | ||
|
40f39e918d | ||
|
2ec3ee2734 | ||
|
bc29b89a16 | ||
|
e21853286e | ||
|
c012b8c4a5 | ||
|
48f6c28556 | ||
|
0c1502f801 | ||
|
fec20ed381 | ||
|
252c147dcf | ||
|
453d474104 | ||
|
84cf4a9b66 | ||
|
fb016bebde | ||
|
8f6a738481 | ||
|
b07f958577 | ||
|
8da0fde52a | ||
|
39be16cb63 | ||
|
59d0c0def4 | ||
|
79c03db9a0 | ||
|
0c77823020 | ||
|
deed7e0022 | ||
|
99db8c7335 | ||
|
9fe2aa9ed5 | ||
|
4c80dc256b | ||
|
cafe9e9c11 | ||
|
27ff4e63b6 | ||
|
8020714e07 | ||
|
dbd825ba4b | ||
|
fee6cf29eb | ||
|
56287d8e7a | ||
|
45504eaf95 | ||
|
b8a9b1150a | ||
|
8bd0e43f58 | ||
|
2c83e9e83c | ||
|
53c9ca10a7 | ||
|
75fbdac42e | ||
|
09d54546ca | ||
|
b62fece3d0 | ||
|
284a2b7f64 | ||
|
9c873ccbbd | ||
|
5f72f90031 | ||
|
93cf3c69b8 | ||
|
88f856cbc7 | ||
|
2d5796d161 | ||
|
1d20dc9fcb | ||
|
49502235b5 | ||
|
6e9d71fcf8 | ||
|
27c7e33773 | ||
|
3012619049 | ||
|
d6801966c4 | ||
|
518e29118c | ||
|
acf4f3fbf0 | ||
|
8378030c70 | ||
|
dc7140d486 | ||
|
e3771a1c53 | ||
|
2e9ac00a42 | ||
|
4b8b3acd39 | ||
|
8703798ca0 | ||
|
47ac438844 | ||
|
bd3aa28523 | ||
|
68d0ae4002 | ||
|
6991039640 | ||
|
00611ef9dc | ||
|
3c50e4768a | ||
|
63dc9834be | ||
|
f042e42633 | ||
|
39b80a2e7e | ||
|
fb6e948358 | ||
|
181b0845bf | ||
|
b2a82dcfe5 | ||
|
ed1c05dec9 | ||
|
cd73aef0c9 | ||
|
ed522ec024 | ||
|
16998d1e16 | ||
|
11421d2d32 | ||
|
1af708aaea | ||
|
fbb82fa759 | ||
|
a9f977c3b4 | ||
|
5669e22548 | ||
|
da4bb9b83d | ||
|
c2cbaf0937 | ||
|
d81dce536c | ||
|
8e1e2c5b85 | ||
|
959d5e6822 | ||
|
036a0b4cb3 | ||
|
c038f14374 | ||
|
343cf91643 | ||
|
4fe70a4c46 | ||
|
59f7200512 | ||
|
0335e709c2 | ||
|
a7bb5ac21b | ||
|
99655cab33 | ||
|
348e574154 | ||
|
f35dee8643 | ||
|
168412c2e7 | ||
|
b9832542fb | ||
|
316545b253 | ||
|
348a57a0d8 | ||
|
a3f5654816 | ||
|
8207afe806 | ||
|
e15b8f8092 | ||
|
e6c99028d6 | ||
|
2e691d7c26 | ||
|
6c87812780 | ||
|
798a3632cf | ||
|
aa1d0aabd2 | ||
|
f348e691fa | ||
|
0dfda83e45 | ||
|
56044fa3f7 | ||
|
4c69fd4f60 | ||
|
92e3f7a6a3 | ||
|
76d0618d6e | ||
|
80c3a99eb1 | ||
|
91e3a3237b | ||
|
8c8c5a5826 | ||
|
b9370955d6 | ||
|
2761e728f7 | ||
|
f418bf4f63 | ||
|
3d046e4369 | ||
|
b6eb6f2d70 | ||
|
45ce3e26c1 | ||
|
ae91cedf76 | ||
|
b2a6b484b5 | ||
|
95c4fa56cc | ||
|
e8c56afa8e | ||
|
ddc3b1f6c2 | ||
|
d33d886574 | ||
|
0487980d2f | ||
|
e68257d6c9 | ||
|
ea0bbab680 | ||
|
5d4a8136c5 | ||
|
c98d851cd2 | ||
|
42fa89db7a | ||
|
1a70acc6f2 | ||
|
ee0a287112 | ||
|
cf16a66c63 | ||
|
4544b17c94 | ||
|
8119c9da51 | ||
|
baf4d75c01 | ||
|
ac9eff0fcc | ||
|
dc57bd92ff | ||
|
3c56a2c868 | ||
|
2d2ab10a24 | ||
|
ac906c9994 | ||
|
480406d579 | ||
|
47efb644b7 | ||
|
fd0e519e41 | ||
|
0c8bb990d0 | ||
|
bd7139827b | ||
|
4f648aff52 | ||
|
3feb45dc01 | ||
|
0489dc7c33 | ||
|
a30843cff9 | ||
|
3a34a0eb40 | ||
|
e3f82e136a | ||
|
8a7df4ba9f | ||
|
e86d1a4c7a | ||
|
5b9d0b60a1 | ||
|
7eff2f0c49 | ||
|
97236bb01d | ||
|
9c6aa12f48 | ||
|
96ccb03eea | ||
|
55f55820c5 | ||
|
955839d513 | ||
|
a650e628e5 | ||
|
54142b73fb | ||
|
55e0d2695d | ||
|
2f90ab15dc | ||
|
fd3fc66bfc | ||
|
a352a94d8a | ||
|
410b81f46f | ||
|
aa3711c5cc | ||
|
d6b1f97a04 | ||
|
b4e8e57a22 | ||
|
9644e6195c | ||
|
764e0c7607 | ||
|
97d640dd40 | ||
|
d2915b5b05 | ||
|
f274f6fd18 | ||
|
f507ac2569 | ||
|
208cbd6d89 | ||
|
cd2aa2902a | ||
|
fa2d7fa3da | ||
|
7463767781 | ||
|
958bc864c9 | ||
|
4484668750 | ||
|
d5dea4b87f | ||
|
0fdef6a0a2 | ||
|
bd71b6bad8 | ||
|
9b7887b279 | ||
|
3960e43872 | ||
|
201c8f9ec9 | ||
|
8c8374a08c | ||
|
467595afc9 | ||
|
acb54f098c | ||
|
5755d13460 | ||
|
2c3500315d | ||
|
47ea60c0cd | ||
|
18b18c1396 | ||
|
ff227de5fa | ||
|
6799692811 | ||
|
c6173f7f6f | ||
|
d0e4dabc44 | ||
|
f815dae300 | ||
|
b3bd6bb39e | ||
|
71df6409c2 | ||
|
e4f9a1e0cf | ||
|
ca6a05e393 | ||
|
c0d26164dc | ||
|
76fe2a1ba9 | ||
|
8cbdb54402 | ||
|
764ef80a62 | ||
|
0c37d93c01 | ||
|
c57a5128e5 | ||
|
6cf4eba20a | ||
|
6825d728c2 | ||
|
6d3091b2a2 | ||
|
61473877a4 | ||
|
52989c8f5c | ||
|
b64ba2ef16 | ||
|
461ae99dd8 | ||
|
8681df6f02 | ||
|
ba081ee442 | ||
|
cf90d05115 | ||
|
658c6554af | ||
|
94d9d608f7 | ||
|
015b50be5f | ||
|
85970f8c96 | ||
|
1740ab0bbe | ||
|
9d2b5593a2 | ||
|
881d62d69d | ||
|
935129f0a5 | ||
|
1a9bdc5e6d | ||
|
1f565bca10 | ||
|
da089197a3 | ||
|
2df8f41d6c | ||
|
4e4f0d4c97 | ||
|
eaad22c0a1 | ||
|
63e8553a09 | ||
|
b65828416f | ||
|
48dc8033f5 | ||
|
2d838b69fd | ||
|
6c529a6908 | ||
|
9baefec541 | ||
|
327d66bb80 | ||
|
760a4dfcb9 | ||
|
ceb8cdd337 | ||
|
570c754eec | ||
|
8ed75d1d21 | ||
|
54710b8221 | ||
|
ff794033e1 | ||
|
f0f486da9e | ||
|
aedcfd1d24 | ||
|
44e738acf5 | ||
|
701b45c286 | ||
|
72fe687d82 | ||
|
632cd66b57 | ||
|
20530c000e | ||
|
8824786fb4 | ||
|
bdeb4a4efe | ||
|
cf455fc19b | ||
|
1bcddadb7a | ||
|
6dc28f11e0 | ||
|
8ad601fcc0 | ||
|
f400844a3d | ||
|
560096878f | ||
|
0938f5ab71 | ||
|
06193d27c0 | ||
|
eb18857ecc | ||
|
9a280e99ad | ||
|
c7ca20b45a | ||
|
60e64a3646 | ||
|
d60f89976e | ||
|
41e05ddf9c | ||
|
5a34f16dcf | ||
|
769ca4e26d | ||
|
b6e62b08e4 | ||
|
effe5b32fd | ||
|
57c2e89f00 | ||
|
914a0bf514 | ||
|
75fbdb653a | ||
|
bdfe75cff3 | ||
|
bcd845fd59 | ||
|
f1e71ecb78 | ||
|
0aa4c8af6f | ||
|
a950b80d5a | ||
|
ed3bb6429b | ||
|
1e88491ca1 | ||
|
6b6ad05e3a | ||
|
5f4a364095 | ||
|
95a8867527 | ||
|
7cb1301e80 | ||
|
e6e070d89e | ||
|
ba2bcaba07 | ||
|
864f82ba11 | ||
|
f671c992e1 | ||
|
a4151800f1 | ||
|
932f24c966 | ||
|
0c0bce9755 | ||
|
f07508073f | ||
|
e06cc1bd2d | ||
|
36e33a4c10 | ||
|
7f668c1653 | ||
|
b464fa98df | ||
|
23491f1e8c | ||
|
2b90a2eed2 | ||
|
13b9d15d8f | ||
|
a053504bb8 | ||
|
dcdd4aec85 | ||
|
179da2ac05 | ||
|
4848739b6e | ||
|
46da285831 | ||
|
71a6a36a54 | ||
|
c8ca9ef7ab | ||
|
5af2fff9ca | ||
|
337c9bc01e | ||
|
a196dce1fa | ||
|
b9633bbcd6 | ||
|
46efe2b8dd | ||
|
cc1dd682e8 | ||
|
bdd984a887 | ||
|
2d3dffe5fc | ||
|
65f31a0b38 | ||
|
4a1a6c5933 | ||
|
7e1fd99c37 | ||
|
8fe8209580 | ||
|
264a050360 | ||
|
3623104e3b | ||
|
84c9846f7b |
@ -1,61 +0,0 @@
|
|||||||
{
|
|
||||||
"files": [
|
|
||||||
"README.md"
|
|
||||||
],
|
|
||||||
"imageSize": 100,
|
|
||||||
"commit": false,
|
|
||||||
"contributors": [
|
|
||||||
{
|
|
||||||
"login": "antonioag95",
|
|
||||||
"name": "antonioag95",
|
|
||||||
"avatar_url": "https://avatars.githubusercontent.com/u/30556866?v=4",
|
|
||||||
"profile": "https://github.com/antonioag95",
|
|
||||||
"contributions": [
|
|
||||||
"test",
|
|
||||||
"code"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"login": "tonjo",
|
|
||||||
"name": "tonjo",
|
|
||||||
"avatar_url": "https://avatars.githubusercontent.com/u/4726289?v=4",
|
|
||||||
"profile": "https://github.com/tonjo",
|
|
||||||
"contributions": [
|
|
||||||
"code"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"login": "reafian",
|
|
||||||
"name": "Richard Newton",
|
|
||||||
"avatar_url": "https://avatars.githubusercontent.com/u/11992416?v=4",
|
|
||||||
"profile": "https://github.com/reafian",
|
|
||||||
"contributions": [
|
|
||||||
"code"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"login": "davejlong",
|
|
||||||
"name": "David Long",
|
|
||||||
"avatar_url": "https://avatars.githubusercontent.com/u/175317?v=4",
|
|
||||||
"profile": "http://www.davejlong.com",
|
|
||||||
"contributions": [
|
|
||||||
"code"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"login": "marneu",
|
|
||||||
"name": "Markus Neubauer",
|
|
||||||
"avatar_url": "https://avatars.githubusercontent.com/u/5978293?v=4",
|
|
||||||
"profile": "http://www.std-soft.com",
|
|
||||||
"contributions": [
|
|
||||||
"code"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"contributorsPerLine": 7,
|
|
||||||
"projectName": "WGDashboard",
|
|
||||||
"projectOwner": "donaldzou",
|
|
||||||
"repoType": "github",
|
|
||||||
"repoHost": "https://github.com",
|
|
||||||
"skipCi": true
|
|
||||||
}
|
|
@ -12,6 +12,7 @@
|
|||||||
name: "CodeQL"
|
name: "CodeQL"
|
||||||
|
|
||||||
on:
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
push:
|
push:
|
||||||
branches: [ main ]
|
branches: [ main ]
|
||||||
pull_request:
|
pull_request:
|
||||||
@ -38,11 +39,11 @@ jobs:
|
|||||||
|
|
||||||
steps:
|
steps:
|
||||||
- name: Checkout repository
|
- name: Checkout repository
|
||||||
uses: actions/checkout@v2
|
uses: actions/checkout@v3
|
||||||
|
|
||||||
# Initializes the CodeQL tools for scanning.
|
# Initializes the CodeQL tools for scanning.
|
||||||
- name: Initialize CodeQL
|
- name: Initialize CodeQL
|
||||||
uses: github/codeql-action/init@v1
|
uses: github/codeql-action/init@v3
|
||||||
with:
|
with:
|
||||||
languages: ${{ matrix.language }}
|
languages: ${{ matrix.language }}
|
||||||
# If you wish to specify custom queries, you can do so here or in a config file.
|
# If you wish to specify custom queries, you can do so here or in a config file.
|
||||||
@ -53,7 +54,7 @@ jobs:
|
|||||||
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
|
# Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
|
||||||
# If this step fails, then you should remove it and run the build manually (see below)
|
# If this step fails, then you should remove it and run the build manually (see below)
|
||||||
- name: Autobuild
|
- name: Autobuild
|
||||||
uses: github/codeql-action/autobuild@v1
|
uses: github/codeql-action/autobuild@v3
|
||||||
|
|
||||||
# ℹ️ Command-line programs to run using the OS shell.
|
# ℹ️ Command-line programs to run using the OS shell.
|
||||||
# 📚 https://git.io/JvXDl
|
# 📚 https://git.io/JvXDl
|
||||||
@ -67,4 +68,4 @@ jobs:
|
|||||||
# make release
|
# make release
|
||||||
|
|
||||||
- name: Perform CodeQL Analysis
|
- name: Perform CodeQL Analysis
|
||||||
uses: github/codeql-action/analyze@v1
|
uses: github/codeql-action/analyze@v3
|
105
.github/workflows/docker.yml
vendored
Normal file
@ -0,0 +1,105 @@
|
|||||||
|
name: Docker Build and Push
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- 'main'
|
||||||
|
tags:
|
||||||
|
- '*'
|
||||||
|
release:
|
||||||
|
types: [ published ]
|
||||||
|
|
||||||
|
env:
|
||||||
|
DOCKERHUB_PREFIX: docker.io
|
||||||
|
GITHUB_CONTAINER_PREFIX: ghcr.io
|
||||||
|
DOCKER_IMAGE: donaldzou/wgdashboard
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
docker_build:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
steps:
|
||||||
|
- name: Checkout repository
|
||||||
|
uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Log in to Docker Hub
|
||||||
|
uses: docker/login-action@v3
|
||||||
|
with:
|
||||||
|
registry: ${{ env.DOCKERHUB_PREFIX }}
|
||||||
|
username: ${{ secrets.DOCKER_HUB_USERNAME }}
|
||||||
|
password: ${{ secrets.DOCKER_HUB_PASSWORD }}
|
||||||
|
|
||||||
|
- name: Log in to GitHub Container Registry
|
||||||
|
uses: docker/login-action@v3
|
||||||
|
with:
|
||||||
|
registry: ${{ env.GITHUB_CONTAINER_PREFIX }}
|
||||||
|
username: ${{ github.actor }}
|
||||||
|
password: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
|
||||||
|
- name: Set up QEMU
|
||||||
|
uses: docker/setup-qemu-action@v3
|
||||||
|
with:
|
||||||
|
platforms: |
|
||||||
|
- linux/amd64
|
||||||
|
- linux/arm64
|
||||||
|
- linux/arm/v7
|
||||||
|
|
||||||
|
- name: Set up Docker Buildx
|
||||||
|
uses: docker/setup-buildx-action@v3
|
||||||
|
|
||||||
|
- name: Docker meta by docs https://github.com/docker/metadata-action
|
||||||
|
id: meta
|
||||||
|
uses: docker/metadata-action@v5
|
||||||
|
with:
|
||||||
|
images: |
|
||||||
|
${{ env.DOCKERHUB_PREFIX }}/${{ env.DOCKER_IMAGE }}
|
||||||
|
${{ env.GITHUB_CONTAINER_PREFIX }}/${{ env.DOCKER_IMAGE }}
|
||||||
|
tags: |
|
||||||
|
type=ref,event=branch
|
||||||
|
type=ref,event=tag
|
||||||
|
|
||||||
|
- name: Build and export (multi-arch)
|
||||||
|
uses: docker/build-push-action@v6
|
||||||
|
with:
|
||||||
|
context: .
|
||||||
|
file: ./docker/Dockerfile
|
||||||
|
push: ${{ github.event_name != 'pull_request' }}
|
||||||
|
tags: ${{ steps.meta.outputs.tags }}
|
||||||
|
labels: ${{ steps.meta.outputs.labels }}
|
||||||
|
platforms: linux/amd64,linux/arm64,linux/arm/v7
|
||||||
|
|
||||||
|
docker_scan:
|
||||||
|
if: ${{ github.event_name != 'pull_request' }}
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
needs: docker_build
|
||||||
|
steps:
|
||||||
|
- name: Log in to Docker Hub
|
||||||
|
uses: docker/login-action@v3
|
||||||
|
with:
|
||||||
|
registry: ${{ env.DOCKERHUB_PREFIX }}
|
||||||
|
username: ${{ secrets.DOCKER_HUB_USERNAME }}
|
||||||
|
password: ${{ secrets.DOCKER_HUB_PASSWORD }}
|
||||||
|
|
||||||
|
- name: Docker Scout CVEs
|
||||||
|
uses: docker/scout-action@v1
|
||||||
|
with:
|
||||||
|
command: cves
|
||||||
|
image: ${{ env.GITHUB_CONTAINER_PREFIX }}/${{ env.DOCKER_IMAGE }}:main
|
||||||
|
only-severities: critical,high
|
||||||
|
only-fixed: true
|
||||||
|
write-comment: true
|
||||||
|
github-token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
exit-code: true
|
||||||
|
|
||||||
|
- name: Docker Scout Compare
|
||||||
|
uses: docker/scout-action@v1
|
||||||
|
with:
|
||||||
|
command: compare
|
||||||
|
# Set to Github for maximum compat
|
||||||
|
image: ${{ env.GITHUB_CONTAINER_PREFIX }}/${{ env.DOCKER_IMAGE }}:main
|
||||||
|
to: ${{ env.GITHUB_CONTAINER_PREFIX }}/${{ env.DOCKER_IMAGE }}:latest
|
||||||
|
only-severities: critical,high
|
||||||
|
ignore-unchanged: true
|
||||||
|
github-token: ${{ secrets.GITHUB_TOKEN }}
|
26
.github/workflows/stale.yml
vendored
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
# This workflow warns and then closes issues and PRs that have had no activity for a specified amount of time.
|
||||||
|
#
|
||||||
|
# You can adjust the behavior by modifying this file.
|
||||||
|
# For more information, see:
|
||||||
|
# https://github.com/actions/stale
|
||||||
|
name: Mark stale issues and pull requests
|
||||||
|
|
||||||
|
on:
|
||||||
|
workflow_dispatch:
|
||||||
|
schedule:
|
||||||
|
- cron: '00 08 * * *'
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
stale:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
permissions:
|
||||||
|
issues: write
|
||||||
|
steps:
|
||||||
|
- uses: actions/stale@v9
|
||||||
|
with:
|
||||||
|
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||||
|
stale-issue-message: 'This issue has not been updated for 20 days'
|
||||||
|
stale-pr-message: 'This pull request has not been updated for 20 days'
|
||||||
|
stale-issue-label: 'stale'
|
||||||
|
exempt-issue-labels: 'enhancement,ongoing'
|
||||||
|
days-before-stale: 20
|
37
.gitignore
vendored
@ -15,4 +15,39 @@ log/**
|
|||||||
release/*
|
release/*
|
||||||
src/db/wgdashboard.db
|
src/db/wgdashboard.db
|
||||||
.jshintrc
|
.jshintrc
|
||||||
node_modules/
|
node_modules/**
|
||||||
|
*/proxy.js
|
||||||
|
src/static/app/proxy.js
|
||||||
|
.secrets
|
||||||
|
|
||||||
|
# Logs
|
||||||
|
logs
|
||||||
|
*.log
|
||||||
|
npm-debug.log*
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
||||||
|
pnpm-debug.log*
|
||||||
|
lerna-debug.log*
|
||||||
|
|
||||||
|
node_modules
|
||||||
|
.DS_Store
|
||||||
|
dist-ssr
|
||||||
|
coverage
|
||||||
|
*.local
|
||||||
|
|
||||||
|
/cypress/videos/
|
||||||
|
/cypress/screenshots/
|
||||||
|
|
||||||
|
# Editor directories and files
|
||||||
|
.vscode/*
|
||||||
|
!.vscode/extensions.json
|
||||||
|
.idea
|
||||||
|
*.suo
|
||||||
|
*.ntvs*
|
||||||
|
*.njsproj
|
||||||
|
*.sln
|
||||||
|
*.sw?
|
||||||
|
|
||||||
|
*.tsbuildinfo
|
||||||
|
.vite/*
|
||||||
|
|
||||||
|
666
README.md
@ -1,605 +1,87 @@
|
|||||||
### Development on v4
|
> [!TIP]
|
||||||
Hi Everyone, I've decided to promote the long time working `v3.1` to `v4`! This version will be a huge update on the structure of the app. The frontend will be completely handle on the client side with Vue.js, and communicate with the server via REST Api. With this design, it could be possible for the frontend app to connect to multiple server in the future. Stay tune to this new verion, I'm working very hard on this while balancing school and work. If you would like know whats going on, you can check out [this branch](https://github.com/donaldzou/WGDashboard/tree/v4) 😊
|
> 🎉 I'm excited to announce that WGDashboard is officially listed on DigitalOcean's Marketplace! For more information, please visit [Host WGDashboard & WireGuard with DigitalOcean](https://donaldzou.dev/WGDashboard-Documentation/host-wgdashboard-wireguard-with-digitalocean.html) for more information!
|
||||||
|
|
||||||
#### A preview on v4.0 🥹
|
> [!NOTE]
|
||||||
|
> **Help Wanted 🎉**: Localizing WGDashboard to other languages! If you're willing to help, please visit https://github.com/donaldzou/WGDashboard/issues/397. Many thanks!
|
||||||

|
|
||||||
|
|
||||||
|
|
||||||
<hr>
|
|
||||||
|
|
||||||
##### Known issue on WGDashboard `v3.0 - v3.0.6`
|

|
||||||
- [IPv6 in WireGuard might not fully support.](https://github.com/donaldzou/WGDashboard/issues/167)
|
|
||||||
<hr>
|
|
||||||
|
|
||||||
<p align="center">
|
|
||||||
<img alt="WGDashboard" src="img/logo.png" width="128">
|
|
||||||
</p>
|
|
||||||
<h1 align="center">WGDashboard</h1>
|
|
||||||
|
|
||||||
|
|
||||||
<p align="center">
|
<p align="center">
|
||||||
<img src="http://ForTheBadge.com/images/badges/made-with-python.svg">
|
<img alt="WGDashboard" src="https://wgdashboard-resources.tor1.cdn.digitaloceanspaces.com/Logos/Logo-2-Rounded-512x512.png" width="128">
|
||||||
|
</p>
|
||||||
|
<h1 align="center">
|
||||||
|
<a href="https://wgdashboard.dev">WGDashboard</a>
|
||||||
|
</h1>
|
||||||
|
<p align="center">
|
||||||
|
<img src="https://img.shields.io/badge/Made_With-Python-blue?style=for-the-badge&logo=python&logoColor=ffffff">
|
||||||
|
<img src="https://img.shields.io/badge/Made_With-Vue.js-42b883?style=for-the-badge&logo=vuedotjs&logoColor=ffffff">
|
||||||
|
<img src="https://img.shields.io/badge/License-Apache_License_2.0-D22128?style=for-the-badge&logo=apache&logoColor=ffffff">
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p align="center">
|
||||||
|
<a href="https://github.com/donaldzou/wireguard-dashboard/releases/latest"><img src="https://img.shields.io/github/v/release/donaldzou/wireguard-dashboard?style=for-the-badge"></a>
|
||||||
|
<a href="https://wakatime.com/badge/github/donaldzou/WGDashboard"><img src="https://wakatime.com/badge/github/donaldzou/WGDashboard.svg?style=for-the-badge" alt="wakatime"></a>
|
||||||
|
<a href="https://hitscounter.dev"><img src="https://hitscounter.dev/api/hit?url=https%3A%2F%2Fgithub.com%2Fdonaldzou%2FWGDashboard&label=Visitor&icon=github&color=%230a58ca&style=for-the-badge"></a>
|
||||||
|
<img src="https://img.shields.io/docker/pulls/donaldzou/wgdashboard?logo=docker&label=Docker%20Image%20Pulls&labelColor=ffffff&style=for-the-badge">
|
||||||
|
</p>
|
||||||
|
<p align="center"><b>This project is supported by</b></p>
|
||||||
|
<p align="center">
|
||||||
|
<a href="https://m.do.co/c/a84cb9aac585">
|
||||||
|
<img src="https://opensource.nyc3.cdn.digitaloceanspaces.com/attribution/assets/SVG/DO_Logo_horizontal_blue.svg" width="201px">
|
||||||
|
</a>
|
||||||
|
</p>
|
||||||
|
<p align="center">Monitoring WireGuard is not convenient, in most case, you'll need to login to your server and type <code>wg show</code>. That's why this project is being created, to view and manage all WireGuard configurations in a easy way.</p>
|
||||||
|
<p align="center">With all these awesome features, while keeping it <b>easy to install and use</b></p>
|
||||||
|
|
||||||
|
<p align="center"><b><i>This project is not affiliate to the official WireGuard Project</i></b></p>
|
||||||
|
|
||||||
|
<h3 align="center">Looking for help or want to chat about this project?</h4>
|
||||||
|
<p align="center">
|
||||||
|
You can reach out at
|
||||||
</p>
|
</p>
|
||||||
<p align="center">
|
<p align="center">
|
||||||
<a href="https://github.com/donaldzou/wireguard-dashboard/releases/latest"><img src="https://img.shields.io/github/v/release/donaldzou/wireguard-dashboard"></a>
|
<a align="center" href="https://discord.gg/72TwzjeuWm" target="_blank"><img src="https://img.shields.io/discord/1276818723637956628?labelColor=ffffff&style=for-the-badge&logo=discord&label=Discord"></a>
|
||||||
<a href="https://wakatime.com/badge/github/donaldzou/WGDashboard"><img src="https://wakatime.com/badge/github/donaldzou/WGDashboard.svg" alt="wakatime"></a>
|
<a align="center" href="https://www.reddit.com/r/WGDashboard/" target="_blank"><img src="https://img.shields.io/badge/Reddit-r%2FWGDashboard-FF4500?style=for-the-badge&logo=reddit"></a>
|
||||||
<a href="https://hits.seeyoufarm.com"><img src="https://hits.seeyoufarm.com/api/count/incr/badge.svg?url=https%3A%2F%2Fgithub.com%2Fdonaldzou%2FWGDashboard&count_bg=%2379C83D&title_bg=%23555555&icon=github.svg&icon_color=%23E7E7E7&title=Visitor&edge_flat=false"/></a>
|
<a align="center" href="https://app.element.io/#/room/#wgd:matrix.org" target="_blank"><img src="https://img.shields.io/badge/Matrix_Chatroom-%23WGD-000000?style=for-the-badge&logo=matrix"></a>
|
||||||
|
</p>
|
||||||
|
<h3 align="center">Want to support this project?</h4>
|
||||||
|
<p align="center">
|
||||||
|
You can support via <br>
|
||||||
|
</p>
|
||||||
|
<p align="center">
|
||||||
|
<a align="center" href="https://github.com/sponsors/donaldzou" target="_blank"><img src="https://img.shields.io/badge/GitHub%20Sponsor-2e9a40?style=for-the-badge&logo=github"></a>
|
||||||
|
<a align="center" href="https://buymeacoffee.com/donaldzou" target="_blank"><img src="https://img.shields.io/badge/Buy%20me%20a%20coffee-ffdd00?style=for-the-badge&logo=buymeacoffee&logoColor=000000"></a>
|
||||||
|
<a align="center" href="https://patreon.com/c/DonaldDonnyZou/membership" target="_blank"><img src="https://img.shields.io/badge/Patreon-000000?style=for-the-badge&logo=patreon&logoColor=ffffff"></a>
|
||||||
</p>
|
</p>
|
||||||
<p align="center">Monitoring WireGuard is not convinient, need to login into server and type <code>wg show</code>. That's why this platform is being created, to view all configurations and manage them in a easier way.</p>
|
|
||||||
<p align="center"><small>Note: This project is not affiliate to the official WireGuard Project ;)</small></p>
|
|
||||||
|
|
||||||
## 📣 What's New: v3.0
|
<p align="center">
|
||||||
|
<b>or, visit our merch store and support us by purchasing a merch for only $USD 17.00 (Including shipping worldwide & duties)</b>
|
||||||
- 🎉 **New Features**
|
</p>
|
||||||
- **Moved from TinyDB to SQLite**: SQLite provide a better performance and loading speed when getting peers! Also avoided crashing the database due to **race condition**.
|
<p align="center">
|
||||||
- **Added Gunicorn WSGI Server**: This could provide more stable on handling HTTP request, and more flexibility in the future (such as HTTPS support). **BIG THANKS to @pgalonza :heart:**
|
<a align="center" href="https://merch.wgdashboard.dev" target="_blank"><img src="https://img.shields.io/badge/Merch%20from%20WGDashboard-926183?style=for-the-badge"></a>
|
||||||
- **Add Peers by Bulk:** User can add peers by bulk, just simply set the amount and click add.
|
</p>
|
||||||
- **Delete Peers by Bulk**: User can delete peers by bulk, without deleting peers one by one.
|
|
||||||
- **Download Peers in Zip**: User can download all *downloadable* peers in a zip.
|
|
||||||
- **Added Pre-shared Key to peers:** Now each peer can add with a pre-shared key to enhance security. Previously added peers can add the pre-shared key through the peer setting button.
|
|
||||||
- **Redirect Back to Previous Page:** The dashboard will now redirect you back to your previous page if the current session got timed out and you need to sign in again.
|
|
||||||
- **Added Some [🥘 Experimental Functions](#-experimental-functions)**
|
|
||||||
|
|
||||||
- 🪚 **Bug Fixed**
|
|
||||||
- [IP Sorting range issues #99](https://github.com/donaldzou/WGDashboard/issues/99) [❤️ @barryboom]
|
|
||||||
- [INvalid character written to tunnel json file #108](https://github.com/donaldzou/WGDashboard/issues/108) [❤️ @ikidd]
|
|
||||||
- [Add IPv6 #91](https://github.com/donaldzou/WGDashboard/pull/91) [❤️ @pgalonza]
|
|
||||||
- [Added MTU and PersistentKeepalive to QR code and download files #112](https://github.com/donaldzou/WGDashboard/pull/112) [:heart: @reafian]
|
|
||||||
- **And many other bugs provided by our beloved users** :heart:
|
|
||||||
- **🧐 Other Changes**
|
|
||||||
- **Key generating moved to front-end**: No longer need to use the server's WireGuard to generate keys, thanks to the `wireguard.js` from the [official repository](https://git.zx2c4.com/wireguard-tools/tree/contrib/keygen-html/wireguard.js)!
|
|
||||||
- **Peer transfer calculation**: each peer will now show all transfer amount (previously was only showing transfer amount from the last configuration start-up).
|
|
||||||
- **UI adjustment on running peers**: peers will have a new style indicating that it is running.
|
|
||||||
- **`wgd.sh` finally can update itself**: So now user could update the whole dashboard from `wgd.sh`, with the `update` command.
|
|
||||||
- **Minified JS and CSS files**: Although only a small changes on the file size, but I think is still a good practice to save a bit of bandwidth ;)
|
|
||||||
|
|
||||||
*And many other small changes for performance and bug fixes! :laughing:*
|
|
||||||
|
|
||||||
> If you have any other brilliant ideas for this project, please shout it in here [#129](https://github.com/donaldzou/WGDashboard/issues/129) :heart:
|
|
||||||
|
|
||||||
**For users who is using `v2.x.x` please be sure to read [this](#please-note-for-user-who-is-using-v231-or-below) before updating WGDashboard ;)**
|
|
||||||
|
|
||||||
<hr>
|
<hr>
|
||||||
|
<h4 align="center">
|
||||||
## Table of Content
|
for more information, visit our
|
||||||
|
</h4>
|
||||||
|
<h1 align="center">
|
||||||
- [💡 Features](#-features)
|
<a href="https://wgdashboard.dev">Official Website</a>
|
||||||
- [📝 Requirement](#-requirement)
|
</h1>
|
||||||
- [🛠 Install](#-install)
|
|
||||||
- [🪜 Usage](#-usage)
|
|
||||||
- [Start/Stop/Restart WGDashboard](#startstoprestart-wgdashboard)
|
# Screenshots
|
||||||
- [Autostart WGDashboard on boot](#autostart-wgdashboard-on-boot--v22)
|
|
||||||
- [✂️ Dashboard Configuration](#%EF%B8%8F-dashboard-configuration)
|
<img src="https://wgdashboard-resources.tor1.cdn.digitaloceanspaces.com/Documentation%20Images/sign-in.png" alt=""/>
|
||||||
- [Dashboard Configuration file](#dashboard-configuration-file)
|
<img src="https://wgdashboard-resources.tor1.cdn.digitaloceanspaces.com/Documentation%20Images/cross-server.png" alt=""/>
|
||||||
- [Generating QR code and peer configuration file (.conf)](#generating-qr-code-and-peer-configuration-file-conf)
|
<img src="https://wgdashboard-resources.tor1.cdn.digitaloceanspaces.com/Documentation%20Images/index.png" alt=""/>
|
||||||
- [❓ How to update the dashboard?](#-how-to-update-the-dashboard)
|
<img src="https://wgdashboard-resources.tor1.cdn.digitaloceanspaces.com/Documentation%20Images/new-configuration.png" alt="" />
|
||||||
- [🥘 Experimental Functions](#-experimental-functions)
|
<img src="https://wgdashboard-resources.tor1.cdn.digitaloceanspaces.com/Documentation%20Images/settings.png" alt="" />
|
||||||
- [🔍 Screenshot](#-screenshot)
|
<img src="https://wgdashboard-resources.tor1.cdn.digitaloceanspaces.com/Documentation%20Images/light-dark.png" alt="" />
|
||||||
- [⏰ Changelog](#--changelog)
|
<img src="https://wgdashboard-resources.tor1.cdn.digitaloceanspaces.com/Documentation%20Images/configuration.png" alt=""/>
|
||||||
- [🛒 Dependencies](#-dependencies)
|
<img src="https://wgdashboard-resources.tor1.cdn.digitaloceanspaces.com/Documentation%20Images/add-peers.png" alt="" />
|
||||||
- [✨ Contributors](#-contributors)
|
<img src="https://wgdashboard-resources.tor1.cdn.digitaloceanspaces.com/Documentation%20Images/ping.png" alt=""/>
|
||||||
|
<img src="https://wgdashboard-resources.tor1.cdn.digitaloceanspaces.com/Documentation%20Images/traceroute.png" alt=""/>
|
||||||
## 💡 Features
|
|
||||||
|
|
||||||
- **No need to re-configure existing WireGuard configuration! It can search for existed configuration files.**
|
|
||||||
- Easy to use interface, provided username and password protection to the dashboard
|
|
||||||
- Add peers and edit (Allowed IPs, DNS, Private Key...)
|
|
||||||
- View peers and configuration real time details (Data Usage, Latest Handshakes...)
|
|
||||||
- Share your peer configuration with QR code or file download
|
|
||||||
- Testing tool: Ping and Traceroute to your peer's ip
|
|
||||||
- **And more functions are coming up!**
|
|
||||||
|
|
||||||
|
|
||||||
## 📝 Requirement
|
|
||||||
|
|
||||||
- Recommend the following OS, tested by our beloved users:
|
|
||||||
- [x] Ubuntu 18.04.1 LTS - 20.04.1 LTS [@Me]
|
|
||||||
- [x] Debian GNU/Linux 10 (buster) [❤️ @[robchez](https://github.com/robchez)]
|
|
||||||
- [x] AlmaLinux 8.4 (Electric Cheetah) [❤️ @[barry-smithjr](https://github.com/)]
|
|
||||||
- [x] CentOS 7 [❤️ @[PrzemekSkw](https://github.com/PrzemekSkw)]
|
|
||||||
|
|
||||||
> **If you have tested on other OS and it works perfectly please provide it to me in [#31](https://github.com/donaldzou/wireguard-dashboard/issues/31). Thank you!**
|
|
||||||
|
|
||||||
- **WireGuard** and **WireGuard-Tools (`wg-quick`)** are installed.
|
|
||||||
|
|
||||||
> Don't know how? Check this <a href="https://www.wireguard.com/install/">official documentation</a>
|
|
||||||
- Net Tools (`net-tools`) is installed.
|
|
||||||
- You can verify this by checking if `ifconfig` return a list of network interface. Such as `eth0`
|
|
||||||
- Configuration files under **`/etc/wireguard`**, but please note the following sample
|
|
||||||
|
|
||||||
```ini
|
|
||||||
[Interface]
|
|
||||||
...
|
|
||||||
SaveConfig = true
|
|
||||||
# Need to include this line to allow WireGuard Tool to save your configuration,
|
|
||||||
# or if you just want it to monitor your WireGuard Interface and don't need to
|
|
||||||
# make any changes with the dashboard, you can set it to false.
|
|
||||||
|
|
||||||
[Peer]
|
|
||||||
PublicKey = abcd1234
|
|
||||||
AllowedIPs = 1.2.3.4/32
|
|
||||||
# Must have for each peer
|
|
||||||
```
|
|
||||||
|
|
||||||
- Python 3.7+ & Pip3
|
|
||||||
|
|
||||||
- Browser support CSS3 and ES6
|
|
||||||
|
|
||||||
## 🛠 Install
|
|
||||||
1. Download WGDashboard
|
|
||||||
|
|
||||||
```shell
|
|
||||||
git clone -b v3.0.6 https://github.com/donaldzou/WGDashboard.git wgdashboard
|
|
||||||
|
|
||||||
2. Open the WGDashboard folder
|
|
||||||
|
|
||||||
```shell
|
|
||||||
cd wgdashboard/src
|
|
||||||
```
|
|
||||||
|
|
||||||
3. Install WGDashboard
|
|
||||||
|
|
||||||
```shell
|
|
||||||
sudo chmod u+x wgd.sh
|
|
||||||
sudo ./wgd.sh install
|
|
||||||
```
|
|
||||||
|
|
||||||
4. Give read and execute permission to root of the WireGuard configuration folder, you can change the path if your configuration files are not stored in `/etc/wireguard`
|
|
||||||
|
|
||||||
```shell
|
|
||||||
sudo chmod -R 755 /etc/wireguard
|
|
||||||
```
|
|
||||||
|
|
||||||
5. Run WGDashboard
|
|
||||||
|
|
||||||
```shell
|
|
||||||
./wgd.sh start
|
|
||||||
```
|
|
||||||
|
|
||||||
**Note**:
|
|
||||||
|
|
||||||
> For [`pivpn`](https://github.com/pivpn/pivpn) user, please use `sudo ./wgd.sh start` to run if your current account does not have the permission to run `wg show` and `wg-quick`.
|
|
||||||
|
|
||||||
6. Access dashboard
|
|
||||||
|
|
||||||
Access your server with port `10086` (e.g. http://your_server_ip:10086), using username `admin` and password `admin`. See below how to change port and ip that the dashboard is running with.
|
|
||||||
|
|
||||||
## 🪜 Usage
|
|
||||||
|
|
||||||
#### Start/Stop/Restart WGDashboard
|
|
||||||
|
|
||||||
|
|
||||||
```shell
|
|
||||||
cd wgdashboard/src
|
|
||||||
-----------------------------
|
|
||||||
./wgd.sh start # Start the dashboard in background
|
|
||||||
-----------------------------
|
|
||||||
./wgd.sh debug # Start the dashboard in foreground (debug mode)
|
|
||||||
-----------------------------
|
|
||||||
./wgd.sh stop # Stop the dashboard
|
|
||||||
-----------------------------
|
|
||||||
./wgd.sh restart # Restart the dasboard
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Autostart WGDashboard on boot (>= v2.2)
|
|
||||||
|
|
||||||
In the `src` folder, it contained a file called `wg-dashboard.service`, we can use this file to let our system to autostart the dashboard after reboot. The following guide has tested on **Ubuntu**, most **Debian** based OS might be the same, but some might not. Please don't hesitate to provide your system if you have tested the autostart on another system.
|
|
||||||
|
|
||||||
1. Changing the directory to the dashboard's directory
|
|
||||||
|
|
||||||
```shell
|
|
||||||
cd wgdashboard/src
|
|
||||||
```
|
|
||||||
|
|
||||||
2. Get the full path of the dashboard's directory
|
|
||||||
|
|
||||||
```shell
|
|
||||||
pwd
|
|
||||||
#Output: /root/wgdashboard/src
|
|
||||||
```
|
|
||||||
|
|
||||||
For this example, the output is `/root/wireguard-dashboard/src`, your path might be different since it depends on where you downloaded the dashboard in the first place. **Copy the the output to somewhere, we will need this in the next step.**
|
|
||||||
|
|
||||||
3. Edit the service file, the service file is located in `wireguard-dashboard/src`, you can use other editor you like, here will be using `nano`
|
|
||||||
|
|
||||||
```shell
|
|
||||||
nano wg-dashboard.service
|
|
||||||
```
|
|
||||||
|
|
||||||
You will see something like this:
|
|
||||||
|
|
||||||
```ini
|
|
||||||
[Unit]
|
|
||||||
After=network.service
|
|
||||||
|
|
||||||
[Service]
|
|
||||||
WorkingDirectory=<your dashboard directory full path here>
|
|
||||||
ExecStart=/usr/bin/python3 <your dashboard directory full path here>/dashboard.py
|
|
||||||
Restart=always
|
|
||||||
|
|
||||||
|
|
||||||
[Install]
|
|
||||||
WantedBy=default.target
|
|
||||||
```
|
|
||||||
|
|
||||||
Now, we need to replace both `<your dashboard directory full path here>` to the one you just copied from step 2. After doing this, the file will become something like this, your file might be different:
|
|
||||||
|
|
||||||
```ini
|
|
||||||
[Unit]
|
|
||||||
After=netword.service
|
|
||||||
|
|
||||||
[Service]
|
|
||||||
WorkingDirectory=/root/wgdashboard/src
|
|
||||||
ExecStart=/usr/bin/python3 /root/wgdashboard/src/dashboard.py
|
|
||||||
Restart=always
|
|
||||||
|
|
||||||
|
|
||||||
[Install]
|
|
||||||
WantedBy=default.target
|
|
||||||
```
|
|
||||||
|
|
||||||
**Be aware that after the value of `WorkingDirectory`, it does not have a `/` (slash).** And then save the file after you edited it
|
|
||||||
|
|
||||||
4. Copy the service file to systemd folder
|
|
||||||
|
|
||||||
```bash
|
|
||||||
$ cp wg-dashboard.service /etc/systemd/system/wg-dashboard.service
|
|
||||||
```
|
|
||||||
|
|
||||||
To make sure you copy the file successfully, you can use this command `cat /etc/systemd/system/wg-dashboard.service` to see if it will output the file you just edited.
|
|
||||||
|
|
||||||
5. Enable the service
|
|
||||||
|
|
||||||
```bash
|
|
||||||
$ sudo chmod 664 /etc/systemd/system/wg-dashboard.service
|
|
||||||
$ sudo systemctl daemon-reload
|
|
||||||
$ sudo systemctl enable wg-dashboard.service
|
|
||||||
$ sudo systemctl start wg-dashboard.service # <-- To start the service
|
|
||||||
```
|
|
||||||
|
|
||||||
6. Check if the service run correctly
|
|
||||||
|
|
||||||
```bash
|
|
||||||
$ sudo systemctl status wg-dashboard.service
|
|
||||||
```
|
|
||||||
|
|
||||||
And you should see something like this
|
|
||||||
|
|
||||||
```shell
|
|
||||||
● wg-dashboard.service
|
|
||||||
Loaded: loaded (/etc/systemd/system/wg-dashboard.service; enabled; vendor preset: enabled)
|
|
||||||
Active: active (running) since Tue 2021-08-03 22:31:26 UTC; 4s ago
|
|
||||||
Main PID: 6602 (python3)
|
|
||||||
Tasks: 1 (limit: 453)
|
|
||||||
Memory: 26.1M
|
|
||||||
CGroup: /system.slice/wg-dashboard.service
|
|
||||||
└─6602 /usr/bin/python3 /root/wgdashboard/src/dashboard.py
|
|
||||||
|
|
||||||
Aug 03 22:31:26 ubuntu-wg systemd[1]: Started wg-dashboard.service.
|
|
||||||
Aug 03 22:31:27 ubuntu-wg python3[6602]: * Serving Flask app "WGDashboard" (lazy loading)
|
|
||||||
Aug 03 22:31:27 ubuntu-wg python3[6602]: * Environment: production
|
|
||||||
Aug 03 22:31:27 ubuntu-wg python3[6602]: WARNING: This is a development server. Do not use it in a production deployment.
|
|
||||||
Aug 03 22:31:27 ubuntu-wg python3[6602]: Use a production WSGI server instead.
|
|
||||||
Aug 03 22:31:27 ubuntu-wg python3[6602]: * Debug mode: off
|
|
||||||
Aug 03 22:31:27 ubuntu-wg python3[6602]: * Running on all addresses.
|
|
||||||
Aug 03 22:31:27 ubuntu-wg python3[6602]: WARNING: This is a development server. Do not use it in a production deployment.
|
|
||||||
Aug 03 22:31:27 ubuntu-wg python3[6602]: * Running on http://0.0.0.0:10086/ (Press CTRL+C to quit)
|
|
||||||
```
|
|
||||||
|
|
||||||
If you see `Active:` followed by `active (running) since...` then it means it run correctly.
|
|
||||||
|
|
||||||
7. Stop/Start/Restart the service
|
|
||||||
|
|
||||||
```bash
|
|
||||||
sudo systemctl stop wg-dashboard.service # <-- To stop the service
|
|
||||||
sudo systemctl start wg-dashboard.service # <-- To start the service
|
|
||||||
sudo systemctl restart wg-dashboard.service # <-- To restart the service
|
|
||||||
```
|
|
||||||
|
|
||||||
8. **And now you can reboot your system, and use the command at step 6 to see if it will auto start after the reboot, or just simply access the dashboard through your browser. If you have any questions or problem, please report it in the issue page.**
|
|
||||||
|
|
||||||
## ✂️ Dashboard Configuration
|
|
||||||
|
|
||||||
#### Dashboard Configuration file
|
|
||||||
|
|
||||||
Since version 2.0, WGDashboard will be using a configuration file called `wg-dashboard.ini`, (It will generate automatically after first time running the dashboard). More options will include in future versions, and for now it included the following configurations:
|
|
||||||
|
|
||||||
| | Description | Default | Edit Available |
|
|
||||||
| ---------------------------- | ------------------------------------------------------------ | ---------------------------------------------------- | -------------- |
|
|
||||||
| **`[Account]`** | *Configuration on account* | | |
|
|
||||||
| `username` | Dashboard login username | `admin` | Yes |
|
|
||||||
| `password` | Password, will be hash with SHA256 | `admin` hashed in SHA256 | Yes |
|
|
||||||
| | | | |
|
|
||||||
| **`[Server]`** | *Configuration on dashboard* | | |
|
|
||||||
| `wg_conf_path` | The path of all the Wireguard configurations | `/etc/wireguard` | Yes |
|
|
||||||
| `app_ip` | IP address the dashboard will run with | `0.0.0.0` | Yes |
|
|
||||||
| `app_port` | Port the the dashboard will run with | `10086` | Yes |
|
|
||||||
| `auth_req` | Does the dashboard need authentication to access, if `auth_req = false` , user will not be access the **Setting** tab due to security consideration. **User can only edit the file directly in system**. | `true` | **No** |
|
|
||||||
| `version` | Dashboard Version | `v3.0.6` | **No** |
|
|
||||||
| `dashboard_refresh_interval` | How frequent the dashboard will refresh on the configuration page | `60000ms` | Yes |
|
|
||||||
| `dashboard_sort` | How configuration is sorting | `status` | Yes |
|
|
||||||
| | | | |
|
|
||||||
| **`[Peers]`** | *Default Settings on a new peer* | | |
|
|
||||||
| `peer_global_dns` | DNS Server | `1.1.1.1` | Yes |
|
|
||||||
| `peer_endpoint_allowed_ip` | Endpoint Allowed IP | `0.0.0.0/0` | Yes |
|
|
||||||
| `peer_display_mode` | How peer will display | `grid` | Yes |
|
|
||||||
| `remote_endpoint` | Remote Endpoint (i.e where your peers will connect to) | *depends on your server's default network interface* | Yes |
|
|
||||||
| `peer_mtu` | Maximum Transmit Unit | `1420` | |
|
|
||||||
| `peer_keep_alive` | Keep Alive | `21` | Yes |
|
|
||||||
|
|
||||||
#### Generating QR code and peer configuration file (.conf)
|
|
||||||
|
|
||||||
Starting version 2.2, dashboard can now generate QR code and configuration file for each peer. Here is a template of what each QR code encoded with and the same content will be inside the file:
|
|
||||||
|
|
||||||
```ini
|
|
||||||
[Interface]
|
|
||||||
PrivateKey = QWERTYUIOPO234567890YUSDAKFH10E1B12JE129U21=
|
|
||||||
Address = 0.0.0.0/32
|
|
||||||
DNS = 1.1.1.1
|
|
||||||
|
|
||||||
[Peer]
|
|
||||||
PublicKey = QWERTYUIOPO234567890YUSDAKFH10E1B12JE129U21=
|
|
||||||
AllowedIPs = 0.0.0.0/0
|
|
||||||
Endpoint = 0.0.0.0:51820
|
|
||||||
```
|
|
||||||
|
|
||||||
| | Description | Default Value | Available in Peer setting |
|
|
||||||
| ----------------- | ------------------------------------------------------------ | ------------------------------------------------------------ | ------------------------- |
|
|
||||||
| **`[Interface]`** | | | |
|
|
||||||
| `PrivateKey` | The private key of this peer | Private key generated by WireGuard (`wg genkey`) or provided by user | Yes |
|
|
||||||
| `Address` | The `allowed_ips` of your peer | N/A | Yes |
|
|
||||||
| `DNS` | The DNS server your peer will use | `1.1.1.1` - Cloud flare DNS, you can change it when you adding the peer or in the peer setting. | Yes |
|
|
||||||
| **`[Peer]`** | | | |
|
|
||||||
| `PublicKey` | The public key of your server | N/A | No |
|
|
||||||
| `AllowedIPs` | IP ranges for which a peer will route traffic | `0.0.0.0/0` - Indicated a default route to send all internet and VPN traffic through that peer. | Yes |
|
|
||||||
| `Endpoint` | Your wireguard server ip and port, the dashboard will search for your server's default interface's ip. | `<your server default interface ip>:<listen port>` | Yes |
|
|
||||||
|
|
||||||
## ❓ How to update the dashboard?
|
|
||||||
|
|
||||||
#### **Please note for user who is using `v2.3.1` or below**
|
|
||||||
|
|
||||||
- For user who is using `v2.3.1` or below, please notice that all data that stored in the current database will **not** transfer to the new database. This is hard decision to move from TinyDB to SQLite. But SQLite does provide a thread-safe access and TinyDB doesn't. I couldn't find a safe way to transfer the data, so you need to do them manually... Sorry about that :pensive: . But I guess this would be a great start for future development :sunglasses:.
|
|
||||||
|
|
||||||
<hr>
|
|
||||||
|
|
||||||
#### Update Method 1 (For `v3.0` or above)
|
|
||||||
|
|
||||||
1. Change your directory to `wgdashboard/src`
|
|
||||||
|
|
||||||
```bash
|
|
||||||
cd wgdashboard/src
|
|
||||||
```
|
|
||||||
|
|
||||||
2. Update the dashboard with the following
|
|
||||||
|
|
||||||
```bash
|
|
||||||
./wgd.sh update
|
|
||||||
chmod +x ./wgd.sh
|
|
||||||
```
|
|
||||||
|
|
||||||
> If this doesn't work, please use the method below. Sorry about that :(
|
|
||||||
|
|
||||||
#### Update Method 2
|
|
||||||
|
|
||||||
|
|
||||||
1. Change your directory to `wgdashboard`
|
|
||||||
|
|
||||||
```shell
|
|
||||||
cd wgdashboard/src
|
|
||||||
```
|
|
||||||
|
|
||||||
2. Update the dashboard
|
|
||||||
```shell
|
|
||||||
git pull https://github.com/donaldzou/WGDashboard.git v3.0.6 --force
|
|
||||||
```
|
|
||||||
|
|
||||||
3. Install
|
|
||||||
|
|
||||||
```shell
|
|
||||||
./wgd.sh install
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Starting with `v3.0`, you can simply do `./wgd.sh update` !! (I hope, lol)
|
|
||||||
|
|
||||||
## 🥘 Experimental Functions
|
|
||||||
|
|
||||||
#### Progressive Web App (PWA) for WGDashboard
|
|
||||||
|
|
||||||
- With `v3.0`, I've added a `manifest.json` into the dashboard, so user could add their dashboard as a PWA to their browser or mobile device.
|
|
||||||
|
|
||||||
<img src="img/PWA.gif"/>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
## 🔍 Screenshot
|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||

|
|
||||||
|
|
||||||
## ⏰ Changelog
|
|
||||||
|
|
||||||
#### v3.0.6 - Mar 22, 2022
|
|
||||||
|
|
||||||
**Fixed Bug**
|
|
||||||
- When wgdashboard is running behind a proxy server, redirecting could cause using http while proxy is using https [❤️ from #161]
|
|
||||||
|
|
||||||
#### v3.0.5 - Jan 31, 2022
|
|
||||||
|
|
||||||
**Quick Fix**
|
|
||||||
- Fixed public key does not match when user used an existing private key
|
|
||||||
- Sorry for the wrong version number that causing the dashboard ask for update after updating.
|
|
||||||
|
|
||||||
#### v3.0.3 - Jan 23, 2022
|
|
||||||
|
|
||||||
- Fixed when dashboard configuration file cannot be found after a fresh install. [❤️ from #132 ]
|
|
||||||
|
|
||||||
#### v3.0 - Jan 18, 2022
|
|
||||||
|
|
||||||
- 🎉 **New Features**
|
|
||||||
- **Moved from TinyDB to SQLite**: SQLite provide a better performance and loading speed when getting peers! Also avoided crashing the database due to **race condition**.
|
|
||||||
- **Added Gunicorn WSGI Server**: This could provide more stable on handling HTTP request, and more flexibility in the future (such as HTTPS support). **BIG THANKS to @pgalonza :heart:**
|
|
||||||
- **Add Peers by Bulk:** User can add peers by bulk, just simply set the amount and click add.
|
|
||||||
- **Delete Peers by Bulk**: User can delete peers by bulk, without deleting peers one by one.
|
|
||||||
- **Download Peers in Zip**: User can download all *downloadable* peers in a zip.
|
|
||||||
- **Added Pre-shared Key to peers:** Now each peer can add with a pre-shared key to enhance security. Previously added peers can add the pre-shared key through the peer setting button.
|
|
||||||
- **Redirect Back to Previous Page:** The dashboard will now redirect you back to your previous page if the current session got timed out and you need to sign in again.
|
|
||||||
- **Added Some [🥘 Experimental Functions](https://github.com/donaldzou/WGDashboard#-experimental-functions)**
|
|
||||||
|
|
||||||
- 🪚 **Bug Fixed**
|
|
||||||
- [IP Sorting range issues #99](https://github.com/donaldzou/WGDashboard/issues/99) [❤️ @barryboom]
|
|
||||||
- [INvalid character written to tunnel json file #108](https://github.com/donaldzou/WGDashboard/issues/108) [❤️ @ikidd]
|
|
||||||
- [Add IPv6 #91](https://github.com/donaldzou/WGDashboard/pull/91) [❤️ @pgalonza]
|
|
||||||
- [Added MTU and PersistentKeepalive to QR code and download files #112](https://github.com/donaldzou/WGDashboard/pull/112) [:heart: @reafian]
|
|
||||||
- **And many other bugs provided by our beloved users** :heart:
|
|
||||||
- **🧐 Other Changes**
|
|
||||||
- **Key generating moved to front-end**: No longer need to use the server's WireGuard to generate keys, thanks to the `wireguard.js` from the [official repository](https://git.zx2c4.com/wireguard-tools/tree/contrib/keygen-html/wireguard.js)!
|
|
||||||
- **Peer transfer calculation**: each peer will now show all transfer amount (previously was only showing transfer amount from the last configuration start-up).
|
|
||||||
- **UI adjustment on running peers**: peers will have a new style indicating that it is running.
|
|
||||||
- **`wgd.sh` finally can update itself**: So now user could update the whole dashboard from `wgd.sh`, with the `update` command.
|
|
||||||
- **Minified JS and CSS files**: Although only a small changes on the file size, but I think is still a good practice to save a bit of bandwidth ;)
|
|
||||||
|
|
||||||
*And many other small changes for performance and bug fixes! :laughing:*
|
|
||||||
|
|
||||||
> If you have any other brilliant ideas for this project, please shout it in here [#129](https://github.com/donaldzou/WGDashboard/issues/129) :heart:
|
|
||||||
|
|
||||||
**For users who is using `v2.x.x` please be sure to read [this](https://github.com/donaldzou/WGDashboard#please-note-for-user-who-is-using-v231-or-below) before updating WGDashboard ;)**
|
|
||||||
|
|
||||||
#### v2.3.1 - Sep 8, 2021
|
|
||||||
|
|
||||||
- Updated dashboard's name to **WGDashboard**!!
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#### v2.3 - Sep 8, 2021
|
|
||||||
|
|
||||||
- 🎉 **New Features**
|
|
||||||
- **Update directly from `wgd.sh`:** Now you can update WGDashboard directly from the bash script.
|
|
||||||
- **Displaying Peers:** You can switch the display mode between list and table in the configuration page.
|
|
||||||
- 🪚 **Bug Fixed**
|
|
||||||
- [Peer DNS Validation Fails #67](issues/67): Added DNS format check. [❤️ @realfian]
|
|
||||||
- [configparser.NoSectionError: No section: 'Interface' #66](issues/66): Changed permission requirement for `etc/wireguard` from `744` to `755`. [❤️ @ramalmaty]
|
|
||||||
- [Feature request: Interface not loading when information missing #73](issues/73): Fixed when Configuration Address and Listen Port is missing will crash the dashboard. [❤️ @js32]
|
|
||||||
- [Remote Peer, MTU and PersistentKeepalives added #70](pull/70): Added MTU, remote peer and Persistent Keepalive. [❤️ @realfian]
|
|
||||||
- [Fixes DNS check to support search domain #65](pull/65): Added allow input domain into DNS. [❤️@davejlong]
|
|
||||||
- **🧐 Other Changes**
|
|
||||||
- Moved Add Peer Button into the right bottom corner.
|
|
||||||
|
|
||||||
#### v2.2.1 - Aug 16, 2021
|
|
||||||
|
|
||||||
Bug Fixed:
|
|
||||||
- Added support for full subnet on Allowed IP
|
|
||||||
- Peer setting Save button
|
|
||||||
|
|
||||||
#### v2.2 - Aug 14, 2021
|
|
||||||
|
|
||||||
- 🎉 **New Features**
|
|
||||||
- **Add new peers**: Now you can add peers directly on dashboard, it will generate a pair of private key and public key. You can also set its DNS, endpoint allowed IPs. Both can set a default value in the setting page. [❤️ in [#44](https://github.com/donaldzou/wireguard-dashboard/issues/44)]
|
|
||||||
- **QR Code:** You can add the private key in peer setting of your existed peer to create a QR code. Or just create a new one, dashboard will now be able to auto generate a private key and public key ;) Don't worry, all keys will be generated on your machine, and **will delete all key files after they got generated**. [❤️ in [#29](https://github.com/donaldzou/wireguard-dashboard/issues/29)]
|
|
||||||
- **Peer configuration file download:** Same as QR code, you now can download the peer configuration file, so you don't need to manually input all the details on the peer machine! [❤️ in [#40](https://github.com/donaldzou/wireguard-dashboard/issues/40)]
|
|
||||||
- **Search peers**: You can now search peers by their name.
|
|
||||||
- **Autostart on boot:** Added a tutorial on how to start the dashboard to on boot! Please read the [tutorial below](#autostart-wireguard-dashboard-on-boot). [❤️ in [#29](https://github.com/donaldzou/wireguard-dashboard/issues/29)]
|
|
||||||
- **Click to copy**: You can now click and copy all peer's public key and configuration's public key.
|
|
||||||
- ....
|
|
||||||
- 🪚 **Bug Fixed**
|
|
||||||
- When there are comments in the wireguard config file, will cause the dashboard to crash.
|
|
||||||
- Used regex to search for config files.
|
|
||||||
- **🧐 Other Changes**
|
|
||||||
- Moved all external CSS and JavaScript file to local hosting (Except Bootstrap Icon, due to large amount of SVG files).
|
|
||||||
- Updated Python dependencies
|
|
||||||
- Flask: `v1.1.2 => v2.0.1`
|
|
||||||
- Jinja: `v2.10.1 => v3.0.1`
|
|
||||||
- icmplib: `v2.1.1 => v3.0.1`
|
|
||||||
- Updated CSS/JS dependencies
|
|
||||||
- Bootstrap: `v4.5.3 => v4.6.0`
|
|
||||||
- UI adjustment
|
|
||||||
- Adjusted how peers will display in larger screens, used to be 1 row per peer, now is 3 peers in 1 row.
|
|
||||||
|
|
||||||
#### v2.1 - Jul 2, 2021
|
|
||||||
|
|
||||||
- Added **Ping** and **Traceroute** tools!
|
|
||||||
- Adjusted the calculation of data usage on each peers
|
|
||||||
- Added refresh interval of the dashboard
|
|
||||||
- Bug fixed when no configuration on fresh install ([#23](https://github.com/donaldzou/wireguard-dashboard/issues/23))
|
|
||||||
- Fixed crash when too many peers ([#22](https://github.com/donaldzou/wireguard-dashboard/issues/22))
|
|
||||||
|
|
||||||
#### v2.0 - May 5, 2021
|
|
||||||
|
|
||||||
- Added login function to dashboard
|
|
||||||
- ***I'm not using the most ideal way to store the username and password, feel free to provide a better way to do this if you any good idea!***
|
|
||||||
- Added a config file to the dashboard
|
|
||||||
- Dashboard config can be change within the **Setting** tab on the side bar
|
|
||||||
- Adjusted UI
|
|
||||||
- And much more!
|
|
||||||
|
|
||||||
#### v1.1.2 - Apr 3, 2021
|
|
||||||
|
|
||||||
- Resolved issue [#3](https://github.com/donaldzou/wireguard-dashboard/issues/3).
|
|
||||||
|
|
||||||
#### v1.1.1 - Apr 2, 2021
|
|
||||||
|
|
||||||
- Able to add a friendly name to each peer. Thanks [#2](https://github.com/donaldzou/wireguard-dashboard/issues/2) !
|
|
||||||
|
|
||||||
#### v1.0 - Dec 27, 2020
|
|
||||||
|
|
||||||
- Added the function to remove peers
|
|
||||||
|
|
||||||
## 🛒 Dependencies
|
|
||||||
|
|
||||||
- CSS/JS
|
|
||||||
- [Bootstrap](https://getbootstrap.com/docs/4.6/getting-started/introduction/) `v4.6.0`
|
|
||||||
- [Bootstrap Icon](https://icons.getbootstrap.com) `v1.4.0`
|
|
||||||
- [jQuery](https://jquery.com) `v3.5.1`
|
|
||||||
- Python
|
|
||||||
- [Flask](https://pypi.org/project/Flask/) `v2.0.1`
|
|
||||||
- [ifcfg](https://pypi.org/project/ifcfg/) `v0.24`
|
|
||||||
- [psutil](https://pypi.org/project/psutil/) `v5.9.8`
|
|
||||||
- [icmplib](https://pypi.org/project/icmplib/) `v2.1.1`
|
|
||||||
- [flask-qrcode](https://pypi.org/project/Flask-QRcode/) `v3.0.0`
|
|
||||||
|
|
||||||
## ✨ Contributors
|
|
||||||
|
|
||||||
<!-- ALL-CONTRIBUTORS-BADGE:START - Do not remove or modify this section -->
|
|
||||||
[](#contributors-)
|
|
||||||
<!-- ALL-CONTRIBUTORS-BADGE:END -->
|
|
||||||
|
|
||||||
Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/docs/en/emoji-key)):
|
|
||||||
|
|
||||||
<!-- ALL-CONTRIBUTORS-LIST:START - Do not remove or modify this section -->
|
|
||||||
<!-- prettier-ignore-start -->
|
|
||||||
<!-- markdownlint-disable -->
|
|
||||||
<table>
|
|
||||||
<tr>
|
|
||||||
<td align="center"><a href="https://github.com/antonioag95"><img src="https://avatars.githubusercontent.com/u/30556866?v=4?s=100" width="100px;" alt=""/><br /><sub><b>antonioag95</b></sub></a><br /><a href="https://github.com/donaldzou/WGDashboard/commits?author=antonioag95" title="Tests">⚠️</a> <a href="https://github.com/donaldzou/WGDashboard/commits?author=antonioag95" title="Code">💻</a></td>
|
|
||||||
<td align="center"><a href="https://github.com/tonjo"><img src="https://avatars.githubusercontent.com/u/4726289?v=4?s=100" width="100px;" alt=""/><br /><sub><b>tonjo</b></sub></a><br /><a href="https://github.com/donaldzou/WGDashboard/commits?author=tonjo" title="Code">💻</a></td>
|
|
||||||
<td align="center"><a href="https://github.com/reafian"><img src="https://avatars.githubusercontent.com/u/11992416?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Richard Newton</b></sub></a><br /><a href="https://github.com/donaldzou/WGDashboard/commits?author=reafian" title="Code">💻</a></td>
|
|
||||||
<td align="center"><a href="http://www.davejlong.com"><img src="https://avatars.githubusercontent.com/u/175317?v=4?s=100" width="100px;" alt=""/><br /><sub><b>David Long</b></sub></a><br /><a href="https://github.com/donaldzou/WGDashboard/commits?author=davejlong" title="Code">💻</a></td>
|
|
||||||
<td align="center"><a href="http://www.std-soft.com"><img src="https://avatars.githubusercontent.com/u/5978293?v=4?s=100" width="100px;" alt=""/><br /><sub><b>Markus Neubauer</b></sub></a><br /><a href="https://github.com/donaldzou/WGDashboard/commits?author=marneu" title="Code">💻</a></td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
|
|
||||||
<!-- markdownlint-restore -->
|
|
||||||
<!-- prettier-ignore-end -->
|
|
||||||
|
|
||||||
<!-- ALL-CONTRIBUTORS-LIST:END -->
|
|
||||||
|
|
||||||
This project follows the [all-contributors](https://github.com/all-contributors/all-contributors) specification. Contributions of any kind welcome!
|
|
||||||
|
|
||||||
|
70
docker/Dockerfile
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
FROM golang:1.24 AS awg
|
||||||
|
|
||||||
|
RUN git clone https://github.com/amnezia-vpn/amneziawg-go /awg
|
||||||
|
WORKDIR /awg
|
||||||
|
RUN go mod download && \
|
||||||
|
go mod verify && \
|
||||||
|
go build -ldflags '-linkmode external -extldflags "-fno-PIC -static"' -v -o /usr/bin
|
||||||
|
|
||||||
|
FROM alpine:latest
|
||||||
|
LABEL maintainer="dselen@nerthus.nl"
|
||||||
|
|
||||||
|
RUN apk update && apk add \
|
||||||
|
iproute2 iptables bash curl wget unzip procps sudo \
|
||||||
|
tzdata wireguard-tools python3 py3-psutil py3-bcrypt openresolv \
|
||||||
|
&& cd /usr/bin/ \
|
||||||
|
&& wget $(curl -s https://api.github.com/repos/amnezia-vpn/amneziawg-tools/releases/latest | grep 'alpine' | cut -d : -f 2,3 | tr -d '", ' | tail -n 1) \
|
||||||
|
&& unzip -j alpine-3.19-amneziawg-tools.zip \
|
||||||
|
&& chmod +x /usr/bin/awg /usr/bin/awg-quick \
|
||||||
|
&& rm alpine-3.19-amneziawg-tools.zip
|
||||||
|
|
||||||
|
COPY --from=awg /usr/bin/amneziawg-go /usr/bin/amneziawg-go
|
||||||
|
|
||||||
|
# Declaring environment variables, change Peernet to an address you like, standard is a 24 bit subnet.
|
||||||
|
ARG wg_net="10.0.0.1" \
|
||||||
|
wg_port="51820"
|
||||||
|
|
||||||
|
# Following ENV variables are changable on container runtime because /entrypoint.sh handles that. See compose.yaml for more info.
|
||||||
|
ENV TZ="Europe/Amsterdam" \
|
||||||
|
global_dns="9.9.9.9" \
|
||||||
|
wgd_port="10086" \
|
||||||
|
public_ip=""
|
||||||
|
|
||||||
|
# Using WGDASH -- like wg_net functionally as a ARG command. But it is needed in entrypoint.sh so it needs to be exported as environment variable.
|
||||||
|
ENV WGDASH=/opt/wgdashboard
|
||||||
|
|
||||||
|
# Doing WireGuard Dashboard installation measures. Modify the git clone command to get the preferred version, with a specific branch for example.
|
||||||
|
RUN mkdir /data \
|
||||||
|
&& mkdir /configs \
|
||||||
|
&& mkdir -p ${WGDASH}/src \
|
||||||
|
&& mkdir -p /etc/amnezia/amneziawg
|
||||||
|
COPY ./src ${WGDASH}/src
|
||||||
|
|
||||||
|
# Generate basic WireGuard interface. Echoing the WireGuard interface config for readability, adjust if you want it for efficiency.
|
||||||
|
# Also setting the pipefail option, verbose: https://github.com/hadolint/hadolint/wiki/DL4006.
|
||||||
|
SHELL ["/bin/bash", "-o", "pipefail", "-c"]
|
||||||
|
RUN out_adapt=$(ip -o -4 route show to default | awk '{print $NF}') \
|
||||||
|
&& echo -e "[Interface]\n\
|
||||||
|
Address = ${wg_net}/24\n\
|
||||||
|
PrivateKey =\n\
|
||||||
|
PostUp = iptables -t nat -I POSTROUTING 1 -s ${wg_net}/24 -o ${out_adapt} -j MASQUERADE\n\
|
||||||
|
PostUp = iptables -I FORWARD -i wg0 -o wg0 -j DROP\n\
|
||||||
|
PreDown = iptables -t nat -D POSTROUTING -s ${wg_net}/24 -o ${out_adapt} -j MASQUERADE\n\
|
||||||
|
PreDown = iptables -D FORWARD -i wg0 -o wg0 -j DROP\n\
|
||||||
|
ListenPort = ${wg_port}\n\
|
||||||
|
SaveConfig = true\n\
|
||||||
|
DNS = ${global_dns}" > /configs/wg0.conf.template \
|
||||||
|
&& chmod 600 /configs/wg0.conf.template
|
||||||
|
|
||||||
|
# Defining a way for Docker to check the health of the container. In this case: checking the gunicorn process.
|
||||||
|
HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
|
||||||
|
CMD sh -c 'pgrep gunicorn > /dev/null && pgrep tail > /dev/null' || exit 1
|
||||||
|
|
||||||
|
# Copy the basic entrypoint.sh script.
|
||||||
|
COPY ./docker/entrypoint.sh /entrypoint.sh
|
||||||
|
|
||||||
|
# Exposing the default WireGuard Dashboard port for web access.
|
||||||
|
EXPOSE 10086
|
||||||
|
WORKDIR $WGDASH
|
||||||
|
|
||||||
|
ENTRYPOINT ["/bin/bash", "/entrypoint.sh"]
|
197
docker/README.md
Normal file
@ -0,0 +1,197 @@
|
|||||||
|
# WGDashboard Docker Explanation:
|
||||||
|
Author: @DaanSelen<br>
|
||||||
|
|
||||||
|
This document delves into how the WGDashboard Docker container has been built.<br>
|
||||||
|
Of course there are two stages (simply said), one before run-time and one at/after run-time.<br>
|
||||||
|
The `Dockerfile` describes how the container image is made, and the `entrypoint.sh` is executed after running the container. <br>
|
||||||
|
In this example, WireGuard is integrated into the container itself, so it should be a run-and-go(/out-of-the-box).<br>
|
||||||
|
For more details on the source-code specific to this Docker image, refer to the source files, they have lots of comments.
|
||||||
|
|
||||||
|
<br>
|
||||||
|
<img
|
||||||
|
src="https://wgdashboard-resources.tor1.cdn.digitaloceanspaces.com/Logos/Logo-2-Rounded-512x512.png"
|
||||||
|
alt="WG-Dashboard Logo"
|
||||||
|
title="WG-Dashboard Logo"
|
||||||
|
style="display: block; margin: 0 auto;"
|
||||||
|
width="150"
|
||||||
|
height="150"
|
||||||
|
/>
|
||||||
|
<br>
|
||||||
|
|
||||||
|
To get the container running you either pull the image from the repository, (docker.io)`donaldzou/wgdashboard:latest`.<br>
|
||||||
|
From there either use the environment variables describe below as parameters or use the Docker Compose file: `compose.yaml`.<br>
|
||||||
|
Be careful, the default generated WireGuard configuration file uses port 51820/udp. So use this port if you want to use it out of the box.<br>
|
||||||
|
Otherwise edit the configuration file in `/etc/wireguard/wg0.conf`.
|
||||||
|
|
||||||
|
# WGDashboard: 🐳 Docker Deployment Guide
|
||||||
|
|
||||||
|
To run the container, you can either pull the image from Docker Hub or build it yourself. The image is available at:
|
||||||
|
|
||||||
|
```
|
||||||
|
docker.io/donaldzou/wgdashboard:latest
|
||||||
|
```
|
||||||
|
|
||||||
|
> `docker.io` is in most cases automatically resolved by the Docker application.
|
||||||
|
|
||||||
|
### 🔧 Quick Docker Run Command
|
||||||
|
|
||||||
|
Here's an example to get it up and running quickly:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
docker run -d \
|
||||||
|
--name wgdashboard \
|
||||||
|
--restart unless-stopped \
|
||||||
|
-p 10086:10086/tcp \
|
||||||
|
-p 51820:51820/udp \
|
||||||
|
--cap-add NET_ADMIN \
|
||||||
|
donaldzou/wgdashboard:latest
|
||||||
|
```
|
||||||
|
|
||||||
|
> ⚠️ The default WireGuard port is `51820/udp`. If you change this, update the `/etc/wireguard/wg0.conf` accordingly.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### 📦 Docker Compose Alternative
|
||||||
|
|
||||||
|
You can also use Docker Compose for easier configuration:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
services:
|
||||||
|
wgdashboard:
|
||||||
|
image: donaldzou/wgdashboard:latest
|
||||||
|
restart: unless-stopped
|
||||||
|
container_name: wgdashboard
|
||||||
|
environment:
|
||||||
|
# - tz=Europe/Amsterdam
|
||||||
|
# - global_dns=1.1.1.1
|
||||||
|
# - public_ip=YOUR_PUBLIC_IP
|
||||||
|
ports:
|
||||||
|
- 10086:10086/tcp
|
||||||
|
- 51820:51820/udp
|
||||||
|
volumes:
|
||||||
|
- conf:/etc/wireguard
|
||||||
|
- data:/data
|
||||||
|
cap_add:
|
||||||
|
- NET_ADMIN
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
conf:
|
||||||
|
data:
|
||||||
|
```
|
||||||
|
|
||||||
|
> 📁 You can customize the **volume paths** on the host to fit your needs. The example above uses Docker volumes.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔄 Updating the Container
|
||||||
|
|
||||||
|
Updating WGDashboard is currently in **alpha** stage. While the update process may work, it's still under testing.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ⚙️ Environment Variables
|
||||||
|
|
||||||
|
| Variable | Accepted Values | Default | Example | Description |
|
||||||
|
|---------------|------------------------------------------|-------------------------|------------------------|-----------------------------------------------------------------------------|
|
||||||
|
| `tz` | Timezone | `Europe/Amsterdam` | `America/New_York` | Sets the container's timezone. Useful for accurate logs and scheduling. |
|
||||||
|
| `global_dns` | IPv4 and IPv6 addresses | `9.9.9.9` | `8.8.8.8`, `1.1.1.1` | Default DNS for WireGuard clients. |
|
||||||
|
| `public_ip` | Public IP address | Retrieved automatically | `253.162.134.73` | Used to generate accurate client configs. Needed if container is NAT’d. |
|
||||||
|
| `wgd_port` | Any port that is allowed for the process | `10086` | `443` | This port is used to set the WGDashboard web port. |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🔐 Port Forwarding Note
|
||||||
|
|
||||||
|
When using multiple WireGuard interfaces, remember to **open their respective ports** on the host.
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
```yaml
|
||||||
|
# Individual mapping
|
||||||
|
- 51821:51821/udp
|
||||||
|
|
||||||
|
# Or port range
|
||||||
|
- 51820-51830:51820-51830/udp
|
||||||
|
```
|
||||||
|
|
||||||
|
> 🚨 **Security Tip:** Only expose ports you actually use.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🛠️ Building the Image Yourself
|
||||||
|
|
||||||
|
To build from source:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git clone https://github.com/donaldzou/WGDashboard.git
|
||||||
|
cd WGDashboard
|
||||||
|
docker build . -f docker/Dockerfile -t yourname/wgdashboard:latest
|
||||||
|
```
|
||||||
|
|
||||||
|
Example output:
|
||||||
|
```shell
|
||||||
|
docker images
|
||||||
|
|
||||||
|
REPOSITORY TAG IMAGE ID CREATED SIZE
|
||||||
|
yourname/wgdashboard latest c96fd96ee3b3 42 minutes ago 314MB
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🧱 Dockerfile Overview
|
||||||
|
|
||||||
|
Here's a brief overview of the Dockerfile stages used in the image build:
|
||||||
|
|
||||||
|
### 1. **Build Tools & Go Compilation**
|
||||||
|
|
||||||
|
```Dockerfile
|
||||||
|
FROM golang:1.24 AS compiler
|
||||||
|
WORKDIR /go
|
||||||
|
|
||||||
|
RUN apt-get update && apt-get install -y ...
|
||||||
|
RUN git clone ... && make
|
||||||
|
...
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. **Binary Copy to Scratch**
|
||||||
|
|
||||||
|
```Dockerfile
|
||||||
|
FROM scratch AS bins
|
||||||
|
COPY --from=compiler /go/amneziawg-go/amneziawg-go /amneziawg-go
|
||||||
|
...
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. **Final Alpine Container Setup**
|
||||||
|
|
||||||
|
```Dockerfile
|
||||||
|
FROM alpine:latest
|
||||||
|
COPY --from=bins ...
|
||||||
|
RUN apk update && apk add --no-cache ...
|
||||||
|
COPY ./src ${WGDASH}/src
|
||||||
|
COPY ./docker/entrypoint.sh /entrypoint.sh
|
||||||
|
...
|
||||||
|
EXPOSE 10086
|
||||||
|
ENTRYPOINT ["/bin/bash", "/entrypoint.sh"]
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 🚀 Entrypoint Overview
|
||||||
|
|
||||||
|
### Major Functions:
|
||||||
|
|
||||||
|
- **`ensure_installation`**: Sets up the app, database, and Python environment.
|
||||||
|
- **`set_envvars`**: Writes `wg-dashboard.ini` and applies environment variables.
|
||||||
|
- **`start_core`**: Starts the main WGDashboard service.
|
||||||
|
- **`ensure_blocking`**: Tails the error log to keep the container process alive.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## ✅ Final Notes
|
||||||
|
|
||||||
|
- Use `docker logs wgdashboard` for troubleshooting.
|
||||||
|
- Access the web interface via `http://your-ip:10086` (or whichever port you specified in the compose).
|
||||||
|
- The first time run will auto-generate WireGuard keys and configs (configs are generated from the template).
|
||||||
|
|
||||||
|
## Closing remarks:
|
||||||
|
|
||||||
|
For feedback please submit an issue to the repository. Or message dselen@nerthus.nl.
|
23
docker/compose.yaml
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
services:
|
||||||
|
wireguard-dashboard:
|
||||||
|
image: donaldzou/wgdashboard:latest
|
||||||
|
restart: unless-stopped
|
||||||
|
container_name: wgdashboard
|
||||||
|
#environment:
|
||||||
|
#- tz= # <--- Set container timezone, default: Europe/Amsterdam.
|
||||||
|
#- public_ip= # <--- Set public IP to ensure the correct one is chosen, defaulting to the IP give by ifconfig.me.
|
||||||
|
#- wgd_port= # <--- Set the port WGDashboard will use for its web-server.
|
||||||
|
ports:
|
||||||
|
- 10086:10086/tcp
|
||||||
|
- 51820:51820/udp
|
||||||
|
volumes:
|
||||||
|
- aconf:/etc/amnezia/amneziawg
|
||||||
|
- conf:/etc/wireguard
|
||||||
|
- data:/data
|
||||||
|
cap_add:
|
||||||
|
- NET_ADMIN
|
||||||
|
|
||||||
|
volumes:
|
||||||
|
aconf:
|
||||||
|
conf:
|
||||||
|
data:
|
189
docker/entrypoint.sh
Normal file
@ -0,0 +1,189 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# Path to the configuration file (exists because of previous function).
|
||||||
|
config_file="/data/wg-dashboard.ini"
|
||||||
|
|
||||||
|
trap 'stop_service' SIGTERM
|
||||||
|
|
||||||
|
stop_service() {
|
||||||
|
echo "[WGDashboard] Stopping WGDashboard..."
|
||||||
|
/bin/bash ./wgd.sh stop
|
||||||
|
exit 0
|
||||||
|
}
|
||||||
|
|
||||||
|
echo "------------------------- START ----------------------------"
|
||||||
|
echo "Starting the WireGuard Dashboard Docker container."
|
||||||
|
|
||||||
|
ensure_installation() {
|
||||||
|
# When using a custom directory to store the files, this part moves over and makes sure the installation continues.
|
||||||
|
echo "Quick-installing..."
|
||||||
|
|
||||||
|
# Make the wgd.sh script executable.
|
||||||
|
chmod +x "${WGDASH}"/src/wgd.sh
|
||||||
|
cd "${WGDASH}"/src || exit
|
||||||
|
|
||||||
|
# Github issue: https://github.com/donaldzou/WGDashboard/issues/723
|
||||||
|
echo "Checking for stale pids..."
|
||||||
|
if [[ -f ${WGDASH}/src/gunicorn.pid ]]; then
|
||||||
|
echo "Found stale pid, removing..."
|
||||||
|
rm ${WGDASH}/src/gunicorn.pid
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Removing clear shell command from the wgd.sh script to enhance docker logging.
|
||||||
|
echo "Removing clear command from wgd.sh for better Docker logging."
|
||||||
|
sed -i '/clear/d' ./wgd.sh
|
||||||
|
|
||||||
|
# Create the databases directory if it does not exist yet.
|
||||||
|
if [ ! -d "/data/db" ]; then
|
||||||
|
echo "Creating database dir"
|
||||||
|
mkdir /data/db
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Linking the database on the persistent directory location to where WGDashboard expects.
|
||||||
|
if [ ! -d "${WGDASH}/src/db" ]; then
|
||||||
|
ln -s /data/db "${WGDASH}/src/db"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Create the wg-dashboard.ini file if it does not exist yet.
|
||||||
|
if [ ! -f "${config_file}" ]; then
|
||||||
|
echo "Creating wg-dashboard.ini file"
|
||||||
|
touch "${config_file}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Link the wg-dashboard.ini file from the persistent directory to where WGDashboard expects it.
|
||||||
|
if [ ! -f "${WGDASH}/src/wg-dashboard.ini" ]; then
|
||||||
|
ln -s "${config_file}" "${WGDASH}/src/wg-dashboard.ini"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Create the Python virtual environment.
|
||||||
|
python3 -m venv "${WGDASH}"/src/venv
|
||||||
|
. "${WGDASH}/src/venv/bin/activate"
|
||||||
|
|
||||||
|
# Due to this pip dependency being available as a system package we can just move it to the venv.
|
||||||
|
echo "Moving PIP dependency from ephemerality to runtime environment: psutil"
|
||||||
|
mv /usr/lib/python3.12/site-packages/psutil* "${WGDASH}"/src/venv/lib/python3.12/site-packages
|
||||||
|
|
||||||
|
# Due to this pip dependency being available as a system package we can just move it to the venv.
|
||||||
|
echo "Moving PIP dependency from ephemerality to runtime environment: bcrypt"
|
||||||
|
mv /usr/lib/python3.12/site-packages/bcrypt* "${WGDASH}"/src/venv/lib/python3.12/site-packages
|
||||||
|
|
||||||
|
# Use the bash interpreter to install WGDashboard according to the wgd.sh script.
|
||||||
|
/bin/bash ./wgd.sh install
|
||||||
|
|
||||||
|
echo "Looks like the installation succeeded. Moving on."
|
||||||
|
|
||||||
|
# This first step is to ensure the wg0.conf file exists, and if not, then its copied over from the ephemeral container storage.
|
||||||
|
# This is done so WGDashboard it works out of the box, it also sets a randomly generated private key.
|
||||||
|
|
||||||
|
if [ ! -f "/etc/wireguard/wg0.conf" ]; then
|
||||||
|
echo "Standard wg0 Configuration file not found, grabbing template."
|
||||||
|
cp -a "/configs/wg0.conf.template" "/etc/wireguard/wg0.conf"
|
||||||
|
|
||||||
|
echo "Setting a secure private key."
|
||||||
|
|
||||||
|
local privateKey
|
||||||
|
privateKey=$(wg genkey)
|
||||||
|
sed -i "s|^PrivateKey *=.*$|PrivateKey = ${privateKey}|g" /etc/wireguard/wg0.conf
|
||||||
|
|
||||||
|
echo "Done setting template."
|
||||||
|
else
|
||||||
|
echo "Existing wg0 configuration file found, using that."
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
set_envvars() {
|
||||||
|
printf "\n------------- SETTING ENVIRONMENT VARIABLES ----------------\n"
|
||||||
|
|
||||||
|
# Check if the file is empty
|
||||||
|
if [ ! -s "${config_file}" ]; then
|
||||||
|
echo "Config file is empty. Creating [Peers] section."
|
||||||
|
|
||||||
|
# Create [Peers] section with initial values
|
||||||
|
{
|
||||||
|
echo "[Peers]"
|
||||||
|
echo "peer_global_dns = ${global_dns}"
|
||||||
|
echo "remote_endpoint = ${public_ip}"
|
||||||
|
echo -e "\n[Server]"
|
||||||
|
echo "app_port = ${wgd_port}"
|
||||||
|
} > "${config_file}"
|
||||||
|
|
||||||
|
else
|
||||||
|
echo "Config file is not empty, using pre-existing."
|
||||||
|
fi
|
||||||
|
|
||||||
|
echo "Verifying current variables..."
|
||||||
|
|
||||||
|
# Check and update the DNS if it has changed
|
||||||
|
current_dns=$(grep "peer_global_dns = " "${config_file}" | awk '{print $NF}')
|
||||||
|
if [ "${global_dns}" == "$current_dns" ]; then
|
||||||
|
echo "DNS is set correctly, moving on."
|
||||||
|
|
||||||
|
else
|
||||||
|
echo "Changing default DNS..."
|
||||||
|
sed -i "s/^peer_global_dns = .*/peer_global_dns = ${global_dns}/" "${config_file}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Checking the current set public IP and changing it if it has changed.
|
||||||
|
current_public_ip=$(grep "remote_endpoint = " "${config_file}" | awk '{print $NF}')
|
||||||
|
if [ "${public_ip}" == "" ]; then
|
||||||
|
default_ip=$(curl -s ifconfig.me)
|
||||||
|
|
||||||
|
echo "Trying to fetch the Public-IP using ifconfig.me: ${default_ip}"
|
||||||
|
sed -i "s/^remote_endpoint = .*/remote_endpoint = ${default_ip}/" "${config_file}"
|
||||||
|
elif [ "${current_public_ip}" != "${public_ip}" ]; then
|
||||||
|
sed -i "s/^remote_endpoint = .*/remote_endpoint = ${public_ip}/" "${config_file}"
|
||||||
|
else
|
||||||
|
echo "Public-IP is correct, moving on."
|
||||||
|
fi
|
||||||
|
|
||||||
|
# Checking the current WGDashboard web port and changing if needed.
|
||||||
|
current_wgd_port=$(grep "app_port = " "${config_file}" | awk '{print $NF}')
|
||||||
|
if [ "${current_wgd_port}" == "${wgd_port}" ]; then
|
||||||
|
echo "Current WGD port is set correctly, moving on."
|
||||||
|
else
|
||||||
|
echo "Changing default WGD port..."
|
||||||
|
sed -i "s/^app_port = .*/app_port = ${wgd_port}/" "${config_file}"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# === CORE SERVICES ===
|
||||||
|
start_core() {
|
||||||
|
printf "\n---------------------- STARTING CORE -----------------------\n"
|
||||||
|
|
||||||
|
# Due to some instances complaining about this, making sure its there every time.
|
||||||
|
mkdir -p /dev/net
|
||||||
|
mknod /dev/net/tun c 10 200
|
||||||
|
chmod 600 /dev/net/tun
|
||||||
|
|
||||||
|
# Actually starting WGDashboard
|
||||||
|
echo "Activating Python venv and executing the WireGuard Dashboard service."
|
||||||
|
/bin/bash ./wgd.sh start
|
||||||
|
}
|
||||||
|
|
||||||
|
ensure_blocking() {
|
||||||
|
# Wait a second before continuing, to give the python program some time to get ready.
|
||||||
|
sleep 1s
|
||||||
|
echo -e "\nEnsuring container continuation."
|
||||||
|
|
||||||
|
# Find and tail the latest error and access logs if they exist
|
||||||
|
local logdir="${WGDASH}/src/log"
|
||||||
|
|
||||||
|
latestErrLog=$(find "$logdir" -name "error_*.log" -type f -print | sort -r | head -n 1)
|
||||||
|
|
||||||
|
# Only tail the logs if they are found
|
||||||
|
if [ -n "$latestErrLog" ]; then
|
||||||
|
tail -f "$latestErrLog" &
|
||||||
|
|
||||||
|
# Wait for the tail process to end.
|
||||||
|
wait $!
|
||||||
|
else
|
||||||
|
echo "No log files found to tail. Something went wrong, exiting..."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
# Execute functions for the WireGuard Dashboard services, then set the environment variables
|
||||||
|
ensure_installation
|
||||||
|
set_envvars
|
||||||
|
start_core
|
||||||
|
ensure_blocking
|
BIN
img/AddPeer.png
Before Width: | Height: | Size: 156 KiB |
Before Width: | Height: | Size: 181 KiB |
Before Width: | Height: | Size: 114 KiB |
Before Width: | Height: | Size: 210 KiB |
Before Width: | Height: | Size: 211 KiB |
BIN
img/EditPeer.png
Before Width: | Height: | Size: 149 KiB |
BIN
img/HomePage.png
Before Width: | Height: | Size: 141 KiB |
BIN
img/PWA.gif
Before Width: | Height: | Size: 5.5 MiB |
BIN
img/Ping.png
Before Width: | Height: | Size: 229 KiB |
BIN
img/QRCode.png
Before Width: | Height: | Size: 239 KiB |
Before Width: | Height: | Size: 3.8 MiB |
BIN
img/SearchIP.png
Before Width: | Height: | Size: 153 KiB |
BIN
img/SignIn.png
Before Width: | Height: | Size: 140 KiB |
Before Width: | Height: | Size: 240 KiB |
BIN
img/logo.png
Before Width: | Height: | Size: 180 KiB |
1369
package-lock.json
generated
Normal file
7
package.json
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"dependencies": {
|
||||||
|
"marked": "^15.0.7",
|
||||||
|
"openai": "^4.89.0",
|
||||||
|
"pinia-plugin-persistedstate": "^4.2.0"
|
||||||
|
}
|
||||||
|
}
|
6
qodana.yaml
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
version: "1.0"
|
||||||
|
linter: jetbrains/qodana-python:2024.3
|
||||||
|
profile:
|
||||||
|
name: qodana.recommended
|
||||||
|
include:
|
||||||
|
- name: CheckDependencyLicenses
|
76
src/Utilities.py
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
import re, ipaddress
|
||||||
|
import subprocess
|
||||||
|
|
||||||
|
|
||||||
|
def RegexMatch(regex, text) -> bool:
|
||||||
|
"""
|
||||||
|
Regex Match
|
||||||
|
@param regex: Regex patter
|
||||||
|
@param text: Text to match
|
||||||
|
@return: Boolean indicate if the text match the regex pattern
|
||||||
|
"""
|
||||||
|
pattern = re.compile(regex)
|
||||||
|
return pattern.search(text) is not None
|
||||||
|
|
||||||
|
def GetRemoteEndpoint() -> str:
|
||||||
|
"""
|
||||||
|
Using socket to determine default interface IP address. Thanks, @NOXICS
|
||||||
|
@return:
|
||||||
|
"""
|
||||||
|
import socket
|
||||||
|
with socket.socket(socket.AF_INET, socket.SOCK_DGRAM) as s:
|
||||||
|
s.connect(("1.1.1.1", 80)) # Connecting to a public IP
|
||||||
|
wgd_remote_endpoint = s.getsockname()[0]
|
||||||
|
return str(wgd_remote_endpoint)
|
||||||
|
|
||||||
|
|
||||||
|
def StringToBoolean(value: str):
|
||||||
|
"""
|
||||||
|
Convert string boolean to boolean
|
||||||
|
@param value: Boolean value in string came from Configuration file
|
||||||
|
@return: Boolean value
|
||||||
|
"""
|
||||||
|
return (value.strip().replace(" ", "").lower() in
|
||||||
|
("yes", "true", "t", "1", 1))
|
||||||
|
|
||||||
|
def ValidateIPAddressesWithRange(ips: str) -> bool:
|
||||||
|
s = ips.replace(" ", "").split(",")
|
||||||
|
for ip in s:
|
||||||
|
try:
|
||||||
|
ipaddress.ip_network(ip)
|
||||||
|
except ValueError as e:
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
|
def ValidateIPAddresses(ips) -> bool:
|
||||||
|
s = ips.replace(" ", "").split(",")
|
||||||
|
for ip in s:
|
||||||
|
try:
|
||||||
|
ipaddress.ip_address(ip)
|
||||||
|
except ValueError as e:
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
|
def ValidateDNSAddress(addresses) -> tuple[bool, str]:
|
||||||
|
s = addresses.replace(" ", "").split(",")
|
||||||
|
for address in s:
|
||||||
|
if not ValidateIPAddresses(address) and not RegexMatch(
|
||||||
|
r"(?:[a-z0-9](?:[a-z0-9-]{0,61}[a-z0-9])?\.)+[a-z][a-z]{0,61}[a-z]", address):
|
||||||
|
return False, f"{address} does not appear to be an valid DNS address"
|
||||||
|
return True, ""
|
||||||
|
|
||||||
|
def GenerateWireguardPublicKey(privateKey: str) -> tuple[bool, str] | tuple[bool, None]:
|
||||||
|
try:
|
||||||
|
publicKey = subprocess.check_output(f"wg pubkey", input=privateKey.encode(), shell=True,
|
||||||
|
stderr=subprocess.STDOUT)
|
||||||
|
return True, publicKey.decode().strip('\n')
|
||||||
|
except subprocess.CalledProcessError:
|
||||||
|
return False, None
|
||||||
|
|
||||||
|
def GenerateWireguardPrivateKey() -> tuple[bool, str] | tuple[bool, None]:
|
||||||
|
try:
|
||||||
|
publicKey = subprocess.check_output(f"wg genkey", shell=True,
|
||||||
|
stderr=subprocess.STDOUT)
|
||||||
|
return True, publicKey.decode().strip('\n')
|
||||||
|
except subprocess.CalledProcessError:
|
||||||
|
return False, None
|
4636
src/dashboard.py
@ -1,11 +1,26 @@
|
|||||||
import multiprocessing
|
import os.path
|
||||||
import dashboard
|
import dashboard, configparser
|
||||||
|
from datetime import datetime
|
||||||
|
global sqldb, cursor, DashboardConfig, WireguardConfigurations, AllPeerJobs, JobLogger
|
||||||
|
app_host, app_port = dashboard.gunicornConfig()
|
||||||
|
date = datetime.today().strftime('%Y_%m_%d_%H_%M_%S')
|
||||||
|
|
||||||
app_host, app_port = dashboard.get_host_bind()
|
def post_worker_init(worker):
|
||||||
|
dashboard.startThreads()
|
||||||
|
|
||||||
worker_class = 'gthread'
|
worker_class = 'gthread'
|
||||||
workers = multiprocessing.cpu_count() * 2 + 1
|
workers = 1
|
||||||
threads = 4
|
threads = 1
|
||||||
bind = f"{app_host}:{app_port}"
|
bind = f"{app_host}:{app_port}"
|
||||||
daemon = True
|
daemon = True
|
||||||
pidfile = './gunicorn.pid'
|
pidfile = './gunicorn.pid'
|
||||||
|
wsgi_app = "dashboard:app"
|
||||||
|
accesslog = f"./log/access_{date}.log"
|
||||||
|
log_level = "debug"
|
||||||
|
capture_output = True
|
||||||
|
errorlog = f"./log/error_{date}.log"
|
||||||
|
pythonpath = "., ./modules"
|
||||||
|
|
||||||
|
print(f"[Gunicorn] WGDashboard w/ Gunicorn will be running on {bind}", flush=True)
|
||||||
|
print(f"[Gunicorn] Access log file is at {accesslog}", flush=True)
|
||||||
|
print(f"[Gunicorn] Error log file is at {errorlog}", flush=True)
|
||||||
|
35
src/modules/DashboardLogger.py
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
"""
|
||||||
|
Dashboard Logger Class
|
||||||
|
"""
|
||||||
|
import sqlite3, os, uuid
|
||||||
|
|
||||||
|
class DashboardLogger:
|
||||||
|
def __init__(self, CONFIGURATION_PATH):
|
||||||
|
self.loggerdb = sqlite3.connect(os.path.join(CONFIGURATION_PATH, 'db', 'wgdashboard_log.db'),
|
||||||
|
isolation_level=None,
|
||||||
|
check_same_thread=False)
|
||||||
|
self.loggerdb.row_factory = sqlite3.Row
|
||||||
|
self.__createLogDatabase()
|
||||||
|
self.log(Message="WGDashboard started")
|
||||||
|
def __createLogDatabase(self):
|
||||||
|
with self.loggerdb:
|
||||||
|
loggerdbCursor = self.loggerdb.cursor()
|
||||||
|
existingTable = loggerdbCursor.execute("SELECT name from sqlite_master where type='table'").fetchall()
|
||||||
|
existingTable = [t['name'] for t in existingTable]
|
||||||
|
if "DashboardLog" not in existingTable:
|
||||||
|
loggerdbCursor.execute(
|
||||||
|
"CREATE TABLE DashboardLog (LogID VARCHAR NOT NULL, LogDate DATETIME DEFAULT (strftime('%Y-%m-%d %H:%M:%S','now', 'localtime')), URL VARCHAR, IP VARCHAR, Status VARCHAR, Message VARCHAR, PRIMARY KEY (LogID))")
|
||||||
|
if self.loggerdb.in_transaction:
|
||||||
|
self.loggerdb.commit()
|
||||||
|
|
||||||
|
def log(self, URL: str = "", IP: str = "", Status: str = "true", Message: str = "") -> bool:
|
||||||
|
try:
|
||||||
|
loggerdbCursor = self.loggerdb.cursor()
|
||||||
|
loggerdbCursor.execute(
|
||||||
|
"INSERT INTO DashboardLog (LogID, URL, IP, Status, Message) VALUES (?, ?, ?, ?, ?);", (str(uuid.uuid4()), URL, IP, Status, Message,))
|
||||||
|
loggerdbCursor.close()
|
||||||
|
self.loggerdb.commit()
|
||||||
|
return True
|
||||||
|
except Exception as e:
|
||||||
|
print(f"[WGDashboard] Access Log Error: {str(e)}")
|
||||||
|
return False
|
69
src/modules/Email.py
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
import os.path
|
||||||
|
import smtplib
|
||||||
|
from email import encoders
|
||||||
|
from email.header import Header
|
||||||
|
from email.mime.base import MIMEBase
|
||||||
|
from email.mime.multipart import MIMEMultipart
|
||||||
|
from email.mime.text import MIMEText
|
||||||
|
from email.utils import formataddr
|
||||||
|
|
||||||
|
class EmailSender:
|
||||||
|
def __init__(self, DashboardConfig):
|
||||||
|
self.smtp = None
|
||||||
|
self.DashboardConfig = DashboardConfig
|
||||||
|
if not os.path.exists('./attachments'):
|
||||||
|
os.mkdir('./attachments')
|
||||||
|
|
||||||
|
def Server(self):
|
||||||
|
return self.DashboardConfig.GetConfig("Email", "server")[1]
|
||||||
|
|
||||||
|
def Port(self):
|
||||||
|
return self.DashboardConfig.GetConfig("Email", "port")[1]
|
||||||
|
|
||||||
|
def Encryption(self):
|
||||||
|
return self.DashboardConfig.GetConfig("Email", "encryption")[1]
|
||||||
|
|
||||||
|
def Username(self):
|
||||||
|
return self.DashboardConfig.GetConfig("Email", "username")[1]
|
||||||
|
|
||||||
|
def Password(self):
|
||||||
|
return self.DashboardConfig.GetConfig("Email", "email_password")[1]
|
||||||
|
|
||||||
|
def SendFrom(self):
|
||||||
|
return self.DashboardConfig.GetConfig("Email", "send_from")[1]
|
||||||
|
|
||||||
|
def ready(self):
|
||||||
|
return len(self.Server()) > 0 and len(self.Port()) > 0 and len(self.Encryption()) > 0 and len(self.Username()) > 0 and len(self.Password()) > 0 and len(self.SendFrom())
|
||||||
|
|
||||||
|
def send(self, receiver, subject, body, includeAttachment = False, attachmentName = ""):
|
||||||
|
if self.ready():
|
||||||
|
try:
|
||||||
|
self.smtp = smtplib.SMTP(self.Server(), port=int(self.Port()))
|
||||||
|
self.smtp.ehlo()
|
||||||
|
if self.Encryption() == "STARTTLS":
|
||||||
|
self.smtp.starttls()
|
||||||
|
self.smtp.login(self.Username(), self.Password())
|
||||||
|
message = MIMEMultipart()
|
||||||
|
message['Subject'] = subject
|
||||||
|
message['From'] = self.SendFrom()
|
||||||
|
message["To"] = receiver
|
||||||
|
message.attach(MIMEText(body, "plain"))
|
||||||
|
|
||||||
|
if includeAttachment and len(attachmentName) > 0:
|
||||||
|
attachmentPath = os.path.join('./attachments', attachmentName)
|
||||||
|
if os.path.exists(attachmentPath):
|
||||||
|
attachment = MIMEBase("application", "octet-stream")
|
||||||
|
with open(os.path.join('./attachments', attachmentName), 'rb') as f:
|
||||||
|
attachment.set_payload(f.read())
|
||||||
|
encoders.encode_base64(attachment)
|
||||||
|
attachment.add_header("Content-Disposition", f"attachment; filename= {attachmentName}",)
|
||||||
|
message.attach(attachment)
|
||||||
|
else:
|
||||||
|
self.smtp.close()
|
||||||
|
return False, "Attachment does not exist"
|
||||||
|
self.smtp.sendmail(self.SendFrom(), receiver, message.as_string())
|
||||||
|
self.smtp.close()
|
||||||
|
return True, None
|
||||||
|
except Exception as e:
|
||||||
|
return False, f"Send failed | Reason: {e}"
|
||||||
|
return False, "SMTP not configured"
|
22
src/modules/Log.py
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
"""
|
||||||
|
Log Class
|
||||||
|
"""
|
||||||
|
class Log:
|
||||||
|
def __init__(self, LogID: str, JobID: str, LogDate: str, Status: str, Message: str):
|
||||||
|
self.LogID = LogID
|
||||||
|
self.JobID = JobID
|
||||||
|
self.LogDate = LogDate
|
||||||
|
self.Status = Status
|
||||||
|
self.Message = Message
|
||||||
|
|
||||||
|
def toJson(self):
|
||||||
|
return {
|
||||||
|
"LogID": self.LogID,
|
||||||
|
"JobID": self.JobID,
|
||||||
|
"LogDate": self.LogDate,
|
||||||
|
"Status": self.Status,
|
||||||
|
"Message": self.Message
|
||||||
|
}
|
||||||
|
|
||||||
|
def __dict__(self):
|
||||||
|
return self.toJson()
|
32
src/modules/PeerJob.py
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
"""
|
||||||
|
Peer Job
|
||||||
|
"""
|
||||||
|
from datetime import datetime
|
||||||
|
class PeerJob:
|
||||||
|
def __init__(self, JobID: str, Configuration: str, Peer: str,
|
||||||
|
Field: str, Operator: str, Value: str, CreationDate: datetime, ExpireDate: datetime, Action: str):
|
||||||
|
self.Action = Action
|
||||||
|
self.ExpireDate = ExpireDate
|
||||||
|
self.CreationDate = CreationDate
|
||||||
|
self.Value = Value
|
||||||
|
self.Operator = Operator
|
||||||
|
self.Field = Field
|
||||||
|
self.Configuration = Configuration
|
||||||
|
self.Peer = Peer
|
||||||
|
self.JobID = JobID
|
||||||
|
|
||||||
|
def toJson(self):
|
||||||
|
return {
|
||||||
|
"JobID": self.JobID,
|
||||||
|
"Configuration": self.Configuration,
|
||||||
|
"Peer": self.Peer,
|
||||||
|
"Field": self.Field,
|
||||||
|
"Operator": self.Operator,
|
||||||
|
"Value": self.Value,
|
||||||
|
"CreationDate": self.CreationDate,
|
||||||
|
"ExpireDate": self.ExpireDate,
|
||||||
|
"Action": self.Action
|
||||||
|
}
|
||||||
|
|
||||||
|
def __dict__(self):
|
||||||
|
return self.toJson()
|
53
src/modules/PeerJobLogger.py
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
"""
|
||||||
|
Peer Job Logger
|
||||||
|
"""
|
||||||
|
import sqlite3, os, uuid
|
||||||
|
from .Log import Log
|
||||||
|
|
||||||
|
class PeerJobLogger:
|
||||||
|
def __init__(self, CONFIGURATION_PATH, AllPeerJobs):
|
||||||
|
self.loggerdb = sqlite3.connect(os.path.join(CONFIGURATION_PATH, 'db', 'wgdashboard_log.db'),
|
||||||
|
check_same_thread=False)
|
||||||
|
self.loggerdb.row_factory = sqlite3.Row
|
||||||
|
self.logs: list[Log] = []
|
||||||
|
self.__createLogDatabase()
|
||||||
|
self.AllPeerJobs = AllPeerJobs
|
||||||
|
def __createLogDatabase(self):
|
||||||
|
with self.loggerdb:
|
||||||
|
loggerdbCursor = self.loggerdb.cursor()
|
||||||
|
|
||||||
|
existingTable = loggerdbCursor.execute("SELECT name from sqlite_master where type='table'").fetchall()
|
||||||
|
existingTable = [t['name'] for t in existingTable]
|
||||||
|
|
||||||
|
if "JobLog" not in existingTable:
|
||||||
|
loggerdbCursor.execute("CREATE TABLE JobLog (LogID VARCHAR NOT NULL, JobID NOT NULL, LogDate DATETIME DEFAULT (strftime('%Y-%m-%d %H:%M:%S','now', 'localtime')), Status VARCHAR NOT NULL, Message VARCHAR, PRIMARY KEY (LogID))")
|
||||||
|
if self.loggerdb.in_transaction:
|
||||||
|
self.loggerdb.commit()
|
||||||
|
def log(self, JobID: str, Status: bool = True, Message: str = "") -> bool:
|
||||||
|
try:
|
||||||
|
with self.loggerdb:
|
||||||
|
loggerdbCursor = self.loggerdb.cursor()
|
||||||
|
loggerdbCursor.execute(f"INSERT INTO JobLog (LogID, JobID, Status, Message) VALUES (?, ?, ?, ?)",
|
||||||
|
(str(uuid.uuid4()), JobID, Status, Message,))
|
||||||
|
if self.loggerdb.in_transaction:
|
||||||
|
self.loggerdb.commit()
|
||||||
|
except Exception as e:
|
||||||
|
print(f"[WGDashboard] Peer Job Log Error: {str(e)}")
|
||||||
|
return False
|
||||||
|
return True
|
||||||
|
|
||||||
|
def getLogs(self, all: bool = False, configName = None) -> list[Log]:
|
||||||
|
logs: list[Log] = []
|
||||||
|
try:
|
||||||
|
allJobs = self.AllPeerJobs.getAllJobs(configName)
|
||||||
|
allJobsID = ", ".join([f"'{x.JobID}'" for x in allJobs])
|
||||||
|
with self.loggerdb:
|
||||||
|
loggerdbCursor = self.loggerdb.cursor()
|
||||||
|
table = loggerdbCursor.execute(f"SELECT * FROM JobLog WHERE JobID IN ({allJobsID}) ORDER BY LogDate DESC").fetchall()
|
||||||
|
self.logs.clear()
|
||||||
|
for l in table:
|
||||||
|
logs.append(
|
||||||
|
Log(l["LogID"], l["JobID"], l["LogDate"], l["Status"], l["Message"]))
|
||||||
|
except Exception as e:
|
||||||
|
return logs
|
||||||
|
return logs
|
135
src/modules/SystemStatus.py
Normal file
@ -0,0 +1,135 @@
|
|||||||
|
import psutil
|
||||||
|
class SystemStatus:
|
||||||
|
def __init__(self):
|
||||||
|
self.CPU = CPU()
|
||||||
|
self.MemoryVirtual = Memory('virtual')
|
||||||
|
self.MemorySwap = Memory('swap')
|
||||||
|
self.Disks = Disks()
|
||||||
|
self.NetworkInterfaces = NetworkInterfaces()
|
||||||
|
self.Processes = Processes()
|
||||||
|
def toJson(self):
|
||||||
|
return {
|
||||||
|
"CPU": self.CPU,
|
||||||
|
"Memory": {
|
||||||
|
"VirtualMemory": self.MemoryVirtual,
|
||||||
|
"SwapMemory": self.MemorySwap
|
||||||
|
},
|
||||||
|
"Disks": self.Disks,
|
||||||
|
"NetworkInterfaces": self.NetworkInterfaces,
|
||||||
|
"Processes": self.Processes
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class CPU:
|
||||||
|
def __init__(self):
|
||||||
|
self.cpu_percent: float = 0
|
||||||
|
self.cpu_percent_per_cpu: list[float] = []
|
||||||
|
def getData(self):
|
||||||
|
try:
|
||||||
|
self.cpu_percent_per_cpu = psutil.cpu_percent(interval=0.5, percpu=True)
|
||||||
|
self.cpu_percent = psutil.cpu_percent(interval=0.5)
|
||||||
|
except Exception as e:
|
||||||
|
pass
|
||||||
|
def toJson(self):
|
||||||
|
self.getData()
|
||||||
|
return self.__dict__
|
||||||
|
|
||||||
|
class Memory:
|
||||||
|
def __init__(self, memoryType: str):
|
||||||
|
self.__memoryType__ = memoryType
|
||||||
|
self.total = 0
|
||||||
|
self.available = 0
|
||||||
|
self.percent = 0
|
||||||
|
def getData(self):
|
||||||
|
try:
|
||||||
|
if self.__memoryType__ == "virtual":
|
||||||
|
memory = psutil.virtual_memory()
|
||||||
|
else:
|
||||||
|
memory = psutil.swap_memory()
|
||||||
|
self.total = memory.total
|
||||||
|
self.available = memory.available
|
||||||
|
self.percent = memory.percent
|
||||||
|
except Exception as e:
|
||||||
|
pass
|
||||||
|
def toJson(self):
|
||||||
|
self.getData()
|
||||||
|
return self.__dict__
|
||||||
|
|
||||||
|
class Disks:
|
||||||
|
def __init__(self):
|
||||||
|
self.disks : list[Disk] = []
|
||||||
|
def getData(self):
|
||||||
|
try:
|
||||||
|
self.disks = list(map(lambda x : Disk(x.mountpoint), psutil.disk_partitions()))
|
||||||
|
except Exception as e:
|
||||||
|
pass
|
||||||
|
def toJson(self):
|
||||||
|
self.getData()
|
||||||
|
return self.disks
|
||||||
|
|
||||||
|
class Disk:
|
||||||
|
def __init__(self, mountPoint: str):
|
||||||
|
self.total = 0
|
||||||
|
self.used = 0
|
||||||
|
self.free = 0
|
||||||
|
self.percent = 0
|
||||||
|
self.mountPoint = mountPoint
|
||||||
|
def getData(self):
|
||||||
|
try:
|
||||||
|
disk = psutil.disk_usage(self.mountPoint)
|
||||||
|
self.total = disk.total
|
||||||
|
self.free = disk.free
|
||||||
|
self.used = disk.used
|
||||||
|
self.percent = disk.percent
|
||||||
|
except Exception as e:
|
||||||
|
pass
|
||||||
|
def toJson(self):
|
||||||
|
self.getData()
|
||||||
|
return self.__dict__
|
||||||
|
|
||||||
|
class NetworkInterfaces:
|
||||||
|
def __init__(self):
|
||||||
|
self.interfaces = {}
|
||||||
|
def getData(self):
|
||||||
|
try:
|
||||||
|
network = psutil.net_io_counters(pernic=True, nowrap=True)
|
||||||
|
for i in network.keys():
|
||||||
|
self.interfaces[i] = network[i]._asdict()
|
||||||
|
except Exception as e:
|
||||||
|
pass
|
||||||
|
def toJson(self):
|
||||||
|
self.getData()
|
||||||
|
return self.interfaces
|
||||||
|
|
||||||
|
class Process:
|
||||||
|
def __init__(self, name, command, pid, percent):
|
||||||
|
self.name = name
|
||||||
|
self.command = command
|
||||||
|
self.pid = pid
|
||||||
|
self.percent = percent
|
||||||
|
def toJson(self):
|
||||||
|
return self.__dict__
|
||||||
|
|
||||||
|
class Processes:
|
||||||
|
def __init__(self):
|
||||||
|
self.CPU_Top_10_Processes: list[Process] = []
|
||||||
|
self.Memory_Top_10_Processes: list[Process] = []
|
||||||
|
def getData(self):
|
||||||
|
while True:
|
||||||
|
try:
|
||||||
|
processes = list(psutil.process_iter())
|
||||||
|
self.CPU_Top_10_Processes = sorted(
|
||||||
|
list(map(lambda x : Process(x.name(), " ".join(x.cmdline()), x.pid, x.cpu_percent()), processes)),
|
||||||
|
key=lambda x : x.percent, reverse=True)[:20]
|
||||||
|
self.Memory_Top_10_Processes = sorted(
|
||||||
|
list(map(lambda x : Process(x.name(), " ".join(x.cmdline()), x.pid, x.memory_percent()), processes)),
|
||||||
|
key=lambda x : x.percent, reverse=True)[:20]
|
||||||
|
break
|
||||||
|
except Exception as e:
|
||||||
|
break
|
||||||
|
def toJson(self):
|
||||||
|
self.getData()
|
||||||
|
return {
|
||||||
|
"cpu_top_10": self.CPU_Top_10_Processes,
|
||||||
|
"memory_top_10": self.Memory_Top_10_Processes
|
||||||
|
}
|
@ -1,7 +1,10 @@
|
|||||||
Flask
|
bcrypt
|
||||||
ifcfg
|
ifcfg
|
||||||
psutil
|
psutil
|
||||||
|
pyotp
|
||||||
|
Flask
|
||||||
|
flask-cors
|
||||||
icmplib
|
icmplib
|
||||||
flask-qrcode
|
|
||||||
gunicorn
|
gunicorn
|
||||||
certbot
|
requests
|
||||||
|
tcconfig
|
30
src/static/app/.gitignore
vendored
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
# Logs
|
||||||
|
logs
|
||||||
|
*.log
|
||||||
|
npm-debug.log*
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
||||||
|
pnpm-debug.log*
|
||||||
|
lerna-debug.log*
|
||||||
|
|
||||||
|
node_modules
|
||||||
|
.DS_Store
|
||||||
|
dist-ssr
|
||||||
|
coverage
|
||||||
|
*.local
|
||||||
|
|
||||||
|
/cypress/videos/
|
||||||
|
/cypress/screenshots/
|
||||||
|
|
||||||
|
# Editor directories and files
|
||||||
|
.vscode/*
|
||||||
|
!.vscode/extensions.json
|
||||||
|
.idea
|
||||||
|
*.suo
|
||||||
|
*.ntvs*
|
||||||
|
*.njsproj
|
||||||
|
*.sln
|
||||||
|
*.sw?
|
||||||
|
|
||||||
|
*.tsbuildinfo
|
||||||
|
.vite/*
|
27
src/static/app/build.sh
Executable file
@ -0,0 +1,27 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
echo "Running vite build..."
|
||||||
|
if vite build; then
|
||||||
|
echo "Vite build successful."
|
||||||
|
else
|
||||||
|
echo "Vite build failed. Exiting."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
echo "Checking for changes to commit..."
|
||||||
|
if git diff-index --quiet HEAD --; then
|
||||||
|
|
||||||
|
if git commit -a; then
|
||||||
|
echo "Git commit successful."
|
||||||
|
else
|
||||||
|
echo "Git commit failed. Exiting."
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
echo "No changes to commit. Skipping commit."
|
||||||
|
fi
|
||||||
|
echo "Pushing changes to remote..."
|
||||||
|
if git push; then
|
||||||
|
echo "Git push successful."
|
||||||
|
else
|
||||||
|
echo "Git push failed. Exiting."
|
||||||
|
exit 1
|
||||||
|
fi
|
BIN
src/static/app/dist/assets/bootstrap-icons-BOrJxbIo.woff
vendored
Normal file
BIN
src/static/app/dist/assets/bootstrap-icons-BtvjY1KL.woff2
vendored
Normal file
8
src/static/app/dist/assets/browser-CjSdxGTc.js
vendored
Normal file
1
src/static/app/dist/assets/configuration-qGYhSc1H.js
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
import{_ as r,c as i,d as o,w as e,k as l,a as t,j as _,i as a,l as d,S as u}from"./index-DZliHkQD.js";const m={name:"configuration"},p={class:"mt-md-5 mt-3 text-body"};function f(k,x,h,w,$,v){const n=l("RouterView");return t(),i("div",p,[o(n,null,{default:e(({Component:s,route:c})=>[o(_,{name:"fade2",mode:"out-in"},{default:e(()=>[(t(),a(u,null,{default:e(()=>[(t(),a(d(s),{key:c.path}))]),_:2},1024))]),_:2},1024)]),_:1})])}const B=r(m,[["render",f]]);export{B as default};
|
1
src/static/app/dist/assets/configurationList-BY8izVeQ.css
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
.fade-enter-active[data-v-dafd6275]{transition-delay:var(--7d032b58)!important}.progress-bar[data-v-c20f1a80]{width:0;transition:all 1s cubic-bezier(.42,0,.22,1)}.filter a[data-v-ea61b607]{text-decoration:none}
|
1
src/static/app/dist/assets/configurationList-Cznk60kB.js
vendored
Normal file
1
src/static/app/dist/assets/dayjs.min-PaIL06iQ.js
vendored
Normal file
1
src/static/app/dist/assets/editConfiguration-Ca_IlmFH.css
vendored
Normal file
7
src/static/app/dist/assets/editConfiguration-gjtFMIrx.js
vendored
Normal file
1
src/static/app/dist/assets/index-2ZxMyhlP.css
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
.agentMessage[data-v-8635cf47]{white-space:break-spaces;max-width:80%;display:flex;flex-direction:column;word-wrap:break-word}.text-bg-secondary[data-v-8635cf47]{background-color:RGBA(var(--bs-secondary-rgb),.7)!important}.text-bg-primary[data-v-8635cf47]{background-color:RGBA(var(--bs-primary-rgb),.7)!important}.agentContainer[data-v-a76f42bd]{--agentHeight: 100vh;position:absolute;z-index:9999;top:0;left:100%;width:450px;box-shadow:0 10px 30px #0000004d;backdrop-filter:blur(8px);background:linear-gradient(var(--degree),#009dff52 var(--distance2),#F9464752 100%)}.agentContainer.enabled[data-v-a76f42bd]{height:calc(var(--agentHeight) - 1rem)}@media screen and (max-width: 768px){.agentContainer[data-v-a76f42bd]{--agentHeight: 100vh !important;top:0;left:0;max-height:calc(var(--agentHeight) - 58px - 1rem);width:calc(100% - 1rem)}}.agentChatroomBody[data-v-a76f42bd]{flex:1 1 auto;overflow-y:auto;max-height:calc(var(--agentHeight) - 70px - 244px)}@media screen and (max-width: 768px){.navbar-container[data-v-58e71749]{position:absolute!important;z-index:1000;animation-duration:.4s;animation-fill-mode:both;display:none;animation-timing-function:cubic-bezier(.82,.58,.17,.9)}.navbar-container.active[data-v-58e71749]{animation-direction:normal;display:block!important;animation-name:zoomInFade-58e71749}}.navbar-container[data-v-58e71749]{height:100vh;position:relative}@supports (height: 100dvh){@media screen and (max-width: 768px){.navbar-container[data-v-58e71749]{height:calc(100dvh - 58px)}}}@keyframes zoomInFade-58e71749{0%{opacity:0;transform:translateY(60px);filter:blur(3px)}to{opacity:1;transform:translateY(0);filter:blur(0px)}}.slideIn-enter-active[data-v-58e71749],.slideIn-leave-active[data-v-58e71749]{transition:all .3s cubic-bezier(.82,.58,.17,1)}.slideIn-enter-from[data-v-58e71749],.slideIn-leave-to[data-v-58e71749]{transform:translateY(30px);filter:blur(3px);opacity:0}main[data-v-0c6a5068]{height:100vh}@supports (height: 100dvh){@media screen and (max-width: 768px){main[data-v-0c6a5068]{height:calc(100dvh - 58px)}}}
|
15
src/static/app/dist/assets/index-DFl-XeJT.css
vendored
Normal file
44
src/static/app/dist/assets/index-DZliHkQD.js
vendored
Normal file
1
src/static/app/dist/assets/index-L60y6kc9.js
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
function f(e){return e.includes(":")?6:e.includes(".")?4:0}function b(e){const i=f(e);if(!i)throw new Error(`Invalid IP address: ${e}`);let n=0n,o=0n;const r=Object.create(null);if(i===4)for(const s of e.split(".").map(BigInt).reverse())n+=s*2n**o,o+=8n;else{if(e.includes(".")&&(r.ipv4mapped=!0,e=e.split(":").map(t=>{if(t.includes(".")){const[c,l,d,a]=t.split(".").map($=>Number($).toString(16).padStart(2,"0"));return`${c}${l}:${d}${a}`}else return t}).join(":")),e.includes("%")){let t;[,e,t]=/(.+)%(.+)/.exec(e)||[],r.scopeid=t}const s=e.split(":"),u=s.indexOf("");if(u!==-1)for(;s.length<8;)s.splice(u,0,"");for(const t of s.map(c=>BigInt(parseInt(c||"0",16))).reverse())n+=t*2n**o,o+=16n}return r.number=n,r.version=i,r}const p={4:32,6:128},I=e=>e.includes("/")?f(e):0;function m(e){const i=I(e),n=Object.create(null);if(i)n.cidr=e,n.version=i;else{const a=f(e);if(a)n.cidr=`${e}/${p[a]}`,n.version=a;else throw new Error(`Network is not a CIDR or IP: ${e}`)}const[o,r]=n.cidr.split("/");if(!/^[0-9]+$/.test(r))throw new Error(`Network is not a CIDR or IP: ${e}`);n.prefix=r,n.single=r===String(p[n.version]);const{number:s,version:u}=b(o),t=p[u],c=s.toString(2).padStart(t,"0"),l=Number(t-r),d=c.substring(0,t-l);return n.start=BigInt(`0b${d}${"0".repeat(l)}`),n.end=BigInt(`0b${d}${"1".repeat(l)}`),n}export{m as p};
|
56
src/static/app/dist/assets/index-UG7VB4Xm.js
vendored
Normal file
18
src/static/app/dist/assets/index-myupzXki.js
vendored
Normal file
1
src/static/app/dist/assets/localeText-DG9SnJT8.js
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
import{_ as e,G as t,a as o,c as a,t as c}from"./index-DZliHkQD.js";const s={name:"localeText",props:{t:""},computed:{getLocaleText(){return t(this.t)}}};function n(r,p,l,_,i,x){return o(),a("span",null,c(this.getLocaleText),1)}const u=e(s,[["render",n]]);export{u as L};
|
1
src/static/app/dist/assets/message-BaDb-qC9.css
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
.message[data-v-94c76b54]{width:100%}@media screen and (min-width: 576px){.message[data-v-94c76b54]{width:400px}}
|
1
src/static/app/dist/assets/message-DZx7QSKr.js
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
import{L as l}from"./localeText-DG9SnJT8.js";import{d as c}from"./dayjs.min-PaIL06iQ.js";import{_ as h,a as o,c as a,b as e,d as i,w as u,f as p,t as n,j as g,n as f,k as _}from"./index-DZliHkQD.js";const x={name:"message",methods:{dayjs:c,hide(){this.ct(),this.message.show=!1},show(){this.timeout=setTimeout(()=>{this.message.show=!1},5e3)},ct(){clearTimeout(this.timeout)}},components:{LocaleText:l},props:{message:Object},mounted(){this.show()},data(){return{dismiss:!1,timeout:null}}},v=["id"],b={key:0,class:"d-flex"},w={class:"fw-bold d-block",style:{"text-transform":"uppercase"}},y={class:"ms-auto"},k={key:1},T={class:"card-body d-flex align-items-center gap-3"};function M(j,s,C,L,t,m){const d=_("LocaleText");return o(),a("div",{onMouseenter:s[1]||(s[1]=r=>{t.dismiss=!0,this.ct()}),onMouseleave:s[2]||(s[2]=r=>{t.dismiss=!1,this.show()}),class:"card shadow rounded-3 position-relative message ms-auto",id:this.message.id},[e("div",{class:f([{"text-bg-danger":this.message.type==="danger","text-bg-success":this.message.type==="success","text-bg-warning":this.message.type==="warning"},"card-header pos"])},[i(g,{name:"zoom",mode:"out-in"},{default:u(()=>[t.dismiss?(o(),a("div",k,[e("small",{onClick:s[0]||(s[0]=r=>m.hide()),class:"d-block mx-auto w-100 text-center",style:{cursor:"pointer"}},[s[3]||(s[3]=e("i",{class:"bi bi-x-lg me-2"},null,-1)),i(d,{t:"Dismiss"})])])):(o(),a("div",b,[e("small",w,[i(d,{t:"FROM "}),p(" "+n(this.message.from),1)]),e("small",y,n(m.dayjs().format("hh:mm A")),1)]))]),_:1})],2),e("div",T,[e("div",null,n(this.message.content),1)])],40,v)}const z=h(x,[["render",M],["__scopeId","data-v-94c76b54"]]);export{z as M};
|
1
src/static/app/dist/assets/newConfiguration-CAFzjDsW.css
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
.protocolBtnGroup a[data-v-b97242f3]{transition:all .2s ease-in-out}
|
3
src/static/app/dist/assets/newConfiguration-CPAMxqV6.js
vendored
Normal file
10
src/static/app/dist/assets/osmap-CmjRjQ0N.js
vendored
Normal file
1
src/static/app/dist/assets/osmap-CoctJCk_.css
vendored
Normal file
1
src/static/app/dist/assets/peerAddModal-DD-hu-9U.js
vendored
Normal file
1
src/static/app/dist/assets/peerAddModal-s_w1PY7H.css
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
.list-move[data-v-ed72944d],.list-enter-active[data-v-ed72944d],.list-leave-active[data-v-ed72944d]{transition:all .3s ease}.list-enter-from[data-v-ed72944d],.list-leave-to[data-v-ed72944d]{opacity:0;transform:translateY(10px)}.list-leave-active[data-v-ed72944d]{position:absolute}
|
1
src/static/app/dist/assets/peerConfigurationFile-BFmmapnE.css
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
.slide-up-enter-active[data-v-b0ea2d46],.slide-up-leave-active[data-v-b0ea2d46]{transition:all .2s cubic-bezier(.42,0,.22,1)}.slide-up-enter-from[data-v-b0ea2d46],.slide-up-leave-to[data-v-b0ea2d46]{opacity:0;transform:scale(.9)}@keyframes spin-b0ea2d46{0%{transform:rotate(0)}to{transform:rotate(360deg)}}#check[data-v-b0ea2d46]{animation:cubic-bezier(.42,0,.22,1.3) .7s spin-b0ea2d46}
|
1
src/static/app/dist/assets/peerConfigurationFile-BaTwLYIc.js
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
import{_ as v,D as g,r as o,o as h,J as x,g as y,a as i,c as n,b as s,d as c,n as w,e as C,w as k,j as F}from"./index-DZliHkQD.js";import{L as T}from"./localeText-DG9SnJT8.js";import"./browser-CjSdxGTc.js";const M={class:"peerSettingContainer w-100 h-100 position-absolute top-0 start-0"},S={class:"container d-flex h-100 w-100"},D={class:"m-auto modal-dialog-centered dashboardModal justify-content-center"},P={class:"card rounded-3 shadow w-100"},j={class:"card-header bg-transparent d-flex align-items-center gap-2 border-0 p-4 pb-0"},B={class:"mb-0"},G={class:"card-body p-4 d-flex flex-column gap-3"},L={style:{height:"300px"},class:"d-flex"},N=["value"],V={key:0,class:"spinner-border m-auto",role:"status"},I={class:"d-flex"},W=["disabled"],$={key:0,class:"d-block"},q={key:1,class:"d-block",id:"check"},z={__name:"peerConfigurationFile",props:{selectedPeer:Object},emits:["close"],setup(u,{emit:p}){const m=p,f=u,r=g(),t=o(!1),l=o(""),a=o(!0);o({error:!1,message:void 0}),h(()=>{const d=x();y("/api/downloadPeer/"+d.params.id,{id:f.selectedPeer.id},e=>{e.status?(l.value=e.data.file,a.value=!1):this.dashboardStore.newMessage("Server",e.message,"danger")})});const b=async()=>{navigator.clipboard&&navigator.clipboard.writeText?navigator.clipboard.writeText(l.value).then(()=>{t.value=!0,setTimeout(()=>{t.value=!1},3e3)}).catch(()=>{r.newMessage("WGDashboard","Failed to copy","danger")}):(document.querySelector("#peerConfigurationFile").select(),document.execCommand("copy")?(t.value=!0,setTimeout(()=>{t.value=!1},3e3)):r.newMessage("WGDashboard","Failed to copy","danger"))};return(d,e)=>(i(),n("div",M,[s("div",S,[s("div",D,[s("div",P,[s("div",j,[s("h4",B,[c(T,{t:"Peer Configuration File"})]),s("button",{type:"button",class:"btn-close ms-auto",onClick:e[0]||(e[0]=_=>m("close"))})]),s("div",G,[s("div",L,[s("textarea",{style:{height:"300px"},class:w(["form-control w-100 rounded-3 animate__fadeIn animate__faster animate__animated",{"d-none":a.value}]),id:"peerConfigurationFile",value:l.value},null,10,N),a.value?(i(),n("div",V,e[2]||(e[2]=[s("span",{class:"visually-hidden"},"Loading...",-1)]))):C("",!0)]),s("div",I,[s("button",{onClick:e[1]||(e[1]=_=>b()),disabled:t.value||a.value,class:"ms-auto btn bg-primary-subtle border-primary-subtle text-primary-emphasis rounded-3 position-relative"},[c(F,{name:"slide-up",mode:"out-in"},{default:k(()=>[t.value?(i(),n("span",q,e[4]||(e[4]=[s("i",{class:"bi bi-check-circle-fill"},null,-1)]))):(i(),n("span",$,e[3]||(e[3]=[s("i",{class:"bi bi-clipboard-fill"},null,-1)])))]),_:1})],8,W)])])])])])]))}},R=v(z,[["__scopeId","data-v-b0ea2d46"]]);export{R as default};
|
1
src/static/app/dist/assets/peerJobs-tiQPhspt.js
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
import{S as p,a as b}from"./schedulePeerJob-Ci8HK7bA.js";import{_ as h,W as u,z as m,k as i,a as o,c as a,b as e,d as r,w as _,F as v,h as f,i as J,e as x,T as g}from"./index-DZliHkQD.js";import{L as w}from"./localeText-DG9SnJT8.js";import"./vue-datepicker-4ltJE5cT.js";import"./dayjs.min-PaIL06iQ.js";const P={name:"peerJobs",setup(){return{store:u()}},props:{selectedPeer:Object},components:{LocaleText:w,SchedulePeerJob:p,ScheduleDropdown:b},data(){return{}},methods:{deleteJob(d){this.selectedPeer.jobs=this.selectedPeer.jobs.filter(t=>t.JobID!==d.JobID)},addJob(){this.selectedPeer.jobs.unshift(JSON.parse(JSON.stringify({JobID:m().toString(),Configuration:this.selectedPeer.configuration.Name,Peer:this.selectedPeer.id,Field:this.store.PeerScheduleJobs.dropdowns.Field[0].value,Operator:this.store.PeerScheduleJobs.dropdowns.Operator[0].value,Value:"",CreationDate:"",ExpireDate:"",Action:this.store.PeerScheduleJobs.dropdowns.Action[0].value})))}}},S={class:"peerSettingContainer w-100 h-100 position-absolute top-0 start-0 overflow-y-scroll"},y={class:"container d-flex h-100 w-100"},$={class:"m-auto modal-dialog-centered dashboardModal"},C={class:"card rounded-3 shadow",style:{width:"700px"}},D={class:"card-header bg-transparent d-flex align-items-center gap-2 border-0 p-4 pb-2"},k={class:"mb-0 fw-normal"},j={class:"card-body px-4 pb-4 pt-2 position-relative"},T={class:"d-flex align-items-center mb-3"},N={class:"card shadow-sm",key:"none",style:{height:"153px"}},I={class:"card-body text-muted text-center d-flex"},L={class:"m-auto"};function O(d,t,B,F,V,A){const n=i("LocaleText"),l=i("SchedulePeerJob");return o(),a("div",S,[e("div",y,[e("div",$,[e("div",C,[e("div",D,[e("h4",k,[r(n,{t:"Schedule Jobs"})]),e("button",{type:"button",class:"btn-close ms-auto",onClick:t[0]||(t[0]=s=>this.$emit("close"))})]),e("div",j,[e("div",T,[e("button",{class:"btn bg-primary-subtle border-1 border-primary-subtle text-primary-emphasis rounded-3 shadow",onClick:t[1]||(t[1]=s=>this.addJob())},[t[3]||(t[3]=e("i",{class:"bi bi-plus-lg me-2"},null,-1)),r(n,{t:"Job"})])]),r(g,{name:"schedulePeerJobTransition",tag:"div",class:"position-relative"},{default:_(()=>[(o(!0),a(v,null,f(this.selectedPeer.jobs,(s,E)=>(o(),J(l,{onRefresh:t[2]||(t[2]=c=>this.$emit("refresh")),onDelete:c=>this.deleteJob(s),dropdowns:this.store.PeerScheduleJobs.dropdowns,key:s.JobID,pjob:s},null,8,["onDelete","dropdowns","pjob"]))),128)),this.selectedPeer.jobs.length===0?(o(),a("div",N,[e("div",I,[e("h6",L,[r(n,{t:"This peer does not have any job yet."})])])])):x("",!0)]),_:1})])])])])])}const q=h(P,[["render",O],["__scopeId","data-v-5bbdd42b"]]);export{q as default};
|
1
src/static/app/dist/assets/peerJobs-voXURBEt.css
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
.schedulePeerJobTransition-move[data-v-5bbdd42b],.schedulePeerJobTransition-enter-active[data-v-5bbdd42b],.schedulePeerJobTransition-leave-active[data-v-5bbdd42b]{transition:all .4s cubic-bezier(.82,.58,.17,.9)}.schedulePeerJobTransition-enter-from[data-v-5bbdd42b],.schedulePeerJobTransition-leave-to[data-v-5bbdd42b]{opacity:0;transform:scale(.9)}.schedulePeerJobTransition-leave-active[data-v-5bbdd42b]{position:absolute;width:100%}
|
1
src/static/app/dist/assets/peerJobsAllModal-BnD87caA.js
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
import{S as _}from"./schedulePeerJob-Ci8HK7bA.js";import{_ as g,W as v,k as c,a as t,c as r,b as e,d as l,F as p,h as b,t as m,e as f,i as y}from"./index-DZliHkQD.js";import{L as x}from"./localeText-DG9SnJT8.js";import"./vue-datepicker-4ltJE5cT.js";import"./dayjs.min-PaIL06iQ.js";const J={name:"peerJobsAllModal",setup(){return{store:v()}},components:{LocaleText:x,SchedulePeerJob:_},props:{configurationPeers:Array[Object]},computed:{getAllJobs(){return this.configurationPeers.filter(a=>a.jobs.length>0)}}},k={class:"peerSettingContainer w-100 h-100 position-absolute top-0 start-0 overflow-y-scroll"},w={class:"container d-flex h-100 w-100"},$={class:"m-auto modal-dialog-centered dashboardModal"},A={class:"card rounded-3 shadow",style:{width:"900px"}},L={class:"card-header bg-transparent d-flex align-items-center gap-2 border-0 p-4 pb-2"},S={class:"mb-0 fw-normal"},C={class:"card-body px-4 pb-4 pt-2"},P={key:0,class:"accordion",id:"peerJobsLogsModalAccordion"},j={class:"accordion-header"},M=["data-bs-target"],B={key:0},N={class:"text-muted"},D=["id"],T={class:"accordion-body"},V={key:1,class:"card shadow-sm",style:{height:"153px"}},F={class:"card-body text-muted text-center d-flex"},O={class:"m-auto"};function W(a,o,E,I,R,q){const n=c("LocaleText"),u=c("SchedulePeerJob");return t(),r("div",k,[e("div",w,[e("div",$,[e("div",A,[e("div",L,[e("h4",S,[l(n,{t:"All Active Jobs"})]),e("button",{type:"button",class:"btn-close ms-auto",onClick:o[0]||(o[0]=s=>this.$emit("close"))})]),e("div",C,[e("button",{class:"btn bg-primary-subtle border-1 border-primary-subtle text-primary-emphasis rounded-3 shadow mb-2",onClick:o[1]||(o[1]=s=>this.$emit("allLogs"))},[o[4]||(o[4]=e("i",{class:"bi bi-clock me-2"},null,-1)),l(n,{t:"Logs"})]),this.getAllJobs.length>0?(t(),r("div",P,[(t(!0),r(p,null,b(this.getAllJobs,(s,d)=>(t(),r("div",{class:"accordion-item",key:s.id},[e("h2",j,[e("button",{class:"accordion-button collapsed",type:"button","data-bs-toggle":"collapse","data-bs-target":"#collapse_"+d},[e("small",null,[e("strong",null,[s.name?(t(),r("span",B,m(s.name)+" • ",1)):f("",!0),e("samp",N,m(s.id),1)])])],8,M)]),e("div",{id:"collapse_"+d,class:"accordion-collapse collapse","data-bs-parent":"#peerJobsLogsModalAccordion"},[e("div",T,[(t(!0),r(p,null,b(s.jobs,i=>(t(),y(u,{onDelete:o[2]||(o[2]=h=>this.$emit("refresh")),onRefresh:o[3]||(o[3]=h=>this.$emit("refresh")),dropdowns:this.store.PeerScheduleJobs.dropdowns,viewOnly:!0,key:i.JobID,pjob:i},null,8,["dropdowns","pjob"]))),128))])],8,D)]))),128))])):(t(),r("div",V,[e("div",F,[e("span",O,[l(n,{t:"No active job at the moment."})])])]))])])])])])}const U=g(J,[["render",W]]);export{U as default};
|
1
src/static/app/dist/assets/peerJobsLogsModal-9H0FTeh1.js
vendored
Normal file
2
src/static/app/dist/assets/peerList-CT7gvMs4.js
vendored
Normal file
7
src/static/app/dist/assets/peerList-EJuRsJAQ.css
vendored
Normal file
1
src/static/app/dist/assets/peerQRCode-B62rUhHV.css
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
@media screen and (max-width: 768px){#qrcode[data-v-7c287bf3]{width:100%!important;height:auto!important;aspect-ratio:1/1}}
|
1
src/static/app/dist/assets/peerQRCode-Dxvodnam.js
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
import{b as i}from"./browser-CjSdxGTc.js";import{L as c}from"./localeText-DG9SnJT8.js";import{_ as l,D as p,g as _,k as m,a as n,c as r,b as e,d as u,n as h,e as f}from"./index-DZliHkQD.js";const g={name:"peerQRCode",components:{LocaleText:c},props:{selectedPeer:Object},setup(){return{dashboardStore:p()}},data(){return{loading:!0}},mounted(){_("/api/downloadPeer/"+this.$route.params.id,{id:this.selectedPeer.id},o=>{if(this.loading=!1,o.status){let t="";if(this.selectedPeer.configuration.Protocol==="awg"){let a={containers:[{awg:{isThirdPartyConfig:!0,last_config:o.data.file,port:this.selectedPeer.configuration.ListenPort,transport_proto:"udp"},container:"amnezia-awg"}],defaultContainer:"amnezia-awg",description:this.selectedPeer.name,hostName:this.dashboardStore.Configuration.Peers.remote_endpoint};t=btoa(JSON.stringify(a))}else t=o.data.file;i.toCanvas(document.querySelector("#qrcode"),t,a=>{a&&console.error(a)})}else this.dashboardStore.newMessage("Server",o.message,"danger")})}},b={class:"peerSettingContainer w-100 h-100 position-absolute top-0 start-0"},v={class:"container d-flex h-100 w-100"},C={class:"m-auto modal-dialog-centered dashboardModal justify-content-center"},w={class:"card rounded-3 shadow"},P={class:"card-header bg-transparent d-flex align-items-center gap-2 border-0 p-4 pb-0"},x={class:"mb-0"},S={class:"card-body p-4"},y={class:"d-flex"},L={key:0,class:"spinner-border m-auto",role:"status"};function k(o,t,a,N,s,$){const d=m("LocaleText");return n(),r("div",b,[e("div",v,[e("div",C,[e("div",w,[e("div",P,[e("h4",x,[u(d,{t:"QR Code"})]),e("button",{type:"button",class:"btn-close ms-auto",onClick:t[0]||(t[0]=Q=>this.$emit("close"))})]),e("div",S,[e("div",y,[e("canvas",{id:"qrcode",class:h(["rounded-3 shadow animate__animated animate__fadeIn animate__faster",{"d-none":s.loading}])},null,2),s.loading?(n(),r("div",L,t[1]||(t[1]=[e("span",{class:"visually-hidden"},"Loading...",-1)]))):f("",!0)])])])])])])}const q=l(g,[["render",k],["__scopeId","data-v-7c287bf3"]]);export{q as default};
|
1
src/static/app/dist/assets/peerSearchBar-BrbvrjWX.css
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
.searchPeersContainer[data-v-b741afe7]{width:100%}
|
1
src/static/app/dist/assets/peerSearchBar-JmFRkc04.js
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
import{_ as u,q as m,G as p,r as b,W as f,a2 as h,o as g,a as v,i as y,w as _,b as e,m as x,y as w,d as S,j as B}from"./index-DZliHkQD.js";import{L as T}from"./localeText-DG9SnJT8.js";const C={class:"fixed-bottom w-100 bottom-0 z-2",style:{"z-index":"1"}},P={class:"container-fluid"},k={class:"row g-0"},L={class:"col-md-9 col-lg-10 d-flex justify-content-center py-2"},V={class:"rounded-3 p-2 border shadow searchPeersContainer bg-body-tertiary"},j={class:"d-flex gap-1 align-items-center px-2"},z=["placeholder"],D={__name:"peerSearchBar",emits:["close"],setup(G,{emit:n}){const l=m(()=>p("Search Peers..."));let t;const o=b(""),r=f(),i=()=>{t?(clearTimeout(t),t=setTimeout(()=>{r.searchString=o.value},300)):t=setTimeout(()=>{r.searchString=o.value},300)},d=n,c=h("searchBar");return g(()=>{c.value.focus()}),(M,s)=>(v(),y(B,{name:"slideUp",appear:"",type:"animation",style:{"animation-delay":"1s"}},{default:_(()=>[e("div",C,[e("div",P,[e("div",k,[s[5]||(s[5]=e("div",{class:"col-md-3 col-lg-2"},null,-1)),e("div",L,[e("div",V,[e("div",j,[s[4]||(s[4]=e("h6",{class:"mb-0 me-2"},[e("label",{for:"searchPeers"},[e("i",{class:"bi bi-search"})])],-1)),x(e("input",{ref:"searchBar",class:"flex-grow-1 form-control rounded-3 bg-secondary-subtle border-1 border-secondary-subtle",placeholder:l.value,id:"searchPeers",onKeyup:s[0]||(s[0]=a=>i()),"onUpdate:modelValue":s[1]||(s[1]=a=>o.value=a)},null,40,z),[[w,o.value]]),e("button",{onClick:s[2]||(s[2]=a=>d("close")),style:{"white-space":"nowrap"},class:"btn bg-secondary-subtle text-secondary-emphasis border-secondary-subtle rounded-3 d-flex align-items-center"},[e("span",null,[s[3]||(s[3]=e("i",{class:"bi bi-x-circle-fill me-2"},null,-1)),S(T,{t:"Done"})])])])])])])])])]),_:1}))}},W=u(D,[["__scopeId","data-v-b741afe7"]]);export{W as default};
|
1
src/static/app/dist/assets/peerSettings-BkfvIQvk.css
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
.toggleShowKey[data-v-12b3ae8e]{position:absolute;top:35px;right:12px}
|
1
src/static/app/dist/assets/peerSettings-Bne_y8f7.js
vendored
Normal file
1
src/static/app/dist/assets/peerShareLinkModal-Cl59KMsF.js
vendored
Normal file
1
src/static/app/dist/assets/peerShareLinkModal-ouYxVldA.css
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
.card[data-v-8cfb4d4d]{border-color:var(--bs-border-color)!important}textarea[data-v-6e705c87]:focus,input[data-v-6e705c87]:focus{box-shadow:none;border-color:var(--bs-border-color)!important}textarea[data-v-6e705c87]{padding:var(--bs-card-spacer-y) var(--bs-card-spacer-x)}
|
1
src/static/app/dist/assets/ping-CrzXKpFV.js
vendored
Normal file
1
src/static/app/dist/assets/ping-DojRH9NX.css
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
.pingPlaceholder[data-v-a08ce97e]{width:100%;height:79.98px}.ping-move[data-v-a08ce97e],.ping-enter-active[data-v-a08ce97e],.ping-leave-active[data-v-a08ce97e]{transition:all .4s cubic-bezier(.82,.58,.17,.9)}.ping-leave-active[data-v-a08ce97e]{position:absolute;width:100%}.ping-enter-from[data-v-a08ce97e],.ping-leave-to[data-v-a08ce97e]{opacity:0;filter:blur(3px)}
|
1
src/static/app/dist/assets/protocolBadge-rpLx04Ri.js
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
import{L as r}from"./localeText-DG9SnJT8.js";import{a as t,c as n,f as i,i as s,e as a}from"./index-DZliHkQD.js";const d={key:0,class:"badge wireguardBg rounded-3 shadow"},c={key:1,class:"badge amneziawgBg rounded-3 shadow"},u={__name:"protocolBadge",props:{protocol:String,mini:!1},setup(e){return(m,o)=>e.protocol==="wg"?(t(),n("span",d,[o[0]||(o[0]=i(" WireGuard ")),e.mini?a("",!0):(t(),s(r,{key:0,t:"Configuration"}))])):e.protocol==="awg"?(t(),n("span",c,[o[1]||(o[1]=i(" AmneziaWG ")),e.mini?a("",!0):(t(),s(r,{key:0,t:"Configuration"}))])):a("",!0)}};export{u as _};
|
4
src/static/app/dist/assets/restoreConfiguration-CNCGDtFu.js
vendored
Normal file
1
src/static/app/dist/assets/restoreConfiguration-VgIx_N7z.css
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
.dropdownIcon[data-v-ccf48ac7]{transition:all .2s ease-in-out}.dropdownIcon.active[data-v-ccf48ac7]{transform:rotate(180deg)}.steps{&[data-v-324df2b1]{transition:all .3s ease-in-out;opacity:.3}&.active[data-v-324df2b1]{opacity:1}}
|
1
src/static/app/dist/assets/schedulePeerJob-Ci8HK7bA.js
vendored
Normal file
1
src/static/app/dist/assets/schedulePeerJob-DPy59wup.css
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
.btn.disabled[data-v-6a5aba2a]{opacity:1;background-color:#0d6efd17;border-color:transparent}[data-v-4aa63a3e]{font-size:.875rem}input[data-v-4aa63a3e]{padding:.1rem .4rem}input[data-v-4aa63a3e]:disabled{border-color:transparent;background-color:#0d6efd17;color:#0d6efd}.dp__main[data-v-4aa63a3e]{width:auto;flex-grow:1;--dp-input-padding: 2.5px 30px 2.5px 12px;--dp-border-radius: .5rem}
|
1
src/static/app/dist/assets/selectPeers-7CTijh1V.js
vendored
Normal file
1
src/static/app/dist/assets/selectPeers-Wjnh8YUZ.css
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
.card[data-v-177407c1]{height:100%}.dashboardModal[data-v-177407c1]{height:calc(100% - 1rem)!important}@media screen and (min-height: 700px){.card[data-v-177407c1]{height:700px}}.peerBtn[data-v-177407c1]{border:var(--bs-border-width) solid var(--bs-border-color)}.peerBtn.active[data-v-177407c1]{border:var(--bs-border-width) solid var(--bs-body-color)}
|
1
src/static/app/dist/assets/settings-CrxzSFju.js
vendored
Normal file
1
src/static/app/dist/assets/settings-DbQs6Bt_.css
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
@media screen and (max-width: 992px){.apiKey-card-body{&[data-v-a76253c8]{flex-direction:column!important;align-items:start!important}div.ms-auto[data-v-a76253c8]{margin-left:0!important}div[data-v-a76253c8]{width:100%;align-items:start!important}small[data-v-a76253c8]{margin-right:auto}}}.apiKey-move[data-v-100ee9f9],.apiKey-enter-active[data-v-100ee9f9],.apiKey-leave-active[data-v-100ee9f9]{transition:all .5s ease}.apiKey-enter-from[data-v-100ee9f9],.apiKey-leave-to[data-v-100ee9f9]{opacity:0;transform:translateY(30px) scale(.9)}.apiKey-leave-active[data-v-100ee9f9]{position:absolute;width:100%}.dropdown-menu[data-v-4e34593e]{width:100%}.list-group{&[data-v-4aa2aed9]:first-child{border-top-left-radius:var(--bs-border-radius-lg);border-top-right-radius:var(--bs-border-radius-lg)}&[data-v-4aa2aed9]:last-child{border-bottom-left-radius:var(--bs-border-radius-lg);border-bottom-right-radius:var(--bs-border-radius-lg)}}
|
1
src/static/app/dist/assets/setup-BmUSkunY.js
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
import{_ as u,D as m,A as p,c as r,b as e,d as o,f as c,t as h,e as f,m as l,y as d,a as i,k as w}from"./index-DZliHkQD.js";import{L as g}from"./localeText-DG9SnJT8.js";const b={name:"setup",components:{LocaleText:g},setup(){return{store:m()}},data(){return{setup:{username:"",newPassword:"",repeatNewPassword:"",enable_totp:!0},loading:!1,errorMessage:"",done:!1}},computed:{goodToSubmit(){return this.setup.username&&this.setup.newPassword.length>=8&&this.setup.repeatNewPassword.length>=8&&this.setup.newPassword===this.setup.repeatNewPassword}},methods:{submit(){this.loading=!0,p("/api/Welcome_Finish",this.setup,n=>{n.status?(this.done=!0,this.$router.push("/2FASetup")):(document.querySelectorAll("#createAccount input").forEach(s=>s.classList.add("is-invalid")),this.errorMessage=n.message,document.querySelector(".login-container-fluid").scrollTo({top:0,left:0,behavior:"smooth"})),this.loading=!1})}}},_=["data-bs-theme"],x={class:"m-auto text-body",style:{width:"500px"}},v={class:"dashboardLogo display-4"},y={class:"mb-5"},P={key:0,class:"alert alert-danger"},N={class:"d-flex flex-column gap-3"},k={id:"createAccount",class:"d-flex flex-column gap-2"},S={class:"form-group text-body"},T={for:"username",class:"mb-1 text-muted"},C={class:"form-group text-body"},L={for:"password",class:"mb-1 text-muted"},V={class:"form-group text-body"},A={for:"confirmPassword",class:"mb-1 text-muted"},$=["disabled"],q={key:0,class:"d-flex align-items-center w-100"},M={key:1,class:"d-flex align-items-center w-100"};function B(n,s,D,E,U,F){const t=w("LocaleText");return i(),r("div",{class:"container-fluid login-container-fluid d-flex main pt-5 overflow-scroll","data-bs-theme":this.store.Configuration.Server.dashboard_theme},[e("div",x,[e("span",v,[o(t,{t:"Nice to meet you!"})]),e("p",y,[o(t,{t:"Please fill in the following fields to finish setup"}),s[4]||(s[4]=c(" 😊"))]),e("div",null,[e("h3",null,[o(t,{t:"Create an account"})]),this.errorMessage?(i(),r("div",P,h(this.errorMessage),1)):f("",!0),e("div",N,[e("form",k,[e("div",S,[e("label",T,[e("small",null,[o(t,{t:"Enter an username you like"})])]),l(e("input",{type:"text",autocomplete:"username","onUpdate:modelValue":s[0]||(s[0]=a=>this.setup.username=a),class:"form-control",id:"username",name:"username",required:""},null,512),[[d,this.setup.username]])]),e("div",C,[e("label",L,[e("small",null,[o(t,{t:"Enter a password"}),e("code",null,[o(t,{t:"(At least 8 characters and make sure is strong enough!)"})])])]),l(e("input",{type:"password",autocomplete:"new-password","onUpdate:modelValue":s[1]||(s[1]=a=>this.setup.newPassword=a),class:"form-control",id:"password",name:"password",required:""},null,512),[[d,this.setup.newPassword]])]),e("div",V,[e("label",A,[e("small",null,[o(t,{t:"Confirm password"})])]),l(e("input",{type:"password",autocomplete:"confirm-new-password","onUpdate:modelValue":s[2]||(s[2]=a=>this.setup.repeatNewPassword=a),class:"form-control",id:"confirmPassword",name:"confirmPassword",required:""},null,512),[[d,this.setup.repeatNewPassword]])])]),e("button",{class:"btn btn-dark btn-lg mb-5 d-flex btn-brand shadow align-items-center",ref:"signInBtn",disabled:!this.goodToSubmit||this.loading||this.done,onClick:s[3]||(s[3]=a=>this.submit())},[!this.loading&&!this.done?(i(),r("span",q,[o(t,{t:"Next"}),s[5]||(s[5]=e("i",{class:"bi bi-chevron-right ms-auto"},null,-1))])):(i(),r("span",M,[o(t,{t:"Saving..."}),s[6]||(s[6]=e("span",{class:"spinner-border ms-auto spinner-border-sm",role:"status"},[e("span",{class:"visually-hidden"},"Loading...")],-1))]))],8,$)])])])],8,_)}const j=u(b,[["render",B]]);export{j as default};
|
1
src/static/app/dist/assets/share-B4McccvP.css
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
.animate__fadeInUp[data-v-1b44aacd]{animation-timing-function:cubic-bezier(.42,0,.22,1)}
|
1
src/static/app/dist/assets/share-CsPN7pXl.js
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
import{_,r,D as p,g as u,c as m,b as t,d as c,J as h,a as f,k as b}from"./index-DZliHkQD.js";import{b as v}from"./browser-CjSdxGTc.js";import{L as y}from"./localeText-DG9SnJT8.js";const g={name:"share",components:{LocaleText:y},async setup(){const o=h(),e=r(!1),i=p(),n=r(""),s=r(void 0),l=r(new Blob);await u("/api/getDashboardTheme",{},d=>{n.value=d.data});const a=o.query.ShareID;return a===void 0||a.length===0?(s.value=void 0,e.value=!0):await u("/api/sharePeer/get",{ShareID:a},d=>{d.status?(s.value=d.data,l.value=new Blob([s.value.file],{type:"text/plain"})):s.value=void 0,e.value=!0}),{store:i,theme:n,peerConfiguration:s,blob:l}},mounted(){this.peerConfiguration&&v.toCanvas(document.querySelector("#qrcode"),this.peerConfiguration.file,o=>{o&&console.error(o)})},methods:{download(){const o=new Blob([this.peerConfiguration.file],{type:"text/plain"}),e=URL.createObjectURL(o),i=`${this.peerConfiguration.fileName}.conf`,n=document.createElement("a");n.href=e,n.download=i,n.click()}},computed:{getBlob(){return URL.createObjectURL(this.blob)}}},w=["data-bs-theme"],x={class:"m-auto text-body",style:{width:"500px"}},C={key:0,class:"text-center position-relative",style:{}},U={class:"position-absolute w-100 h-100 top-0 start-0 d-flex animate__animated animate__fadeInUp",style:{"animation-delay":"0.1s"}},I={class:"m-auto"},L={key:1,class:"d-flex align-items-center flex-column gap-3"},k={class:"h1 dashboardLogo text-center animate__animated animate__fadeInUp"},B={id:"qrcode",class:"rounded-3 shadow animate__animated animate__fadeInUp mb-3",ref:"qrcode"},D={class:"text-muted animate__animated animate__fadeInUp mb-1",style:{"animation-delay":"0.2s"}},R=["download","href"];function q(o,e,i,n,s,l){const a=b("LocaleText");return f(),m("div",{class:"container-fluid login-container-fluid d-flex main pt-5 overflow-scroll","data-bs-theme":this.theme},[t("div",x,[this.peerConfiguration?(f(),m("div",L,[t("div",k,[e[1]||(e[1]=t("h6",null,"WGDashboard",-1)),c(a,{t:"Scan QR Code with the WireGuard App to add peer"})]),t("canvas",B,null,512),t("p",D,[c(a,{t:"or click the button below to download the "}),e[2]||(e[2]=t("samp",null,".conf",-1)),c(a,{t:" file"})]),t("a",{download:this.peerConfiguration.fileName+".conf",href:l.getBlob,class:"btn btn-lg bg-primary-subtle text-primary-emphasis border-1 border-primary-subtle animate__animated animate__fadeInUp shadow-sm",style:{"animation-delay":"0.25s"}},e[3]||(e[3]=[t("i",{class:"bi bi-download"},null,-1)]),8,R)])):(f(),m("div",C,[e[0]||(e[0]=t("div",{class:"animate__animated animate__fadeInUp"},[t("h1",{style:{"font-size":"20rem",filter:"blur(1rem)","animation-duration":"7s"},class:"animate__animated animate__flash animate__infinite"},[t("i",{class:"bi bi-file-binary"})])],-1)),t("div",U,[t("h3",I,[c(a,{t:"Oh no... This link is either expired or invalid."})])])]))])],8,w)}const N=_(g,[["render",q],["__scopeId","data-v-1b44aacd"]]);export{N as default};
|
1
src/static/app/dist/assets/signin-BKQo1OCC.js
vendored
Normal file
1
src/static/app/dist/assets/signin-CHulm0U0.css
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
.dot.inactive[data-v-ed7817c7]{background-color:#dc3545;box-shadow:0 0 0 .2rem #dc354545}.spin[data-v-ed7817c7]{animation:spin-ed7817c7 1s infinite cubic-bezier(.82,.58,.17,.9)}@keyframes spin-ed7817c7{0%{transform:rotate(0)}to{transform:rotate(360deg)}}@media screen and (max-width: 768px){.remoteServerContainer[data-v-ed7817c7]{flex-direction:column}.remoteServerContainer .button-group button[data-v-ed7817c7]{width:100%}}@media screen and (max-width: 768px){.login-box[data-v-80e20da4]{width:100%!important}.login-box div[data-v-80e20da4]{width:auto!important}}
|
1
src/static/app/dist/assets/storageMount-Bl6vLa1N.css
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
.square[data-v-2ad535bb]{height:var(--2ec4d3bc);transition:background-color .5s cubic-bezier(.42,0,.22,1)}.square[data-v-9509d7a0]{height:var(--2dc8ab7e);transition:background-color .5s cubic-bezier(.42,0,.22,1)}
|
@ -0,0 +1 @@
|
|||||||
|
import{_ as b,p as m,r as p,q as v,a as t,c as r,d as g,w as x,s as n,n as f,b as l,t as d,e as C,j as w}from"./index-DZliHkQD.js";const y={class:"text-muted me-2"},_={class:"fw-bold"},k={__name:"cpuCore",props:{core_number:Number,percentage:Number,align:Boolean,square:Boolean},setup(e){m(c=>({"2ec4d3bc":o.value}));const u=e,s=p(!1),o=v(()=>u.square?"40px":"25px");return(c,a)=>(t(),r("div",{class:"flex-grow-1 square rounded-3 border position-relative p-2",onMouseenter:a[0]||(a[0]=i=>s.value=!0),onMouseleave:a[1]||(a[1]=i=>s.value=!1),style:n({"background-color":`rgb(13 110 253 / ${e.percentage*10}%)`})},[g(w,{name:"zoomReversed"},{default:x(()=>[s.value?(t(),r("div",{key:0,style:n([{"white-space":"nowrap"},{top:o.value}]),class:f(["floatingLabel z-3 border position-absolute d-block p-1 px-2 bg-body text-body rounded-3 border shadow d-flex",[e.align?"end-0":"start-0"]])},[l("small",y," Core #"+d(e.core_number+1),1),l("small",_,d(e.percentage)+"% ",1)],6)):C("",!0)]),_:1})],36))}},B=b(k,[["__scopeId","data-v-2ad535bb"]]);export{B as C};
|
1
src/static/app/dist/assets/systemStatus-C-kMgV1x.css
vendored
Normal file
@ -0,0 +1 @@
|
|||||||
|
.title[data-v-ffe5ad8f]{height:18px;text-overflow:ellipsis;overflow:hidden;white-space:nowrap}.process-move[data-v-977dc46d],.process-enter-active[data-v-977dc46d],.process-leave-active[data-v-977dc46d]{transition:all .5s cubic-bezier(.42,0,.22,1)}.process-enter-from[data-v-977dc46d],.process-leave-to[data-v-977dc46d]{opacity:0;transform:scale(.9)}.process-leave-active[data-v-977dc46d]{position:absolute;width:100%}.progress-bar[data-v-977dc46d]{width:0;transition:all 1s cubic-bezier(.42,0,.22,1)}.fadeIn[data-v-977dc46d]{opacity:0;animation:fadeIn-977dc46d .5s forwards cubic-bezier(.42,0,.22,1)}@keyframes fadeIn-977dc46d{0%{opacity:0;transform:translateY(30px)}to{opacity:1;transform:translateY(0)}}
|