mirror of
https://github.com/midoks/mdserver-web.git
synced 2026-10-10 03:49:25 +08:00
Compare commits
909
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
59a2765b2f | ||
|
|
612c104cca | ||
|
|
550ca7681a | ||
|
|
6a09706362 | ||
|
|
ca040e8986 | ||
|
|
6fcc980d5d | ||
|
|
b69adc9c7d | ||
|
|
67da343705 | ||
|
|
4b47e27a54 | ||
|
|
fba3ddc9a5 | ||
|
|
4769ce5be8 | ||
|
|
8e16cbf7f9 | ||
|
|
4225d4c040 | ||
|
|
b578f2fddc | ||
|
|
961c3423fd | ||
|
|
8040c8c67a | ||
|
|
5aca7d7c26 | ||
|
|
d9f99e6b7c | ||
|
|
3addb46708 | ||
|
|
e6b493237d | ||
|
|
7f42054a14 | ||
|
|
501af3da15 | ||
|
|
68727cbe91 | ||
|
|
c5207961f3 | ||
|
|
a04e05c7e8 | ||
|
|
5b8cca24aa | ||
|
|
cafe58f3fc | ||
|
|
d1e2781eb9 | ||
|
|
67c899fc84 | ||
|
|
7432548ff9 | ||
|
|
0c2c136097 | ||
|
|
2a7e21951b | ||
|
|
066185e404 | ||
|
|
bd431f1647 | ||
|
|
0a6e88d81d | ||
|
|
02c9988ef7 | ||
|
|
5ee7df4a46 | ||
|
|
5f382c0034 | ||
|
|
63d17c4d77 | ||
|
|
38722c1dc2 | ||
|
|
ea982b7261 | ||
|
|
73d9edced8 | ||
|
|
af03c004a3 | ||
|
|
ddd9e70ac2 | ||
|
|
8976a7a147 | ||
|
|
677ff8ec0c | ||
|
|
47d240e2b8 | ||
|
|
1f7bd76875 | ||
|
|
f04c70f022 | ||
|
|
5044cc5db9 | ||
|
|
4329b90ac4 | ||
|
|
f7873b6dcd | ||
|
|
a80d666786 | ||
|
|
0598704dce | ||
|
|
8f19a05755 | ||
|
|
f320d7cc69 | ||
|
|
1e97929a24 | ||
|
|
c66bbac97e | ||
|
|
1f7a0d2795 | ||
|
|
f05ce79e63 | ||
|
|
9f3d06191a | ||
|
|
2c6614a5ff | ||
|
|
905bad29cf | ||
|
|
9ba572a8ce | ||
|
|
85fc333d2c | ||
|
|
5bc27a4725 | ||
|
|
e5acf206f5 | ||
|
|
ae7afc2eb5 | ||
|
|
5c8527a5de | ||
|
|
274592229e | ||
|
|
60ddb13f8e | ||
|
|
f62af00fb7 | ||
|
|
9c6faefffd | ||
|
|
c9e27c2add | ||
|
|
97b1e8b9b3 | ||
|
|
18cdcd1436 | ||
|
|
7d783c64b6 | ||
|
|
0b65d69750 | ||
|
|
693dbb8cf1 | ||
|
|
117a99e729 | ||
|
|
45d4b8a5de | ||
|
|
a5791051b1 | ||
|
|
dc3f8f8e0b | ||
|
|
bf1d00c012 | ||
|
|
51cf851cea | ||
|
|
1e09408eb9 | ||
|
|
7659865caf | ||
|
|
ad1851d068 | ||
|
|
e174652afc | ||
|
|
a95e7aacd7 | ||
|
|
6aa638ef9a | ||
|
|
6f3d50bae5 | ||
|
|
301a5a95b8 | ||
|
|
d35b9dccc7 | ||
|
|
82680605ac | ||
|
|
5b148b3d1a | ||
|
|
714acd20e5 | ||
|
|
bf58cd8acc | ||
|
|
059053d27c | ||
|
|
185e73c26a | ||
|
|
569511d321 | ||
|
|
f88bf5059f | ||
|
|
05b63b8d2b | ||
|
|
3a197b273d | ||
|
|
f4fdbab41c | ||
|
|
f6b2940657 | ||
|
|
78b9ac5df0 | ||
|
|
3ea4272307 | ||
|
|
efc7cee9ad | ||
|
|
5c4805453d | ||
|
|
f6da241c85 | ||
|
|
d5b6eb2325 | ||
|
|
6ed2cddf71 | ||
|
|
9c31d76442 | ||
|
|
7aecbaf7df | ||
|
|
ee81a86d24 | ||
|
|
0a60d66732 | ||
|
|
c3e3cff472 | ||
|
|
194d46eb6a | ||
|
|
d66bc94edc | ||
|
|
fb4ffcd343 | ||
|
|
10c235e317 | ||
|
|
9c156928d8 | ||
|
|
771905d7f6 | ||
|
|
cd21b3f3e7 | ||
|
|
982026db24 | ||
|
|
f9b91d9989 | ||
|
|
f7b1159bd7 | ||
|
|
6aa49ddbcf | ||
|
|
866eddac1d | ||
|
|
af0cce4216 | ||
|
|
3951322d86 | ||
|
|
dfdd16b444 | ||
|
|
aa8fec2b7e | ||
|
|
2a8958ab84 | ||
|
|
f5125670ee | ||
|
|
2a8d4fb7a9 | ||
|
|
59f7fe2290 | ||
|
|
928db6e7e7 | ||
|
|
f3927bd6cf | ||
|
|
bb92727b56 | ||
|
|
21b80b1fce | ||
|
|
637da92834 | ||
|
|
f5859626a9 | ||
|
|
003449eb0a | ||
|
|
d27f926191 | ||
|
|
7483247d4d | ||
|
|
1ad841d3af | ||
|
|
27376d879c | ||
|
|
8b824447c1 | ||
|
|
a1d26dcd45 | ||
|
|
5e68c7bf0a | ||
|
|
481ae16713 | ||
|
|
2d2bd39f9e | ||
|
|
7246a878a5 | ||
|
|
542ee3cdb0 | ||
|
|
69503b223b | ||
|
|
78d8fa7ebf | ||
|
|
21e9d8f2d8 | ||
|
|
3c6adfbd44 | ||
|
|
c0e70abe6d | ||
|
|
f174e71228 | ||
|
|
7a9a811ea6 | ||
|
|
c7e3d9d9a3 | ||
|
|
161f7a1bc0 | ||
|
|
175cce0131 | ||
|
|
3735897ed2 | ||
|
|
30bfa5a630 | ||
|
|
594fdb4712 | ||
|
|
31a441ea02 | ||
|
|
06f7087fad | ||
|
|
e7b8f5afdc | ||
|
|
6b57addfa7 | ||
|
|
aea435fbaf | ||
|
|
9cd6656946 | ||
|
|
4f7df4b3c3 | ||
|
|
70d12b7b8d | ||
|
|
fe576c820a | ||
|
|
b48a53e0d7 | ||
|
|
4701839c33 | ||
|
|
916bc67c4d | ||
|
|
d7bba830cc | ||
|
|
7a28f8f57a | ||
|
|
41ee25aa7b | ||
|
|
c8aa91cfa3 | ||
|
|
98f27498b0 | ||
|
|
5444fe6455 | ||
|
|
9627c6e47b | ||
|
|
13597d6282 | ||
|
|
d09494e3aa | ||
|
|
d35439d286 | ||
|
|
ced48bab47 | ||
|
|
fe2df6cc8d | ||
|
|
8bdfe15101 | ||
|
|
42e9983b88 | ||
|
|
9b7c0b6f99 | ||
|
|
e421292c9e | ||
|
|
7b4d31779a | ||
|
|
f3b09056f3 | ||
|
|
a92ccea6c5 | ||
|
|
9da77a0a9b | ||
|
|
c233ecaaa7 | ||
|
|
5828b8b8cb | ||
|
|
a4a1a2c83c | ||
|
|
0e20bc182c | ||
|
|
99c0b0a81a | ||
|
|
3fa199fec4 | ||
|
|
194c36c6d8 | ||
|
|
2fb542f6b4 | ||
|
|
60af43ed31 | ||
|
|
9f02cf9744 | ||
|
|
846bd270df | ||
|
|
d910d4f462 | ||
|
|
13f5b1bf02 | ||
|
|
1fc3fd6247 | ||
|
|
36e3feb886 | ||
|
|
0ca34afacd | ||
|
|
ffb3288f3d | ||
|
|
e2f68a73ed | ||
|
|
92fa8da2f6 | ||
|
|
7c96ddd63e | ||
|
|
c4e7872179 | ||
|
|
d399cb2bb4 | ||
|
|
985cbc7eca | ||
|
|
27ed21a17d | ||
|
|
972165ba1a | ||
|
|
e76eb4d0e8 | ||
|
|
fd05c40dbb | ||
|
|
a1a7fa5c82 | ||
|
|
531de1407f | ||
|
|
ca455f13e8 | ||
|
|
ab5342b01f | ||
|
|
a5a57c5718 | ||
|
|
27999b21e6 | ||
|
|
d2c6caeef2 | ||
|
|
de30d8a41f | ||
|
|
d3abc8e7b2 | ||
|
|
b7aad65eb5 | ||
|
|
240b1dec27 | ||
|
|
4756e0f68b | ||
|
|
59f62b948a | ||
|
|
0ab0741031 | ||
|
|
2fd2f6e844 | ||
|
|
e534c6177a | ||
|
|
897c6322fc | ||
|
|
201e93f946 | ||
|
|
a78fd957ec | ||
|
|
3ce037b0a1 | ||
|
|
f3176340f8 | ||
|
|
7bfe096e8d | ||
|
|
066428fca8 | ||
|
|
a1313846c8 | ||
|
|
0433123abd | ||
|
|
72e99844dd | ||
|
|
3a7882e7fa | ||
|
|
bec2a9a12c | ||
|
|
8f02363782 | ||
|
|
939c847f3e | ||
|
|
a623039db7 | ||
|
|
b7f49a1839 | ||
|
|
f1f60870f6 | ||
|
|
3b04739fd4 | ||
|
|
fdf1e0b0be | ||
|
|
d3385f526f | ||
|
|
20f9095b93 | ||
|
|
757cf483a2 | ||
|
|
1712cfd024 | ||
|
|
2b524cbe75 | ||
|
|
607d384000 | ||
|
|
14cdc7b82e | ||
|
|
c3219bcba2 | ||
|
|
a12c4dd78e | ||
|
|
39c6f19592 | ||
|
|
f18c05a097 | ||
|
|
80f296ea19 | ||
|
|
b3e9283a58 | ||
|
|
2e140a3ae1 | ||
|
|
4d733c8886 | ||
|
|
62a7320baf | ||
|
|
3dc7c2f5c1 | ||
|
|
fe62bbee06 | ||
|
|
f7c0cd82a0 | ||
|
|
13a2014558 | ||
|
|
5981f94376 | ||
|
|
005e0f1513 | ||
|
|
63a0f0fbdc | ||
|
|
edc0be11a1 | ||
|
|
908b6e67c6 | ||
|
|
dfe46332db | ||
|
|
fcdf2e18b2 | ||
|
|
3c8f9bcf96 | ||
|
|
ebadd6d80d | ||
|
|
6062699074 | ||
|
|
df9d57ba70 | ||
|
|
ffca953539 | ||
|
|
bd4946124f | ||
|
|
8e18c63cee | ||
|
|
d201ca2574 | ||
|
|
b644f91090 | ||
|
|
931f51cf59 | ||
|
|
0909a11fd9 | ||
|
|
2ba2833699 | ||
|
|
20389263fb | ||
|
|
86141ccf3a | ||
|
|
b2fdf84ca0 | ||
|
|
8fbc456e1a | ||
|
|
4e43382496 | ||
|
|
0326d4f112 | ||
|
|
a87c13c5e0 | ||
|
|
5f3720c855 | ||
|
|
f548e020c4 | ||
|
|
7f21781c0b | ||
|
|
680b0f09f2 | ||
|
|
482943a63c | ||
|
|
2871aee18e | ||
|
|
ff46d9b36e | ||
|
|
fd5fcba543 | ||
|
|
fe04e02e88 | ||
|
|
93554332a7 | ||
|
|
b541c7e1b7 | ||
|
|
1dec1a286a | ||
|
|
b17510104d | ||
|
|
c2c8ea0ce6 | ||
|
|
acbdb013ae | ||
|
|
b025f21765 | ||
|
|
1afc8a69e5 | ||
|
|
743fc9e8d3 | ||
|
|
8d692d2b3b | ||
|
|
a0b362e691 | ||
|
|
98e1c2ae8f | ||
|
|
c0e5915285 | ||
|
|
667a0fd0fd | ||
|
|
8ed5f283b0 | ||
|
|
643c370d30 | ||
|
|
4601303065 | ||
|
|
14940d6734 | ||
|
|
fdc2b0f5c4 | ||
|
|
cf0b233f63 | ||
|
|
b3a7ef2206 | ||
|
|
9a3ca3e833 | ||
|
|
9afff8df3f | ||
|
|
ccd0b06223 | ||
|
|
7b495fe9bf | ||
|
|
2c3fb75e8f | ||
|
|
53b606602f | ||
|
|
ecef0cf994 | ||
|
|
3691d41b23 | ||
|
|
1f66457a3e | ||
|
|
8e05beccfd | ||
|
|
35424eae1f | ||
|
|
ed6df6d527 | ||
|
|
4366c4afde | ||
|
|
4bb059f62f | ||
|
|
aa8f618d9c | ||
|
|
dfd753e4c1 | ||
|
|
e38daf3c89 | ||
|
|
535838fc5e | ||
|
|
797853bd61 | ||
|
|
0de3aff045 | ||
|
|
0b7b48ab23 | ||
|
|
5ff0a0bfbc | ||
|
|
9131f44d67 | ||
|
|
6b86bb9d2b | ||
|
|
dc06d6beda | ||
|
|
2c8ea2498e | ||
|
|
6b01173b61 | ||
|
|
883c554850 | ||
|
|
d267da351c | ||
|
|
13a39f4d6a | ||
|
|
cff1d5ee89 | ||
|
|
bf5274c313 | ||
|
|
5623e3efac | ||
|
|
b93bb6c64c | ||
|
|
c9962f169e | ||
|
|
080f343ee2 | ||
|
|
5aeaf13f16 | ||
|
|
528094cfa7 | ||
|
|
ed3e3336b2 | ||
|
|
a75a49c2ae | ||
|
|
1204e72f41 | ||
|
|
13ca5544a5 | ||
|
|
e53fe26d52 | ||
|
|
13772399f6 | ||
|
|
98f11e2662 | ||
|
|
f0b0302f75 | ||
|
|
5b9b466cb9 | ||
|
|
5588a11ea5 | ||
|
|
da05a4ef19 | ||
|
|
34cf899529 | ||
|
|
0d7d67b012 | ||
|
|
5b778bfc0d | ||
|
|
6ce414cecf | ||
|
|
c8160b63e7 | ||
|
|
68d957fd6e | ||
|
|
f09c438500 | ||
|
|
9bc9944a3e | ||
|
|
9e4c2b13d5 | ||
|
|
cd7143047f | ||
|
|
d608ac4d94 | ||
|
|
9fedf9d10d | ||
|
|
9936191da2 | ||
|
|
538eba3acf | ||
|
|
e39ee9a6f7 | ||
|
|
e1820ad5b3 | ||
|
|
22133f1a6c | ||
|
|
0041cbdff3 | ||
|
|
889da3c769 | ||
|
|
d086eb9c61 | ||
|
|
832fa9cd1a | ||
|
|
8c19fb9629 | ||
|
|
a2e4dd6753 | ||
|
|
22ba7dda55 | ||
|
|
2ffa36eecb | ||
|
|
fe5843b35d | ||
|
|
e95acbe947 | ||
|
|
aed8319dd3 | ||
|
|
babe1b885e | ||
|
|
0612f52402 | ||
|
|
db67cea737 | ||
|
|
52cf675843 | ||
|
|
3421bf6ab5 | ||
|
|
2f39bd600b | ||
|
|
acd5ec69e8 | ||
|
|
09878728af | ||
|
|
af82e9f129 | ||
|
|
c74630e738 | ||
|
|
e4e0697b4b | ||
|
|
11b5ac658b | ||
|
|
1306731164 | ||
|
|
bb6c933aee | ||
|
|
d6a93ef1e6 | ||
|
|
6610da402a | ||
|
|
3ace9cf51e | ||
|
|
49c7ec6073 | ||
|
|
8cf526667e | ||
|
|
6c42876411 | ||
|
|
79471e3806 | ||
|
|
cad06425fc | ||
|
|
6f1e11e474 | ||
|
|
59d13e50ce | ||
|
|
5da0746240 | ||
|
|
192ea8b029 | ||
|
|
bfc20b3e42 | ||
|
|
56bee5c752 | ||
|
|
312df5de1f | ||
|
|
4d3640286c | ||
|
|
20930806b6 | ||
|
|
71360b61da | ||
|
|
411645c9e2 | ||
|
|
3de4e1bfff | ||
|
|
34285f6051 | ||
|
|
84f0cda89b | ||
|
|
04012085ce | ||
|
|
9ad00f3ce7 | ||
|
|
609a4ed351 | ||
|
|
5ca74fd312 | ||
|
|
3b1bb06c68 | ||
|
|
b6b7de42d6 | ||
|
|
3aa3b532ac | ||
|
|
290c740ab1 | ||
|
|
23fd4ca1de | ||
|
|
c128a799b2 | ||
|
|
401c103b76 | ||
|
|
6fb88eb428 | ||
|
|
956f20d295 | ||
|
|
03f8578a37 | ||
|
|
265e17beac | ||
|
|
f3d83ad8a5 | ||
|
|
39b376a7cb | ||
|
|
07858794ef | ||
|
|
391473fc14 | ||
|
|
0993adcc5f | ||
|
|
90b1d88111 | ||
|
|
87098cff95 | ||
|
|
e6e64b2a4c | ||
|
|
81436ea81b | ||
|
|
cf7d29190d | ||
|
|
4ff62ec6ad | ||
|
|
dac3ccce8d | ||
|
|
10bd7aa70f | ||
|
|
dec0afc3d3 | ||
|
|
2331853b3b | ||
|
|
b9e063e423 | ||
|
|
52294948b2 | ||
|
|
87bec2b988 | ||
|
|
9ab91ecc93 | ||
|
|
18312a3596 | ||
|
|
8fb35440c8 | ||
|
|
a874934379 | ||
|
|
2c4e4aea3f | ||
|
|
6b115261be | ||
|
|
059ce6228b | ||
|
|
cfa396b4bb | ||
|
|
11e4b377ed | ||
|
|
31fe2aab92 | ||
|
|
19042a9088 | ||
|
|
2eb4543c3b | ||
|
|
e33ccbfc7d | ||
|
|
ed8c9eed0a | ||
|
|
7163a7bbcf | ||
|
|
3f1c23ec8b | ||
|
|
3a6cb0ae2a | ||
|
|
936492d410 | ||
|
|
39d265aae7 | ||
|
|
5a33098dd3 | ||
|
|
c9927c3fb9 | ||
|
|
da90386816 | ||
|
|
6be4c5977b | ||
|
|
bbf93bfd9e | ||
|
|
a2570af5f7 | ||
|
|
77120cf31b | ||
|
|
395f449b93 | ||
|
|
8be5f4aa7b | ||
|
|
3ccebf4f47 | ||
|
|
15d48accad | ||
|
|
d0045d831c | ||
|
|
a7cac3d671 | ||
|
|
7bc7fdecae | ||
|
|
480663e2ad | ||
|
|
2290efd060 | ||
|
|
dece5a0532 | ||
|
|
b0a44d24d4 | ||
|
|
d092d9f5f2 | ||
|
|
944b3e779c | ||
|
|
55dc2715e9 | ||
|
|
84bf3abd5d | ||
|
|
00c9157f9b | ||
|
|
faa95b0d88 | ||
|
|
52d0b3c940 | ||
|
|
0141028d41 | ||
|
|
9e6bf08a72 | ||
|
|
49ff9750cb | ||
|
|
75b2b4dbc2 | ||
|
|
667c60dda0 | ||
|
|
aa6818f953 | ||
|
|
6f884fe60a | ||
|
|
a56d53c672 | ||
|
|
00b1788172 | ||
|
|
cee268fcbc | ||
|
|
40dd929823 | ||
|
|
b59949e181 | ||
|
|
9c5975253a | ||
|
|
62003b987a | ||
|
|
df0a454c3a | ||
|
|
02048c3265 | ||
|
|
65649d171d | ||
|
|
a136da6ec2 | ||
|
|
3b25ca7e60 | ||
|
|
c5cf8f8afb | ||
|
|
89f7d464ab | ||
|
|
4cac6d1921 | ||
|
|
dca5c82d2f | ||
|
|
08988130b5 | ||
|
|
8849001384 | ||
|
|
ae0a6b5079 | ||
|
|
d6c6ae5561 | ||
|
|
a6dc15f22d | ||
|
|
360b962bd2 | ||
|
|
37d37e0f1d | ||
|
|
ddc6da9172 | ||
|
|
ceba7ab673 | ||
|
|
384704b8e5 | ||
|
|
232d0c2f74 | ||
|
|
ef8e6f07f5 | ||
|
|
435e565559 | ||
|
|
ec9d7e337d | ||
|
|
b5c7a2a778 | ||
|
|
72d9c9b761 | ||
|
|
30812e1fba | ||
|
|
c95cb07e8d | ||
|
|
f3f2ea8ef0 | ||
|
|
9e5a5192cb | ||
|
|
271b3d448c | ||
|
|
b7424e7195 | ||
|
|
b607527e1f | ||
|
|
3c42a4e450 | ||
|
|
b11fd9508a | ||
|
|
dc0504cf24 | ||
|
|
1d8b82395f | ||
|
|
39ac55760e | ||
|
|
ef6071956d | ||
|
|
bbbe1c84ce | ||
|
|
064252daad | ||
|
|
3cb63c6265 | ||
|
|
b541abad42 | ||
|
|
547027be0d | ||
|
|
d786b2524a | ||
|
|
abac345628 | ||
|
|
d4290cbdd2 | ||
|
|
cfd2d99e24 | ||
|
|
28437ccfb0 | ||
|
|
43bf755609 | ||
|
|
de225fc8b9 | ||
|
|
44f891f7a3 | ||
|
|
64d1387b03 | ||
|
|
51ccbefb04 | ||
|
|
29e3017e51 | ||
|
|
197e2d80c6 | ||
|
|
b8f04043bc | ||
|
|
2ed70b4b96 | ||
|
|
d056c37be5 | ||
|
|
e75abae4b9 | ||
|
|
3e695770d7 | ||
|
|
7301edbc5a | ||
|
|
704b256408 | ||
|
|
e129051a06 | ||
|
|
d38e9f7ad0 | ||
|
|
c696742866 | ||
|
|
046389df99 | ||
|
|
3e244ed227 | ||
|
|
5016aedb0f | ||
|
|
c1cd45a5f3 | ||
|
|
79c9865a5d | ||
|
|
4082f29424 | ||
|
|
d777a29d8c | ||
|
|
04e43e051e | ||
|
|
dffe9a77e7 | ||
|
|
1b1462586e | ||
|
|
f11d2db761 | ||
|
|
842c16540a | ||
|
|
614cc1032f | ||
|
|
ad46b2050e | ||
|
|
8109989927 | ||
|
|
9d9b39dfe2 | ||
|
|
422514c40a | ||
|
|
632568643b | ||
|
|
a5f29a2a6a | ||
|
|
b02705aa5a | ||
|
|
1f9ab560e3 | ||
|
|
b3970326cd | ||
|
|
f6d008e0b6 | ||
|
|
6f9a49ad7a | ||
|
|
fc3c7d334a | ||
|
|
1030d1b923 | ||
|
|
94d4a90bcc | ||
|
|
da4b872666 | ||
|
|
cf15b19f4b | ||
|
|
aa9cdaada4 | ||
|
|
659c9fe16b | ||
|
|
2b8c6d6e43 | ||
|
|
263f83088f | ||
|
|
374c938ac0 | ||
|
|
6aa73b0cae | ||
|
|
7f731ae06f | ||
|
|
f87aff704c | ||
|
|
be9fa87133 | ||
|
|
de6fc19dee | ||
|
|
3987c6d3dc | ||
|
|
96875052cd | ||
|
|
583de57f96 | ||
|
|
41b7245f03 | ||
|
|
ee3684706f | ||
|
|
07936dfb81 | ||
|
|
5c46202be0 | ||
|
|
7123a15457 | ||
|
|
4aeb45b1d5 | ||
|
|
006a62be22 | ||
|
|
1e91d11125 | ||
|
|
99fb4aa723 | ||
|
|
5c5bbb2da2 | ||
|
|
8b05045901 | ||
|
|
1b73439525 | ||
|
|
ec51ad016a | ||
|
|
a42fae66ac | ||
|
|
b4c635c2e5 | ||
|
|
9802bc5915 | ||
|
|
196fbbdd7d | ||
|
|
fa278abd50 | ||
|
|
f9b2dbcb5e | ||
|
|
a8808be9ae | ||
|
|
4122de13e3 | ||
|
|
4acae8e5f5 | ||
|
|
1abb611c3e | ||
|
|
726f9fa0d2 | ||
|
|
cfb64caadc | ||
|
|
f501dd5150 | ||
|
|
d1fe006e39 | ||
|
|
d4db5dbfac | ||
|
|
efa512a3f6 | ||
|
|
b53436343d | ||
|
|
bb1a634f3c | ||
|
|
acda55cc55 | ||
|
|
0ff0728517 | ||
|
|
c575f55354 | ||
|
|
d58339dbcb | ||
|
|
f6a18fa25f | ||
|
|
3016eff372 | ||
|
|
ca518591f3 | ||
|
|
d2c74f2243 | ||
|
|
71dbf8e359 | ||
|
|
cb2ad0ca82 | ||
|
|
a157db7879 | ||
|
|
fb949f4ee6 | ||
|
|
b91ae3fde2 | ||
|
|
b508e8003b | ||
|
|
0632a4b535 | ||
|
|
397e0b89a5 | ||
|
|
2427bf81cd | ||
|
|
4179bbf580 | ||
|
|
b93c3bb88e | ||
|
|
a52c854afd | ||
|
|
d7e5cf1f45 | ||
|
|
cfb444deac | ||
|
|
78615451ac | ||
|
|
0134643c83 | ||
|
|
70ddd1c86f | ||
|
|
808dc743ef | ||
|
|
e30693f9f2 | ||
|
|
5c97fbe011 | ||
|
|
6419a18525 | ||
|
|
4a804e9243 | ||
|
|
d44526004f | ||
|
|
c1f402b278 | ||
|
|
45a3eec1b5 | ||
|
|
d6f41ceb6a | ||
|
|
a1bc0f6faf | ||
|
|
8420961cf7 | ||
|
|
421594c01d | ||
|
|
ca13e8ad77 | ||
|
|
f161f24253 | ||
|
|
e5ab03951f | ||
|
|
5b45bab36d | ||
|
|
0594d3f9ce | ||
|
|
ad541216c5 | ||
|
|
1adc0c1405 | ||
|
|
b22185b374 | ||
|
|
02f3107e93 | ||
|
|
52fece867d | ||
|
|
2e3578dcf1 | ||
|
|
73b09a7886 | ||
|
|
f33c7e5a5e | ||
|
|
3e90db2c8d | ||
|
|
d599363609 | ||
|
|
ec3167f239 | ||
|
|
2b1099b6e9 | ||
|
|
e55a0760e3 | ||
|
|
5c1353a38f | ||
|
|
f95684b316 | ||
|
|
6fc729ccf9 | ||
|
|
45d0c4fc4a | ||
|
|
6759b9055c | ||
|
|
de694b2465 | ||
|
|
c7b3daca36 | ||
|
|
452769233f | ||
|
|
8d196a3aa1 | ||
|
|
01baf509ad | ||
|
|
d59ee2c843 | ||
|
|
8919d5734e | ||
|
|
bb91b30d6d | ||
|
|
103ab2e099 | ||
|
|
0d33d0db79 | ||
|
|
6d9f4cfa08 | ||
|
|
c88c95ba21 | ||
|
|
a705f6a8e4 | ||
|
|
3f40ed0a01 | ||
|
|
39653896bf | ||
|
|
31d665d962 | ||
|
|
811fa4a40d | ||
|
|
320f58424d | ||
|
|
376f0f0a4e | ||
|
|
0efb700cee | ||
|
|
ff3fcae366 | ||
|
|
22d94ce67c | ||
|
|
042fd7ac6d | ||
|
|
1a48f52dad | ||
|
|
fc5970c65d | ||
|
|
e758698ee2 | ||
|
|
ea03bb056a | ||
|
|
088346fd5e | ||
|
|
cbcb686363 | ||
|
|
628175a8ee | ||
|
|
96d0115257 | ||
|
|
801fdf51a7 | ||
|
|
24fa3a7ca9 | ||
|
|
3dc781498c | ||
|
|
c3afaeb35a | ||
|
|
4390180a89 | ||
|
|
98e22f6eeb | ||
|
|
3bfd0f34f5 | ||
|
|
ec8e46494a | ||
|
|
7cb9b0acec | ||
|
|
f97d2a5987 | ||
|
|
5a50fe70a8 | ||
|
|
4ecb28575e | ||
|
|
07bf8f162c | ||
|
|
19beb1bccc | ||
|
|
33c27d7924 | ||
|
|
a9fb6c9ec9 | ||
|
|
7198a4de95 | ||
|
|
53cb56ddf2 | ||
|
|
e4582f328c | ||
|
|
bb7f1b6a7d | ||
|
|
1781d879d1 | ||
|
|
d89158124f | ||
|
|
bfdad5f9a3 | ||
|
|
165d9ba14d | ||
|
|
ad102aa940 | ||
|
|
1489d15d74 | ||
|
|
2dbda3e6dd | ||
|
|
4775d4f756 | ||
|
|
f57fd600e6 | ||
|
|
bf281107b2 | ||
|
|
083fe59fa5 | ||
|
|
8f36e0ccb2 | ||
|
|
a5b3a650eb | ||
|
|
177a08af91 | ||
|
|
5ef4b3d05d | ||
|
|
ad3d93cf8b | ||
|
|
72643b7776 | ||
|
|
45128636b0 | ||
|
|
7b962335e4 | ||
|
|
c0c67095be | ||
|
|
b3a0f11a80 | ||
|
|
088337765c | ||
|
|
066e7087c9 | ||
|
|
29144595a6 | ||
|
|
45c2b3cdca | ||
|
|
e3ecdfb2b3 | ||
|
|
560d482f31 | ||
|
|
62ffc94b69 | ||
|
|
0cb10cc926 | ||
|
|
fb4eb8747c | ||
|
|
1a26b83a46 | ||
|
|
7c2316f005 | ||
|
|
149baeb1fc | ||
|
|
9714bcb419 | ||
|
|
4e6a54dbad | ||
|
|
18498813c6 | ||
|
|
854e34a90c | ||
|
|
a0898c849e | ||
|
|
3a26b71828 | ||
|
|
f500dcac8b | ||
|
|
3a6e2e6e4b | ||
|
|
1d439e788c | ||
|
|
75a2fcdaaf | ||
|
|
2ca8598671 | ||
|
|
2d42486ac5 | ||
|
|
24f22b5b8c | ||
|
|
68f8638638 | ||
|
|
fe6b16eda4 | ||
|
|
4e35c34854 | ||
|
|
cc51d9501f | ||
|
|
c08cfd4763 | ||
|
|
5fdcb8bf71 | ||
|
|
ce3b232c4b | ||
|
|
ec7ae65f7d | ||
|
|
e4c5212ac4 | ||
|
|
23442accaf | ||
|
|
2e6f5f9add | ||
|
|
110e356fae | ||
|
|
a243d088ee | ||
|
|
365fd466c2 | ||
|
|
d1304de69e | ||
|
|
e51fa2608d | ||
|
|
cbfcd00317 | ||
|
|
7a96d3abc5 | ||
|
|
33a510a5b5 | ||
|
|
e8942128b8 | ||
|
|
7e2bd928e2 | ||
|
|
89185476aa | ||
|
|
f491641ab8 | ||
|
|
8b108d1aff | ||
|
|
2cfa7ec2ab | ||
|
|
d63aae100d | ||
|
|
e8249adc53 | ||
|
|
281427e479 | ||
|
|
af775fc77d | ||
|
|
f328354125 | ||
|
|
1f70f962cf | ||
|
|
414787447e | ||
|
|
979f2174e5 | ||
|
|
84d1c96094 | ||
|
|
0d8d9022a0 | ||
|
|
44d0e9e11c | ||
|
|
771d6e04ba | ||
|
|
87563daa1d | ||
|
|
9fefb285e1 | ||
|
|
b9ca4fc11e | ||
|
|
2646d0565d | ||
|
|
e1f900f1c5 | ||
|
|
908b3341b2 | ||
|
|
5aef2467b1 | ||
|
|
f59114a688 | ||
|
|
363ed8b3ff | ||
|
|
3970feacc3 | ||
|
|
f6c528149e | ||
|
|
8f6b5ddba9 | ||
|
|
ef350b9fd2 | ||
|
|
004dfb5e7d | ||
|
|
3c08d7c159 | ||
|
|
e227204065 | ||
|
|
ce653b7a6a | ||
|
|
a207215775 | ||
|
|
8115b0fb4d | ||
|
|
048bd573e8 | ||
|
|
918ea8d0f9 | ||
|
|
97d2fd5a63 | ||
|
|
617a517048 | ||
|
|
2e7910a84c | ||
|
|
2ee4c4e3fd | ||
|
|
9ae4d32ed4 | ||
|
|
90a7f4ea10 | ||
|
|
d22cb8305d | ||
|
|
a7a78f2103 | ||
|
|
db7ec00c07 | ||
|
|
dd21d25295 | ||
|
|
f7360aa081 | ||
|
|
1d1ba7192a | ||
|
|
0bf540c5e7 |
@@ -0,0 +1,16 @@
|
||||
name: sync2gitee
|
||||
on: [push]
|
||||
|
||||
jobs:
|
||||
repo-sync:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Mirror the Github organization repos to Gitee.
|
||||
uses: Yikun/hub-mirror-action@master
|
||||
with:
|
||||
src: 'github/midoks'
|
||||
dst: 'gitee/midoks'
|
||||
dst_key: ${{ secrets.GITEE_PRIVATE_KEY }}
|
||||
dst_token: ${{ secrets.GITEE_TOKEN }}
|
||||
static_list: "mdserver-web"
|
||||
force_update: true
|
||||
+10
-2
@@ -151,14 +151,18 @@ data/system.db-journal
|
||||
data/hook_*.json
|
||||
data/letsencrypt.json
|
||||
data/basic_auth.json
|
||||
data/notify.json
|
||||
data/api.json
|
||||
data/bind_domain.pl
|
||||
data/unauthorized_status.pl
|
||||
|
||||
plugins/vip_*
|
||||
plugins/own_*
|
||||
plugins/my_*
|
||||
plugins/l2tp
|
||||
plugins/openlitespeed
|
||||
plugins/migration_api
|
||||
plugins/system_safe
|
||||
plugins/tamper_proof
|
||||
plugins/cryptocurrency_trade
|
||||
plugins/gdrive
|
||||
plugins/mtproxy
|
||||
plugins/zimg
|
||||
@@ -171,3 +175,7 @@ plugins/file_search
|
||||
debug.out
|
||||
|
||||
|
||||
|
||||
|
||||
mdioks.session-journal
|
||||
*.session
|
||||
|
||||
@@ -29,6 +29,13 @@
|
||||
基本上可以使用,后续会继续优化!欢迎提供意见!
|
||||
|
||||
- 吹水组 - https://t.me/mdserver_web
|
||||
- 交流论坛 - https://bbs.midoks.me
|
||||
|
||||
```
|
||||
如果出现问题,最好私给我面板信息。不要让我猜。如果不提供,不要提出问题,自行解决。 — 座右铭
|
||||
Talk is cheap, show me the code. -- linus
|
||||
```
|
||||
|
||||
- [兼容性测试报告](/compatibility.md)
|
||||
- [常用命令说明](/cmd.md)
|
||||
|
||||
@@ -38,6 +45,7 @@
|
||||
* PHP[53-82] - PHP是世界上最好的编程语言。
|
||||
* MySQL - 一种关系数据库管理系统。
|
||||
* MariaDB - 是MySQL的一个重要分支。
|
||||
* MySQL[APT/YUM] - 一种关系数据库管理系统。
|
||||
* MongoDB - 一种非关系NOSQL数据库管理系统。
|
||||
* phpMyAdmin - 著名Web端MySQL管理工具。
|
||||
* Memcached - 一个高性能的分布式内存对象缓存系统。
|
||||
@@ -66,12 +74,12 @@ phpMyAdmin[4.4.15]支持MySQL[5.5-5.7]
|
||||
phpMyAdmin[5.2.0]支持MySQL[8.0]
|
||||
|
||||
PHP[53-72]支持phpMyAdmin[4.4.15]
|
||||
PHP[72-81]支持phpMyAdmin[5.2.0]
|
||||
PHP[72-82]支持phpMyAdmin[5.2.0]
|
||||
```
|
||||
|
||||
# 特别赞助
|
||||
|
||||
- [找资源 - 阿里云盘资源搜索引擎](https://zhaoziyuan.la/)
|
||||
- [找资源 - 阿里云盘资源搜索引擎](https://zhaoziyuan.pw/)
|
||||
|
||||
# AD - VPS推荐 - 🙏
|
||||
|
||||
@@ -84,45 +92,41 @@ PHP[72-81]支持phpMyAdmin[5.2.0]
|
||||
# Docker
|
||||
|
||||
- 由[DDSRem](https://github.com/DDSRem)开发维护。
|
||||
- https://hub.docker.com/r/ddsderek/mw-server
|
||||
- https://hub.docker.com/r/ddsderek/mw
|
||||
|
||||
```
|
||||
docker run -itd --name mw-server --privileged=true -p 7200:7200 -p 80:80 -p 443:443 -p 888:888 ddsderek/mw-server:latest
|
||||
```
|
||||
|
||||
|
||||
### 版本更新 0.12.2
|
||||
|
||||
* 开放菜单权限配置。
|
||||
* 升级SSH终端2.0。
|
||||
* 增加已安装类型。
|
||||
* 加入切换linux软件源的命令。
|
||||
* iptables安装优化。
|
||||
* 网站统计POST获取数据优化。
|
||||
* mysql[apt/yum]迁移优化。
|
||||
* 优化防火墙导入。
|
||||
* 图标可设置。
|
||||
* 各种细节优化。
|
||||
### 版本更新 0.15.2
|
||||
|
||||
* OpenResty升级到1.21.4.2
|
||||
* 计划任务自动脚本运行时间
|
||||
* rsyncd添加优化
|
||||
* 使用mariadb二进制导入备份文件
|
||||
* docker插件-镜像导入导出功能
|
||||
* 网站设置反向代理升级功能
|
||||
* 优化初始化安装SSH端口识别
|
||||
|
||||
### JSDelivr安装地址
|
||||
|
||||
- 初始安装
|
||||
|
||||
```
|
||||
curl -fsSL https://cdn.jsdelivr.net/gh/midoks/mdserver-web@latest/scripts/install.sh | bash
|
||||
curl --insecure -fsSL https://cdn.jsdelivr.net/gh/midoks/mdserver-web@latest/scripts/install.sh | bash
|
||||
```
|
||||
|
||||
- 直接更新
|
||||
|
||||
```
|
||||
curl -fsSL https://cdn.jsdelivr.net/gh/midoks/mdserver-web@latest/scripts/update.sh | bash
|
||||
curl --insecure -fsSL https://cdn.jsdelivr.net/gh/midoks/mdserver-web@latest/scripts/update.sh | bash
|
||||
```
|
||||
|
||||
- 卸载脚本
|
||||
|
||||
```
|
||||
wget -O uninstall.sh https://cdn.jsdelivr.net/gh/midoks/mdserver-web@latest/scripts/uninstall.sh && bash uninstall.sh
|
||||
wget --no-check-certificate -O uninstall.sh https://cdn.jsdelivr.net/gh/midoks/mdserver-web@latest/scripts/uninstall.sh && bash uninstall.sh
|
||||
```
|
||||
|
||||
### 备用地址
|
||||
@@ -130,20 +134,20 @@ wget -O uninstall.sh https://cdn.jsdelivr.net/gh/midoks/mdserver-web@latest/scri
|
||||
- 初始安装
|
||||
|
||||
```
|
||||
curl -fsSL https://raw.githubusercontent.com/midoks/mdserver-web/master/scripts/install.sh | bash
|
||||
|
||||
curl --insecure -fsSL https://raw.githubusercontent.com/midoks/mdserver-web/master/scripts/install.sh | bash
|
||||
curl --insecure -fsSL https://code.midoks.me/midoks/mdserver-web/raw/branch/master/scripts/install.sh | bash
|
||||
```
|
||||
|
||||
- 直接更新
|
||||
|
||||
```
|
||||
curl -fsSL https://raw.githubusercontent.com/midoks/mdserver-web/master/scripts/update.sh | bash
|
||||
curl --insecure -fsSL https://raw.githubusercontent.com/midoks/mdserver-web/master/scripts/update.sh | bash
|
||||
```
|
||||
|
||||
- 卸载脚本
|
||||
|
||||
```
|
||||
wget -O uninstall.sh https://raw.githubusercontent.com/midoks/mdserver-web/master/scripts/uninstall.sh && bash uninstall.sh
|
||||
wget --no-check-certificate -O uninstall.sh https://raw.githubusercontent.com/midoks/mdserver-web/master/scripts/uninstall.sh && bash uninstall.sh
|
||||
```
|
||||
|
||||
|
||||
@@ -152,22 +156,22 @@ wget -O uninstall.sh https://raw.githubusercontent.com/midoks/mdserver-web/maste
|
||||
- 需已经安装mdserver-web
|
||||
|
||||
```
|
||||
curl -fsSL https://raw.githubusercontent.com/midoks/mdserver-web/dev/scripts/quick/app.sh | bash
|
||||
curl --insecure -fsSL https://raw.githubusercontent.com/midoks/mdserver-web/dev/scripts/quick/app.sh | bash
|
||||
```
|
||||
|
||||
|
||||
### DEV使用
|
||||
|
||||
```
|
||||
curl -fsSL https://raw.githubusercontent.com/midoks/mdserver-web/dev/scripts/install_dev.sh | bash
|
||||
curl -fsSL https://raw.githubusercontent.com/midoks/mdserver-web/dev/scripts/update_dev.sh | bash
|
||||
curl --insecure -fsSL https://raw.githubusercontent.com/midoks/mdserver-web/dev/scripts/install_dev.sh | bash
|
||||
curl --insecure -fsSL https://raw.githubusercontent.com/midoks/mdserver-web/dev/scripts/update_dev.sh | bash
|
||||
|
||||
wget -O uninstall.sh https://raw.githubusercontent.com/midoks/mdserver-web/dev/scripts/uninstall.sh && bash uninstall.sh
|
||||
wget --no-check-certificate -O uninstall.sh https://raw.githubusercontent.com/midoks/mdserver-web/dev/scripts/uninstall.sh && bash uninstall.sh
|
||||
|
||||
curl -fsSL https://raw.githubusercontent.com/midoks/mdserver-web/dev/scripts/quick/debug.sh | bash
|
||||
curl --insecure -fsSL https://raw.githubusercontent.com/midoks/mdserver-web/dev/scripts/quick/debug.sh | bash
|
||||
|
||||
curl -fsSL https://gitee.com/midoks/mdserver-web/raw/dev/scripts/install_dev.sh | bash
|
||||
curl -fsSL https://gitee.com/midoks/mdserver-web/raw/dev/scripts/update_dev.sh | bash
|
||||
curl --insecure -fsSL https://code.midoks.me/midoks/mdserver-web/raw/branch/dev/scripts/install_dev.sh | bash
|
||||
curl --insecure -fsSL https://code.midoks.me/midoks/mdserver-web/raw/branch/dev/scripts/update_dev.sh | bash
|
||||
```
|
||||
|
||||
### 捐赠地址 USDT(TRC20)
|
||||
|
||||
@@ -28,6 +28,7 @@ from route import app, socketio
|
||||
from gevent.pywsgi import WSGIServer
|
||||
from geventwebsocket.handler import WebSocketHandler
|
||||
|
||||
|
||||
from gevent import monkey
|
||||
monkey.patch_all()
|
||||
|
||||
|
||||
+10
-10
@@ -371,8 +371,7 @@ fi
|
||||
response = urllib.request.urlopen(req, timeout=timeout)
|
||||
return response
|
||||
except Exception as ex:
|
||||
|
||||
self.getError()
|
||||
# self.getError()
|
||||
raise Exception("requestsPost: {}".format(self.getError(str(ex))))
|
||||
|
||||
def getRequestJson(self, response):
|
||||
@@ -931,7 +930,8 @@ fi
|
||||
index).encode()
|
||||
)
|
||||
X509Req.set_pubkey(pk)
|
||||
X509Req.set_version(2)
|
||||
# X509Req.set_version(2)
|
||||
X509Req.set_version(0)
|
||||
X509Req.sign(pk, self.__digest)
|
||||
return OpenSSL.crypto.dump_certificate_request(OpenSSL.crypto.FILETYPE_ASN1, X509Req)
|
||||
|
||||
@@ -1007,7 +1007,7 @@ fi
|
||||
try:
|
||||
result = {}
|
||||
x509 = OpenSSL.crypto.load_certificate(
|
||||
OpenSSL.crypto.FILETYPE_PEM, public.readFile(pem_file))
|
||||
OpenSSL.crypto.FILETYPE_PEM, mw.readFile(pem_file))
|
||||
# 取产品名称
|
||||
issuer = x509.get_issuer()
|
||||
result['issuer'] = ''
|
||||
@@ -1281,16 +1281,16 @@ fullchain.pem 粘贴到证书输入框
|
||||
# 获取当前正在使用此证书的网站目录
|
||||
def getSslUsedSite(self, save_path):
|
||||
pkey_file = '{}/privkey.pem'.format(save_path)
|
||||
pkey = public.readFile(pkey_file)
|
||||
pkey = mw.readFile(pkey_file)
|
||||
if not pkey:
|
||||
return False
|
||||
cert_paths = 'vhost/cert'
|
||||
import panelSite
|
||||
args = public.dict_obj()
|
||||
args = mw.dict_obj()
|
||||
args.siteName = ''
|
||||
for c_name in os.listdir(cert_paths):
|
||||
skey_file = '{}/{}/privkey.pem'.format(cert_paths, c_name)
|
||||
skey = public.readFile(skey_file)
|
||||
skey = mw.readFile(skey_file)
|
||||
if not skey:
|
||||
continue
|
||||
if skey == pkey:
|
||||
@@ -1298,7 +1298,7 @@ fullchain.pem 粘贴到证书输入框
|
||||
run_path = panelSite.panelSite().GetRunPath(args)
|
||||
if not run_path:
|
||||
continue
|
||||
sitePath = public.M('sites').where(
|
||||
sitePath = mw.M('sites').where(
|
||||
'name=?', c_name).getField('path')
|
||||
if not sitePath:
|
||||
continue
|
||||
@@ -1316,7 +1316,7 @@ fullchain.pem 粘贴到证书输入框
|
||||
self.__config['orders'] = {}
|
||||
import panelSite
|
||||
siteObj = panelSite.panelSite()
|
||||
args = public.dict_obj()
|
||||
args = mw.dict_obj()
|
||||
for siteName in os.listdir(cert_path):
|
||||
try:
|
||||
cert_file = '{}/{}/fullchain.pem'.format(cert_path, siteName)
|
||||
@@ -1522,7 +1522,7 @@ fullchain.pem 粘贴到证书输入框
|
||||
self.__config['orders'][i]['auth_to'] = auth_to
|
||||
|
||||
# 是否到了允许重试的时间
|
||||
if 'next_retry_time' in self._config['orders'][i]:
|
||||
if 'next_retry_time' in self.__config['orders'][i]:
|
||||
timeout = self.__config['orders'][i][
|
||||
'next_retry_time'] - int(time.time())
|
||||
if timeout > 0:
|
||||
|
||||
@@ -840,7 +840,8 @@ class cert_request:
|
||||
index).encode()
|
||||
)
|
||||
X509Req.set_pubkey(pk)
|
||||
X509Req.set_version(2)
|
||||
# X509Req.set_version(2)
|
||||
X509Req.set_version(0)
|
||||
X509Req.sign(pk, self.__digest)
|
||||
return OpenSSL.crypto.dump_certificate_request(OpenSSL.crypto.FILETYPE_ASN1, X509Req)
|
||||
|
||||
@@ -916,7 +917,7 @@ class cert_request:
|
||||
try:
|
||||
result = {}
|
||||
x509 = OpenSSL.crypto.load_certificate(
|
||||
OpenSSL.crypto.FILETYPE_PEM, public.readFile(pem_file))
|
||||
OpenSSL.crypto.FILETYPE_PEM, mw.readFile(pem_file))
|
||||
# 取产品名称
|
||||
issuer = x509.get_issuer()
|
||||
result['issuer'] = ''
|
||||
@@ -1186,16 +1187,16 @@ fullchain.pem 粘贴到证书输入框
|
||||
# 获取当前正在使用此证书的网站目录
|
||||
def getSslUsedSite(self, save_path):
|
||||
pkey_file = '{}/privkey.pem'.format(save_path)
|
||||
pkey = public.readFile(pkey_file)
|
||||
pkey = mw.readFile(pkey_file)
|
||||
if not pkey:
|
||||
return False
|
||||
cert_paths = 'vhost/cert'
|
||||
import panelSite
|
||||
args = public.dict_obj()
|
||||
args = mw.dict_obj()
|
||||
args.siteName = ''
|
||||
for c_name in os.listdir(cert_paths):
|
||||
skey_file = '{}/{}/privkey.pem'.format(cert_paths, c_name)
|
||||
skey = public.readFile(skey_file)
|
||||
skey = mw.readFile(skey_file)
|
||||
if not skey:
|
||||
continue
|
||||
if skey == pkey:
|
||||
@@ -1203,7 +1204,7 @@ fullchain.pem 粘贴到证书输入框
|
||||
run_path = panelSite.panelSite().GetRunPath(args)
|
||||
if not run_path:
|
||||
continue
|
||||
sitePath = public.M('sites').where(
|
||||
sitePath = mw.M('sites').where(
|
||||
'name=?', c_name).getField('path')
|
||||
if not sitePath:
|
||||
continue
|
||||
@@ -1221,7 +1222,7 @@ fullchain.pem 粘贴到证书输入框
|
||||
self.__config['orders'] = {}
|
||||
import panelSite
|
||||
siteObj = panelSite.panelSite()
|
||||
args = public.dict_obj()
|
||||
args = mw.dict_obj()
|
||||
for siteName in os.listdir(cert_path):
|
||||
try:
|
||||
cert_file = '{}/{}/fullchain.pem'.format(cert_path, siteName)
|
||||
|
||||
@@ -33,6 +33,7 @@ from flask import redirect
|
||||
|
||||
def init():
|
||||
initDB()
|
||||
initDBSshPort()
|
||||
initUserInfo()
|
||||
initInitD()
|
||||
initInitTask()
|
||||
@@ -62,6 +63,17 @@ def initDB():
|
||||
print(str(ex))
|
||||
|
||||
|
||||
def initDBSshPort():
|
||||
# SSH端口必须放开
|
||||
import firewall_api
|
||||
cmd_data = mw.execShell(
|
||||
"cat /etc/ssh/sshd_config | grep '^Port \d*' | tail -1")
|
||||
ssh_port = cmd_data[0].replace("Port ", '').strip()
|
||||
if ssh_port == '':
|
||||
ssh_port = '22'
|
||||
firewall_api.firewall_api().addAcceptPortArgs(ssh_port, 'SSH远程管理服务', 'port')
|
||||
|
||||
|
||||
def doContentReplace(src, dst):
|
||||
content = mw.readFile(src)
|
||||
content = content.replace("{$SERVER_PATH}", mw.getRunDir())
|
||||
|
||||
+234
-14
@@ -27,7 +27,7 @@ from flask import request
|
||||
|
||||
class config_api:
|
||||
|
||||
__version = '0.12.2'
|
||||
__version = '0.15.2'
|
||||
__api_addr = 'data/api.json'
|
||||
|
||||
def __init__(self):
|
||||
@@ -348,7 +348,23 @@ class config_api:
|
||||
keyPath = 'ssl/private.pem'
|
||||
certPath = 'ssl/cert.pem'
|
||||
if not os.path.exists(certPath):
|
||||
mw.createSSL()
|
||||
# 不再自动生成证书
|
||||
# mw.createSSL()
|
||||
cert['privateKey'] = ''
|
||||
cert['is_https'] = ''
|
||||
cert['certPem'] = ''
|
||||
cert['rep'] = os.path.exists('ssl/input.pl')
|
||||
cert['info'] = {'endtime': 0, 'subject': '无',
|
||||
'notAfter': '无', 'notBefore': '无', 'issuer': '无'}
|
||||
return cert
|
||||
|
||||
panel_ssl = mw.getServerDir() + "/web_conf/nginx/vhost/panel.conf"
|
||||
if not os.path.exists(panel_ssl):
|
||||
cert['is_https'] = ''
|
||||
else:
|
||||
ssl_data = mw.readFile(panel_ssl)
|
||||
if ssl_data.find('$server_port !~ 443') != -1:
|
||||
cert['is_https'] = 'checked'
|
||||
|
||||
cert['privateKey'] = mw.readFile(keyPath)
|
||||
cert['certPem'] = mw.readFile(certPath)
|
||||
@@ -380,13 +396,86 @@ class config_api:
|
||||
mw.writeFile('ssl/input.pl', 'True')
|
||||
return mw.returnJson(True, '证书已保存!')
|
||||
|
||||
# 设置面板SSL证书设置
|
||||
def setPanelHttpToHttpsApi(self):
|
||||
|
||||
bind_domain = 'data/bind_domain.pl'
|
||||
if not os.path.exists(bind_domain):
|
||||
return mw.returnJson(False, '先要绑定域名!')
|
||||
|
||||
keyPath = 'ssl/private.pem'
|
||||
if not os.path.exists(keyPath):
|
||||
return mw.returnJson(False, '未申请SSL证书!')
|
||||
|
||||
is_https = request.form.get('https', '').strip()
|
||||
|
||||
panel_ssl = mw.getServerDir() + "/web_conf/nginx/vhost/panel.conf"
|
||||
if not os.path.exists(panel_ssl):
|
||||
return mw.returnJson(False, '未开启面板SSL!')
|
||||
|
||||
if is_https == 'false':
|
||||
conf = mw.readFile(panel_ssl)
|
||||
if conf:
|
||||
if conf.find('ssl_certificate') == -1:
|
||||
return mw.returnJson(False, '当前未开启SSL')
|
||||
to = "#error_page 404/404.html;\n\
|
||||
#HTTP_TO_HTTPS_START\n\
|
||||
if ($server_port !~ 443){\n\
|
||||
rewrite ^(/.*)$ https://$host$1 permanent;\n\
|
||||
}\n\
|
||||
#HTTP_TO_HTTPS_END"
|
||||
conf = conf.replace('#error_page 404/404.html;', to)
|
||||
mw.writeFile(panel_ssl, conf)
|
||||
else:
|
||||
conf = mw.readFile(panel_ssl)
|
||||
if conf:
|
||||
rep = "\n\s*#HTTP_TO_HTTPS_START(.|\n){1,300}#HTTP_TO_HTTPS_END"
|
||||
conf = re.sub(rep, '', conf)
|
||||
rep = "\s+if.+server_port.+\n.+\n\s+\s*}"
|
||||
conf = re.sub(rep, '', conf)
|
||||
mw.writeFile(panel_ssl, conf)
|
||||
|
||||
mw.restartWeb()
|
||||
|
||||
action = '开启'
|
||||
if is_https == 'true':
|
||||
action = '关闭'
|
||||
return mw.returnJson(True, action + 'HTTPS跳转成功!')
|
||||
|
||||
# 删除面板证书
|
||||
def delPanelSslApi(self):
|
||||
bind_domain = 'data/bind_domain.pl'
|
||||
if not os.path.exists(bind_domain):
|
||||
return mw.returnJson(False, '未绑定域名!')
|
||||
|
||||
siteName = mw.readFile(bind_domain).strip()
|
||||
|
||||
src_letpath = mw.getServerDir() + '/web_conf/letsencrypt/' + siteName
|
||||
|
||||
dst_letpath = mw.getRunDir() + '/ssl'
|
||||
dst_csrpath = dst_letpath + '/cert.pem'
|
||||
dst_keypath = dst_letpath + '/private.pem'
|
||||
|
||||
if os.path.exists(src_letpath) or os.path.exists(dst_csrpath):
|
||||
if os.path.exists(src_letpath):
|
||||
mw.execShell('rm -rf ' + src_letpath)
|
||||
if os.path.exists(dst_csrpath):
|
||||
mw.execShell('rm -rf ' + dst_csrpath)
|
||||
if os.path.exists(dst_keypath):
|
||||
mw.execShell('rm -rf ' + dst_keypath)
|
||||
# mw.restartWeb()
|
||||
return mw.returnJson(True, '已经删除SSL!')
|
||||
|
||||
# mw.restartWeb()
|
||||
return mw.returnJson(False, '已经不存在SSL!')
|
||||
|
||||
# 申请面板let证书
|
||||
def applyPanelLetSslApi(self):
|
||||
|
||||
# check domain is bind?
|
||||
bind_domain = 'data/bind_domain.pl'
|
||||
if not os.path.exists(bind_domain):
|
||||
return mw.returnJson(False, '未绑定域名!')
|
||||
return mw.returnJson(False, '先要绑定域名!')
|
||||
|
||||
siteName = mw.readFile(bind_domain).strip()
|
||||
auth_to = mw.getRunDir() + "/tmp"
|
||||
@@ -404,6 +493,8 @@ class config_api:
|
||||
dst_csrpath = dst_letpath + '/cert.pem'
|
||||
dst_keypath = dst_letpath + '/private.pem'
|
||||
|
||||
is_already_apply = False
|
||||
|
||||
if not os.path.exists(src_letpath):
|
||||
import cert_api
|
||||
data = cert_api.cert_api().applyCertApi(to_args)
|
||||
@@ -415,6 +506,8 @@ class config_api:
|
||||
msg = msg + '<p><span>响应状态:</span>' + str(emsg['status']) + '</p><p><span>错误类型:</span>' + emsg[
|
||||
'type'] + '</p><p><span>错误代码:</span>' + emsg['detail'] + '</p>'
|
||||
return mw.returnJson(data['status'], msg, data['msg'])
|
||||
else:
|
||||
is_already_apply = True
|
||||
|
||||
mw.buildSoftLink(src_csrpath, dst_csrpath, True)
|
||||
mw.buildSoftLink(src_keypath, dst_keypath, True)
|
||||
@@ -426,6 +519,9 @@ class config_api:
|
||||
if os.path.exists(tmp_well_know):
|
||||
mw.execShell('rm -rf ' + tmp_well_know)
|
||||
|
||||
if is_already_apply:
|
||||
return mw.returnJson(True, '重复申请!', data)
|
||||
|
||||
return mw.returnJson(True, '申请成功!', data)
|
||||
|
||||
def setPanelDomainApi(self):
|
||||
@@ -509,8 +605,6 @@ class config_api:
|
||||
return mw.returnJson(True, 'SSL已关闭,请使用http协议访问面板!')
|
||||
else:
|
||||
try:
|
||||
if not os.path.exists('ssl/input.ssl'):
|
||||
mw.createSSL()
|
||||
mw.writeFile(sslConf, 'True')
|
||||
|
||||
keyPath = mw.getRunDir() + '/ssl/private.pem'
|
||||
@@ -647,9 +741,83 @@ class config_api:
|
||||
else:
|
||||
return False, ''
|
||||
|
||||
def setStatusCodeApi(self):
|
||||
status_code = request.form.get('status_code', '').strip()
|
||||
if re.match("^\d+$", status_code):
|
||||
status_code = int(status_code)
|
||||
if status_code != 0:
|
||||
if status_code < 100 or status_code > 999:
|
||||
return mw.returnJson(False, '状态码范围错误!')
|
||||
else:
|
||||
return mw.returnJson(False, '状态码范围错误!')
|
||||
|
||||
mw.writeFile('data/unauthorized_status.pl', str(status_code))
|
||||
mw.writeLog('面板设置', '将未授权响应状态码设置为:{}'.format(status_code))
|
||||
return mw.returnJson(True, '设置成功!')
|
||||
|
||||
def getNotifyApi(self):
|
||||
# 获取
|
||||
data = mw.getNotifyData(True)
|
||||
return mw.returnData(True, 'ok', data)
|
||||
|
||||
def setNotifyApi(self):
|
||||
tag = request.form.get('tag', '').strip()
|
||||
data = request.form.get('data', '').strip()
|
||||
|
||||
cfg = mw.getNotifyData(False)
|
||||
|
||||
crypt_data = mw.enDoubleCrypt(tag, data)
|
||||
if tag in cfg:
|
||||
cfg[tag]['cfg'] = crypt_data
|
||||
else:
|
||||
t = {'cfg': crypt_data}
|
||||
cfg[tag] = t
|
||||
|
||||
mw.writeNotify(cfg)
|
||||
return mw.returnData(True, '设置成功')
|
||||
|
||||
def setNotifyTestApi(self):
|
||||
# 异步通知验证
|
||||
tag = request.form.get('tag', '').strip()
|
||||
tag_data = request.form.get('data', '').strip()
|
||||
|
||||
if tag == 'tgbot':
|
||||
t = json.loads(tag_data)
|
||||
test_bool = mw.tgbotNotifyTest(t['app_token'], t['chat_id'])
|
||||
if test_bool:
|
||||
return mw.returnData(True, '验证成功')
|
||||
return mw.returnData(False, '验证失败')
|
||||
|
||||
if tag == 'email':
|
||||
t = json.loads(tag_data)
|
||||
test_bool = mw.emailNotifyTest(t)
|
||||
if test_bool:
|
||||
return mw.returnData(True, '验证成功')
|
||||
return mw.returnData(False, '验证失败')
|
||||
|
||||
return mw.returnData(False, '暂时未支持该验证')
|
||||
|
||||
def setNotifyEnableApi(self):
|
||||
# 异步通知验证
|
||||
tag = request.form.get('tag', '').strip()
|
||||
tag_enable = request.form.get('enable', '').strip()
|
||||
|
||||
data = mw.getNotifyData(False)
|
||||
op_enable = True
|
||||
op_action = '开启'
|
||||
if tag_enable != 'true':
|
||||
op_enable = False
|
||||
op_action = '关闭'
|
||||
|
||||
if tag in data:
|
||||
data[tag]['enable'] = op_enable
|
||||
|
||||
mw.writeNotify(data)
|
||||
|
||||
return mw.returnData(True, op_action + '成功')
|
||||
|
||||
def getPanelTokenApi(self):
|
||||
api_file = self.__api_addr
|
||||
|
||||
tmp = mw.readFile(api_file)
|
||||
if not os.path.exists(api_file):
|
||||
ready_data = {"open": False, "token": "", "limit_addr": []}
|
||||
@@ -668,7 +836,7 @@ class config_api:
|
||||
token = mw.getRandomString(32)
|
||||
data['token'] = mw.md5(token)
|
||||
data['token_crypt'] = mw.enCrypt(
|
||||
data['token'], token).decode('utf-8')
|
||||
data['token'], token)
|
||||
mw.writeFile(api_file, json.dumps(data))
|
||||
data['token'] = "***********************************"
|
||||
|
||||
@@ -680,10 +848,6 @@ class config_api:
|
||||
def setPanelTokenApi(self):
|
||||
op_type = request.form.get('op_type', '').strip()
|
||||
|
||||
api_file = self.__api_addr
|
||||
tmp = mw.readFile(api_file)
|
||||
data = json.loads(tmp)
|
||||
|
||||
if op_type == '1':
|
||||
token = mw.getRandomString(32)
|
||||
data['token'] = mw.md5(token)
|
||||
@@ -693,7 +857,14 @@ class config_api:
|
||||
mw.writeFile(api_file, json.dumps(data))
|
||||
return mw.returnJson(True, 'ok', token)
|
||||
|
||||
elif op_type == '2':
|
||||
api_file = self.__api_addr
|
||||
if not os.path.exists(api_file):
|
||||
return mw.returnJson(False, "先在API接口配置")
|
||||
else:
|
||||
tmp = mw.readFile(api_file)
|
||||
data = json.loads(tmp)
|
||||
|
||||
if op_type == '2':
|
||||
data['open'] = not data['open']
|
||||
stats = {True: '开启', False: '关闭'}
|
||||
if not 'token_crypt' in data:
|
||||
@@ -708,12 +879,38 @@ class config_api:
|
||||
return mw.returnJson(not not data['open'], token)
|
||||
|
||||
elif op_type == '3':
|
||||
|
||||
limit_addr = request.form.get('limit_addr', '').strip()
|
||||
data['limit_addr'] = limit_addr.split('\n')
|
||||
mw.writeLog('API配置', '变更IP限制为[%s]' % limit_addr)
|
||||
mw.writeFile(api_file, json.dumps(data))
|
||||
return mw.returnJson(True, '保存成功!')
|
||||
|
||||
def renderUnauthorizedStatus(self, data):
|
||||
cfg_unauth_status = 'data/unauthorized_status.pl'
|
||||
if os.path.exists(cfg_unauth_status):
|
||||
status_code = mw.readFile(cfg_unauth_status)
|
||||
data['status_code'] = status_code
|
||||
data['status_code_msg'] = status_code
|
||||
if status_code == '0':
|
||||
data['status_code_msg'] = "默认-安全入口错误提示"
|
||||
elif status_code == '400':
|
||||
data['status_code_msg'] = "400-客户端请求错误"
|
||||
elif status_code == '401':
|
||||
data['status_code_msg'] = "401-未授权访问"
|
||||
elif status_code == '403':
|
||||
data['status_code_msg'] = "403-拒绝访问"
|
||||
elif status_code == '404':
|
||||
data['status_code_msg'] = "404-页面不存在"
|
||||
elif status_code == '408':
|
||||
data['status_code_msg'] = "408-客户端超时"
|
||||
elif status_code == '416':
|
||||
data['status_code_msg'] = "416-无效的请求"
|
||||
else:
|
||||
data['status_code'] = '0'
|
||||
data['status_code_msg'] = "默认-安全入口错误提示"
|
||||
return data
|
||||
|
||||
def get(self):
|
||||
|
||||
data = {}
|
||||
@@ -766,6 +963,8 @@ class config_api:
|
||||
else:
|
||||
data['bind_domain'] = ''
|
||||
|
||||
data = self.renderUnauthorizedStatus(data)
|
||||
|
||||
api_token = self.__api_addr
|
||||
if os.path.exists(api_token):
|
||||
bac = mw.readFile(api_token)
|
||||
@@ -780,7 +979,9 @@ class config_api:
|
||||
data['username'] = mw.M('users').where(
|
||||
"id=?", (1,)).getField('username')
|
||||
|
||||
# databases hook 获取
|
||||
data['hook_tag'] = request.args.get('tag', '')
|
||||
|
||||
# databases hook
|
||||
database_hook_file = 'data/hook_database.json'
|
||||
if os.path.exists(database_hook_file):
|
||||
df = mw.readFile(database_hook_file)
|
||||
@@ -789,7 +990,7 @@ class config_api:
|
||||
else:
|
||||
data['hook_database'] = []
|
||||
|
||||
# menu hook 获取
|
||||
# menu hook
|
||||
menu_hook_file = 'data/hook_menu.json'
|
||||
if os.path.exists(menu_hook_file):
|
||||
df = mw.readFile(menu_hook_file)
|
||||
@@ -798,4 +999,23 @@ class config_api:
|
||||
else:
|
||||
data['hook_menu'] = []
|
||||
|
||||
# global_static hook
|
||||
global_static_hook_file = 'data/hook_global_static.json'
|
||||
if os.path.exists(global_static_hook_file):
|
||||
df = mw.readFile(global_static_hook_file)
|
||||
df = json.loads(df)
|
||||
data['hook_global_static'] = df
|
||||
else:
|
||||
data['hook_global_static'] = []
|
||||
|
||||
# notiy config
|
||||
notify_data = mw.getNotifyData(True)
|
||||
notify_tag_list = ['tgbot', 'email']
|
||||
for tag in notify_tag_list:
|
||||
new_tag = 'notify_' + tag + '_enable'
|
||||
data[new_tag] = ''
|
||||
if tag in notify_data and 'enable' in notify_data[tag]:
|
||||
if notify_data[tag]['enable']:
|
||||
data[new_tag] = 'checked'
|
||||
|
||||
return data
|
||||
|
||||
@@ -508,6 +508,22 @@ class crontab_api:
|
||||
else:
|
||||
head = "#!/bin/bash\nPATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin\nexport PATH\n"
|
||||
|
||||
start_head = '''
|
||||
SCRIPT_RUN_TIME="0s"
|
||||
MW_ToSeconds()
|
||||
{
|
||||
SEC=$1
|
||||
if [ $SEC -lt 60 ]; then
|
||||
SCRIPT_RUN_TIME="${SEC}s"
|
||||
elif [ $SEC -ge 60 ] && [ $SEC -lt 3600 ];then
|
||||
SCRIPT_RUN_TIME="$(( SEC / 60 ))m$(( SEC % 60 ))s"
|
||||
elif [ $SEC -ge 3600 ]; then
|
||||
SCRIPT_RUN_TIME="$(( SEC / 3600 ))h$(( (SEC % 3600) / 60 ))m$(( (SEC % 3600) % 60 ))s"
|
||||
fi
|
||||
}
|
||||
START_MW_SHELL_TIME=`date +%s`
|
||||
'''
|
||||
|
||||
source_bin_activate = '''
|
||||
export LANG=en_US.UTF-8
|
||||
MW_PATH=%s/bin/activate
|
||||
@@ -515,7 +531,7 @@ if [ -f $MW_PATH ];then
|
||||
source $MW_PATH
|
||||
fi''' % (mw.getRunDir(),)
|
||||
|
||||
head = head + source_bin_activate + "\n"
|
||||
head = head + start_head + source_bin_activate + "\n"
|
||||
log = '.log'
|
||||
|
||||
script_dir = mw.getRunDir() + "/scripts"
|
||||
@@ -557,7 +573,10 @@ fi''' % (mw.getRunDir(),)
|
||||
shell += '''
|
||||
echo "----------------------------------------------------------------------------"
|
||||
endDate=`date +"%Y-%m-%d %H:%M:%S"`
|
||||
echo "★[$endDate] Successful"
|
||||
END_MW_SHELL_TIME=`date +"%s"`
|
||||
((SHELL_COS_TIME=($END_MW_SHELL_TIME-$START_MW_SHELL_TIME)))
|
||||
MW_ToSeconds $SHELL_COS_TIME
|
||||
echo "★[$endDate] Successful | Script Run [$SCRIPT_RUN_TIME] "
|
||||
echo "----------------------------------------------------------------------------"
|
||||
'''
|
||||
cronPath = mw.getServerDir() + '/cron'
|
||||
|
||||
+2
-2
@@ -65,7 +65,7 @@ class Sql():
|
||||
self.__DB_TABLE = table
|
||||
return self
|
||||
|
||||
def where(self, where, param):
|
||||
def where(self, where, param=()):
|
||||
# WHERE条件
|
||||
if where:
|
||||
self.__OPT_WHERE = " WHERE " + where
|
||||
@@ -348,7 +348,7 @@ class Sql():
|
||||
except Exception as ex:
|
||||
return "error: " + str(ex)
|
||||
|
||||
def query(self, sql, param):
|
||||
def query(self, sql, param=()):
|
||||
# 执行SQL语句返回数据集
|
||||
self.__getConn()
|
||||
try:
|
||||
|
||||
+132
-53
@@ -247,8 +247,8 @@ class files_api:
|
||||
os.remove(task_log)
|
||||
return mw.returnJson(True, '任务已删除!')
|
||||
|
||||
# 上传文件
|
||||
def uploadFileApi(self):
|
||||
# 上传文件
|
||||
from werkzeug.utils import secure_filename
|
||||
from flask import request
|
||||
|
||||
@@ -263,6 +263,8 @@ class files_api:
|
||||
if os.path.exists(filename):
|
||||
s_path = filename
|
||||
p_stat = os.stat(s_path)
|
||||
|
||||
# print(filename)
|
||||
f.save(filename)
|
||||
os.chown(filename, p_stat.st_uid, p_stat.st_gid)
|
||||
os.chmod(filename, p_stat.st_mode)
|
||||
@@ -271,6 +273,86 @@ class files_api:
|
||||
mw.writeLog('文件管理', msg)
|
||||
return mw.returnMsg(True, '上传成功!')
|
||||
|
||||
# 设置文件和目录权限
|
||||
def setMode(self, path):
|
||||
s_path = os.path.dirname(path)
|
||||
p_stat = os.stat(s_path)
|
||||
os.chown(path, p_stat.st_uid, p_stat.st_gid)
|
||||
os.chmod(path, p_stat.st_mode)
|
||||
|
||||
def uploadSegmentApi(self):
|
||||
# 分段上传
|
||||
path = request.form.get('path', '')
|
||||
name = request.form.get('name', '')
|
||||
size = request.form.get('size')
|
||||
start = request.form.get('start')
|
||||
dir_mode = request.form.get('dir_mode', '')
|
||||
file_mode = request.form.get('file_mode', '')
|
||||
|
||||
if not mw.fileNameCheck(name):
|
||||
return mw.returnJson(False, '文件名中不能包含特殊字符!')
|
||||
|
||||
if path == '/':
|
||||
return mw.returnJson(False, '不能直接上传文件到系统根目录!')
|
||||
|
||||
if name.find('./') != -1 or path.find('./') != -1:
|
||||
return mw.returnJson(False, '错误的参数')
|
||||
|
||||
if not os.path.exists(path):
|
||||
os.makedirs(path, 493)
|
||||
if not dir_mode != '' or not file_mode != '':
|
||||
mw.setMode(path)
|
||||
|
||||
save_path = os.path.join(
|
||||
path, name + '.' + str(int(size)) + '.upload.tmp')
|
||||
d_size = 0
|
||||
if os.path.exists(save_path):
|
||||
d_size = os.path.getsize(save_path)
|
||||
|
||||
if d_size != int(start):
|
||||
return str(d_size)
|
||||
|
||||
f = open(save_path, 'ab')
|
||||
b64_data = request.form.get('b64_data', '0')
|
||||
if b64_data == '1':
|
||||
import base64
|
||||
b64_data = base64.b64decode(args.b64_data)
|
||||
f.write(b64_data)
|
||||
else:
|
||||
upload_files = request.files.getlist("blob")
|
||||
for tmp_f in upload_files:
|
||||
f.write(tmp_f.read())
|
||||
|
||||
f.close()
|
||||
f_size = os.path.getsize(save_path)
|
||||
if f_size != int(size):
|
||||
return str(f_size)
|
||||
|
||||
new_name = os.path.join(path, name)
|
||||
if os.path.exists(new_name):
|
||||
if new_name.find('.user.ini') != -1:
|
||||
mw.execShell("chattr -i " + new_name)
|
||||
try:
|
||||
os.remove(new_name)
|
||||
except:
|
||||
mw.execShell("rm -f %s" % new_name)
|
||||
|
||||
os.renames(save_path, new_name)
|
||||
|
||||
if dir_mode != '' and dir_mode != '':
|
||||
mode_tmp1 = dir_mode.split(',')
|
||||
mw.setMode(path, mode_tmp1[0])
|
||||
mw.setOwn(path, mode_tmp1[1])
|
||||
mode_tmp2 = file_mode.split(',')
|
||||
mw.setMode(new_name, mode_tmp2[0])
|
||||
mw.setOwn(new_name, mode_tmp2[1])
|
||||
else:
|
||||
self.setMode(new_name)
|
||||
|
||||
msg = mw.getInfo('上传文件[{1}] 到 [{2}]成功!', (new_name, path))
|
||||
mw.writeLog('文件管理', msg)
|
||||
return mw.returnMsg(True, '上传成功!')
|
||||
|
||||
def getRecycleBinApi(self):
|
||||
rPath = self.rPath
|
||||
if not os.path.exists(rPath):
|
||||
@@ -626,7 +708,7 @@ class files_api:
|
||||
return not path in nDirs
|
||||
|
||||
def getDirSize(self, path):
|
||||
if mw.getOs() == 'darwin':
|
||||
if mw.isAppleSystem():
|
||||
tmp = mw.execShell('du -sh ' + path)
|
||||
else:
|
||||
tmp = mw.execShell('du -sbh ' + path)
|
||||
@@ -677,56 +759,29 @@ class files_api:
|
||||
if os.path.getsize(path) > 2097152:
|
||||
return mw.returnJson(False, '不能在线编辑大于2MB的文件!')
|
||||
|
||||
if os.path.isdir(path):
|
||||
return mw.returnJson(False, '这不是一个文件!')
|
||||
|
||||
fp = open(path, 'rb')
|
||||
data = {}
|
||||
data['status'] = True
|
||||
try:
|
||||
if fp:
|
||||
from chardet.universaldetector import UniversalDetector
|
||||
detector = UniversalDetector()
|
||||
srcBody = b""
|
||||
for line in fp.readlines():
|
||||
detector.feed(line)
|
||||
srcBody += line
|
||||
detector.close()
|
||||
char = detector.result
|
||||
data['encoding'] = char['encoding']
|
||||
if char['encoding'] == 'GB2312' or not char['encoding'] or char[
|
||||
'encoding'] == 'TIS-620' or char['encoding'] == 'ISO-8859-9':
|
||||
data['encoding'] = 'GBK'
|
||||
if char['encoding'] == 'ascii' or char[
|
||||
'encoding'] == 'ISO-8859-1':
|
||||
data['encoding'] = 'utf-8'
|
||||
if char['encoding'] == 'Big5':
|
||||
data['encoding'] = 'BIG5'
|
||||
|
||||
if not data['encoding'] in ['GBK', 'utf-8', 'BIG5']:
|
||||
data['encoding'] = 'utf-8'
|
||||
if fp:
|
||||
srcBody = fp.read()
|
||||
fp.close()
|
||||
|
||||
encoding_list = ['utf-8', 'GBK', 'BIG5']
|
||||
for el in encoding_list:
|
||||
try:
|
||||
if sys.version_info[0] == 2:
|
||||
data['data'] = srcBody.decode(
|
||||
data['encoding']).encode('utf-8', errors='ignore')
|
||||
else:
|
||||
data['data'] = srcBody.decode(data['encoding'])
|
||||
except:
|
||||
data['encoding'] = char['encoding']
|
||||
if sys.version_info[0] == 2:
|
||||
data['data'] = srcBody.decode(
|
||||
data['encoding']).encode('utf-8', errors='ignore')
|
||||
else:
|
||||
data['data'] = srcBody.decode(data['encoding'])
|
||||
return mw.returnJson(True, 'OK', data)
|
||||
else:
|
||||
if sys.version_info[0] == 2:
|
||||
data['data'] = srcBody.decode('utf-8').encode('utf-8')
|
||||
else:
|
||||
data['data'] = srcBody.decode('utf-8')
|
||||
data['encoding'] = 'utf-8'
|
||||
data['encoding'] = el
|
||||
data['data'] = srcBody.decode(data['encoding'])
|
||||
break
|
||||
except Exception as ex:
|
||||
if el == 'BIG5':
|
||||
return mw.returnJson(False, '文件编码不被兼容,无法正确读取文件!' + str(ex))
|
||||
else:
|
||||
return mw.returnJson(False, '文件未正常打开!')
|
||||
|
||||
return mw.returnJson(True, 'OK', data)
|
||||
except Exception as ex:
|
||||
return mw.returnJson(False, '文件编码不被兼容,无法正确读取文件!' + str(ex))
|
||||
return mw.returnJson(True, 'OK', data)
|
||||
|
||||
def saveBody(self, path, data, encoding='utf-8'):
|
||||
if not os.path.exists(path):
|
||||
@@ -734,13 +789,10 @@ class files_api:
|
||||
try:
|
||||
if encoding == 'ascii':
|
||||
encoding = 'utf-8'
|
||||
if sys.version_info[0] == 2:
|
||||
data = data.encode(encoding, errors='ignore')
|
||||
fp = open(path, 'w+')
|
||||
else:
|
||||
data = data.encode(
|
||||
encoding, errors='ignore').decode(encoding)
|
||||
fp = open(path, 'w+', encoding=encoding)
|
||||
|
||||
data = data.encode(
|
||||
encoding, errors='ignore').decode(encoding)
|
||||
fp = open(path, 'w+', encoding=encoding)
|
||||
fp.write(data)
|
||||
fp.close()
|
||||
|
||||
@@ -935,6 +987,33 @@ class files_api:
|
||||
|
||||
return mw.getJson(data)
|
||||
|
||||
def execShellApi(self):
|
||||
# 执行SHELL命令
|
||||
shell = request.form.get('shell', '').strip()
|
||||
path = request.form.get('path', '').strip()
|
||||
disabled = ['vi', 'vim', 'top', 'passwd', 'su']
|
||||
tmp = shell.split(' ')
|
||||
if tmp[0] in disabled:
|
||||
return mw.returnJson(False, '禁止执行[{}]'.format(tmp[0]))
|
||||
shellStr = '''#!/bin/bash
|
||||
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
|
||||
export PATH
|
||||
cd %s
|
||||
%s
|
||||
''' % (path, shell)
|
||||
mw.writeFile('/tmp/panelShell.sh', shellStr)
|
||||
mw.execShell(
|
||||
'nohup bash /tmp/panelShell.sh > /tmp/panelShell.pl 2>&1 &')
|
||||
return mw.returnJson(True, 'ok')
|
||||
|
||||
def getExecShellMsgApi(self):
|
||||
# 取SHELL执行结果
|
||||
fileName = '/tmp/panelShell.pl'
|
||||
if not os.path.exists(fileName):
|
||||
return ''
|
||||
status = not mw.processExists('bash', None, '/tmp/panelShell.sh')
|
||||
return mw.returnJson(status, mw.getNumLines(fileName, 200))
|
||||
|
||||
def __get_stats(self, filename, path=None):
|
||||
filename = filename.replace('//', '/')
|
||||
try:
|
||||
|
||||
+14
-13
@@ -34,13 +34,13 @@ class firewall_api:
|
||||
|
||||
def __init__(self):
|
||||
iptables_file = mw.systemdCfgDir() + '/iptables.service'
|
||||
if os.path.exists(iptables_file):
|
||||
self.__isIptables = True
|
||||
if os.path.exists('/usr/sbin/firewalld'):
|
||||
self.__isFirewalld = True
|
||||
if os.path.exists('/usr/sbin/ufw'):
|
||||
elif os.path.exists('/usr/sbin/ufw'):
|
||||
self.__isUfw = True
|
||||
if mw.isAppleSystem():
|
||||
elif os.path.exists(iptables_file):
|
||||
self.__isIptables = True
|
||||
elif mw.isAppleSystem():
|
||||
self.__isMac = True
|
||||
|
||||
##### ----- start ----- ###
|
||||
@@ -123,12 +123,12 @@ class firewall_api:
|
||||
address = port
|
||||
if self.__isUfw:
|
||||
mw.execShell('ufw delete deny from ' + address + ' to any')
|
||||
elif self.__isIptables:
|
||||
cmd = 'iptables -D INPUT -s ' + address + ' -j DROP'
|
||||
mw.execShell(cmd)
|
||||
elif self.__isFirewalld:
|
||||
mw.execShell(
|
||||
'firewall-cmd --permanent --remove-rich-rule=\'rule family=ipv4 source address="' + address + '" drop\'')
|
||||
elif self.__isIptables:
|
||||
cmd = 'iptables -D INPUT -s ' + address + ' -j DROP'
|
||||
mw.execShell(cmd)
|
||||
else:
|
||||
pass
|
||||
|
||||
@@ -149,14 +149,15 @@ class firewall_api:
|
||||
return mw.returnJson(False, '失败,不能删除当前面板端口!')
|
||||
if self.__isUfw:
|
||||
mw.execShell('ufw delete allow ' + port + '/tcp')
|
||||
elif self.__isIptables:
|
||||
mw.execShell(
|
||||
'iptables -D INPUT -p tcp -m state --state NEW -m tcp --dport ' + port + ' -j ACCEPT')
|
||||
elif self.__isFirewalld:
|
||||
port = port.replace(':', '-')
|
||||
mw.execShell(
|
||||
'firewall-cmd --permanent --zone=public --remove-port=' + port + '/tcp')
|
||||
mw.execShell(
|
||||
'firewall-cmd --permanent --zone=public --remove-port=' + port + '/udp')
|
||||
elif self.__isIptables:
|
||||
mw.execShell(
|
||||
'iptables -D INPUT -p tcp -m state --state NEW -m tcp --dport ' + port + ' -j ACCEPT')
|
||||
else:
|
||||
pass
|
||||
msg = mw.getInfo('删除防火墙放行端口[{1}]成功!', (port,))
|
||||
@@ -438,13 +439,13 @@ class firewall_api:
|
||||
def addAcceptPort(self, port):
|
||||
if self.__isUfw:
|
||||
mw.execShell('ufw allow ' + port + '/tcp')
|
||||
elif self.__isIptables:
|
||||
cmd = 'iptables -I INPUT -p tcp -m state --state NEW -m tcp --dport ' + port + ' -j ACCEPT'
|
||||
mw.execShell(cmd)
|
||||
elif self.__isFirewalld:
|
||||
port = port.replace(':', '-')
|
||||
cmd = 'firewall-cmd --permanent --zone=public --add-port=' + port + '/tcp'
|
||||
mw.execShell(cmd)
|
||||
elif self.__isIptables:
|
||||
cmd = 'iptables -I INPUT -p tcp -m state --state NEW -m tcp --dport ' + port + ' -j ACCEPT'
|
||||
mw.execShell(cmd)
|
||||
else:
|
||||
pass
|
||||
return True
|
||||
|
||||
+514
-12
@@ -22,10 +22,14 @@ import hashlib
|
||||
import shlex
|
||||
import datetime
|
||||
import subprocess
|
||||
import glob
|
||||
import base64
|
||||
import re
|
||||
import db
|
||||
|
||||
from random import Random
|
||||
|
||||
import db
|
||||
|
||||
|
||||
def execShell(cmdstring, cwd=None, timeout=None, shell=True):
|
||||
|
||||
@@ -80,6 +84,10 @@ def getPanelDataDir():
|
||||
return getRunDir() + '/data'
|
||||
|
||||
|
||||
def getPanelTmp():
|
||||
return getRunDir() + '/tmp'
|
||||
|
||||
|
||||
def getServerDir():
|
||||
return getRootDir() + '/server'
|
||||
|
||||
@@ -123,6 +131,23 @@ def getAcmeDir():
|
||||
return acme
|
||||
|
||||
|
||||
def getAcmeDomainDir(domain):
|
||||
acme_dir = getAcmeDir()
|
||||
acme_domain = acme_dir + '/' + domain
|
||||
acme_domain_ecc = acme_domain + '_ecc'
|
||||
if os.path.exists(acme_domain_ecc):
|
||||
acme_domain = acme_domain_ecc
|
||||
return acme_domain
|
||||
|
||||
|
||||
def fileNameCheck(filename):
|
||||
f_strs = [';', '&', '<', '>']
|
||||
for fs in f_strs:
|
||||
if filename.find(fs) != -1:
|
||||
return False
|
||||
return True
|
||||
|
||||
|
||||
def triggerTask():
|
||||
isTask = getRunDir() + '/tmp/panelTask.pl'
|
||||
writeFile(isTask, 'True')
|
||||
@@ -228,6 +253,14 @@ def isIpAddr(ip):
|
||||
return False
|
||||
|
||||
|
||||
def getWebStatus():
|
||||
pid = getServerDir() + '/openresty/nginx/logs/nginx.pid'
|
||||
if os.path.exists(pid):
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
# ------------------------------ openresty start -----------------------------
|
||||
def restartWeb():
|
||||
return opWeb("reload")
|
||||
|
||||
@@ -252,6 +285,63 @@ def opWeb(method):
|
||||
return False
|
||||
|
||||
|
||||
def opLuaMake(cmd_name):
|
||||
path = getServerDir() + '/web_conf/nginx/lua/lua.conf'
|
||||
root_dir = getServerDir() + '/web_conf/nginx/lua/' + cmd_name
|
||||
dst_path = getServerDir() + '/web_conf/nginx/lua/' + cmd_name + '.lua'
|
||||
def_path = getServerDir() + '/web_conf/nginx/lua/empty.lua'
|
||||
|
||||
if not os.path.exists(root_dir):
|
||||
execShell('mkdir -p ' + root_dir)
|
||||
|
||||
files = []
|
||||
for fl in os.listdir(root_dir):
|
||||
suffix = getFileSuffix(fl)
|
||||
if suffix != 'lua':
|
||||
continue
|
||||
flpath = os.path.join(root_dir, fl)
|
||||
files.append(flpath)
|
||||
|
||||
if len(files) > 0:
|
||||
def_path = dst_path
|
||||
content = ''
|
||||
for f in files:
|
||||
t = readFile(f)
|
||||
f_base = os.path.basename(f)
|
||||
content += '-- ' + '*' * 20 + ' ' + f_base + ' start ' + '*' * 20 + "\n"
|
||||
content += t
|
||||
content += "\n" + '-- ' + '*' * 20 + ' ' + f_base + ' end ' + '*' * 20 + "\n"
|
||||
writeFile(dst_path, content)
|
||||
else:
|
||||
if os.path.exists(dst_path):
|
||||
os.remove(dst_path)
|
||||
|
||||
conf = readFile(path)
|
||||
conf = re.sub(cmd_name + ' (.*);',
|
||||
cmd_name + " " + def_path + ";", conf)
|
||||
writeFile(path, conf)
|
||||
|
||||
|
||||
def opLuaInitFile():
|
||||
opLuaMake('init_by_lua_file')
|
||||
|
||||
|
||||
def opLuaInitWorkerFile():
|
||||
opLuaMake('init_worker_by_lua_file')
|
||||
|
||||
|
||||
def opLuaInitAccessFile():
|
||||
opLuaMake('access_by_lua_file')
|
||||
|
||||
|
||||
def opLuaMakeAll():
|
||||
opLuaInitFile()
|
||||
opLuaInitWorkerFile()
|
||||
opLuaInitAccessFile()
|
||||
|
||||
# ------------------------------ openresty end -----------------------------
|
||||
|
||||
|
||||
def restartMw():
|
||||
import system_api
|
||||
system_api.system_api().restartMw()
|
||||
@@ -454,6 +544,14 @@ def getDate():
|
||||
return time.strftime('%Y-%m-%d %X', time.localtime())
|
||||
|
||||
|
||||
def getDateFromNow(tf_format="%Y-%m-%d %H:%M:%S", time_zone="Asia/Shanghai"):
|
||||
# 取格式时间
|
||||
import time
|
||||
os.environ['TZ'] = time_zone
|
||||
time.tzset()
|
||||
return time.strftime(tf_format, time.localtime())
|
||||
|
||||
|
||||
def getDataFromInt(val):
|
||||
time_format = '%Y-%m-%d %H:%M:%S'
|
||||
time_str = time.localtime(val)
|
||||
@@ -462,15 +560,44 @@ def getDataFromInt(val):
|
||||
|
||||
def writeLog(stype, msg, args=()):
|
||||
# 写日志
|
||||
uid = 1
|
||||
try:
|
||||
from flask import session
|
||||
uid = 1
|
||||
if 'uid' in session:
|
||||
uid = session['uid']
|
||||
return writeDbLog(stype, msg, args, uid)
|
||||
except Exception as e:
|
||||
print(getTracebackInfo())
|
||||
return False
|
||||
pass
|
||||
# writeFileLog(getTracebackInfo())
|
||||
return writeDbLog(stype, msg, args, uid)
|
||||
|
||||
|
||||
def writeFileLog(msg, path=None, limit_size=50 * 1024 * 1024, save_limit=3):
|
||||
log_file = getServerDir() + '/mdserver-web/logs/debug.log'
|
||||
if path != None:
|
||||
log_file = path
|
||||
|
||||
if os.path.exists(log_file):
|
||||
size = os.path.getsize(log_file)
|
||||
if size > limit_size:
|
||||
log_file_rename = log_file + "_" + \
|
||||
time.strftime("%Y-%m-%d_%H%M%S") + '.log'
|
||||
os.rename(log_file, log_file_rename)
|
||||
logs = sorted(glob.glob(log_file + "_*"))
|
||||
count = len(logs)
|
||||
save_limit = count - save_limit
|
||||
for i in range(count):
|
||||
if i > save_limit:
|
||||
break
|
||||
os.remove(logs[i])
|
||||
# print('|---多余日志[' + logs[i] + ']已删除!')
|
||||
|
||||
f = open(log_file, 'ab+')
|
||||
msg += "\n"
|
||||
if __name__ == '__main__':
|
||||
print(msg)
|
||||
f.write(msg.encode('utf-8'))
|
||||
f.close()
|
||||
return True
|
||||
|
||||
|
||||
def writeDbLog(stype, msg, args=(), uid=1):
|
||||
@@ -577,6 +704,44 @@ def dePunycode(domain):
|
||||
|
||||
|
||||
def enCrypt(key, strings):
|
||||
# 加密字符串
|
||||
try:
|
||||
import base64
|
||||
_key = key.encode('utf-8')
|
||||
_key = base64.urlsafe_b64encode(_key)
|
||||
|
||||
if type(strings) != bytes:
|
||||
strings = strings.encode('utf-8')
|
||||
import cryptography
|
||||
from cryptography.fernet import Fernet
|
||||
f = Fernet(_key)
|
||||
result = f.encrypt(strings)
|
||||
return result.decode('utf-8')
|
||||
except:
|
||||
writeFileLog(getTracebackInfo())
|
||||
return strings
|
||||
|
||||
|
||||
def deCrypt(key, strings):
|
||||
|
||||
# 解密字符串
|
||||
try:
|
||||
import base64
|
||||
_key = key.encode('utf-8')
|
||||
_key = base64.urlsafe_b64encode(_key)
|
||||
|
||||
if type(strings) != bytes:
|
||||
strings = strings.encode('utf-8')
|
||||
from cryptography.fernet import Fernet
|
||||
f = Fernet(_key)
|
||||
result = f.decrypt(strings).decode('utf-8')
|
||||
return result
|
||||
except:
|
||||
writeFileLog(getTracebackInfo())
|
||||
return strings
|
||||
|
||||
|
||||
def enDoubleCrypt(key, strings):
|
||||
# 加密字符串
|
||||
try:
|
||||
import base64
|
||||
@@ -591,11 +756,11 @@ def enCrypt(key, strings):
|
||||
result = f.encrypt(strings)
|
||||
return result.decode('utf-8')
|
||||
except:
|
||||
print(getTracebackInfo())
|
||||
writeFileLog(getTracebackInfo())
|
||||
return strings
|
||||
|
||||
|
||||
def deCrypt(key, strings):
|
||||
def deDoubleCrypt(key, strings):
|
||||
# 解密字符串
|
||||
try:
|
||||
import base64
|
||||
@@ -609,10 +774,137 @@ def deCrypt(key, strings):
|
||||
result = f.decrypt(strings).decode('utf-8')
|
||||
return result
|
||||
except:
|
||||
print(getTracebackInfo())
|
||||
writeFileLog(getTracebackInfo())
|
||||
return strings
|
||||
|
||||
|
||||
def aesEncrypt(data, key='ABCDEFGHIJKLMNOP', vi='0102030405060708'):
|
||||
# aes加密
|
||||
# @param data 被加密的数据
|
||||
# @param key 加解密密匙 16位
|
||||
# @param vi 16位
|
||||
|
||||
from cryptography.hazmat.primitives import padding
|
||||
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
|
||||
from cryptography.hazmat.backends import default_backend
|
||||
|
||||
if not isinstance(data, bytes):
|
||||
data = data.encode()
|
||||
|
||||
# AES_CBC_KEY = os.urandom(32)
|
||||
# AES_CBC_IV = os.urandom(16)
|
||||
|
||||
AES_CBC_KEY = key.encode()
|
||||
AES_CBC_IV = vi.encode()
|
||||
|
||||
# print("AES_CBC_KEY:", AES_CBC_KEY)
|
||||
# print("AES_CBC_IV:", AES_CBC_IV)
|
||||
|
||||
padder = padding.PKCS7(algorithms.AES.block_size).padder()
|
||||
padded_data = padder.update(data) + padder.finalize()
|
||||
|
||||
cipher = Cipher(algorithms.AES(AES_CBC_KEY),
|
||||
modes.CBC(AES_CBC_IV),
|
||||
backend=default_backend())
|
||||
encryptor = cipher.encryptor()
|
||||
|
||||
edata = encryptor.update(padded_data)
|
||||
|
||||
# print(edata)
|
||||
# print(str(edata))
|
||||
# print(edata.decode())
|
||||
return edata
|
||||
|
||||
|
||||
def aesDecrypt(data, key='ABCDEFGHIJKLMNOP', vi='0102030405060708'):
|
||||
# aes加密
|
||||
# @param data 被解密的数据
|
||||
# @param key 加解密密匙 16位
|
||||
# @param vi 16位
|
||||
|
||||
from cryptography.hazmat.primitives import padding
|
||||
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
|
||||
from cryptography.hazmat.backends import default_backend
|
||||
|
||||
from cryptography.hazmat.primitives import padding
|
||||
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
|
||||
from cryptography.hazmat.backends import default_backend
|
||||
|
||||
if not isinstance(data, bytes):
|
||||
data = data.encode()
|
||||
|
||||
AES_CBC_KEY = key.encode()
|
||||
AES_CBC_IV = vi.encode()
|
||||
|
||||
cipher = Cipher(algorithms.AES(AES_CBC_KEY),
|
||||
modes.CBC(AES_CBC_IV),
|
||||
backend=default_backend())
|
||||
decryptor = cipher.decryptor()
|
||||
|
||||
ddata = decryptor.update(data)
|
||||
|
||||
unpadder = padding.PKCS7(algorithms.AES.block_size).unpadder()
|
||||
data = unpadder.update(ddata)
|
||||
|
||||
try:
|
||||
uppadded_data = data + unpadder.finalize()
|
||||
except ValueError:
|
||||
raise Exception('无效的加密信息!')
|
||||
|
||||
return uppadded_data
|
||||
|
||||
|
||||
def aesEncrypt_Crypto(data, key, vi):
|
||||
# 该方法保留,暂时不使用
|
||||
# aes加密
|
||||
# @param data 被加密的数据
|
||||
# @param key 加解密密匙 16位
|
||||
# @param vi 16位
|
||||
|
||||
from Crypto.Cipher import AES
|
||||
cryptor = AES.new(key.encode('utf8'), AES.MODE_CBC, vi.encode('utf8'))
|
||||
# 判断是否含有中文
|
||||
zhmodel = re.compile(u'[\u4e00-\u9fff]')
|
||||
match = zhmodel.search(data)
|
||||
if match == None:
|
||||
# 无中文时
|
||||
add = 16 - len(data) % 16
|
||||
pad = lambda s: s + add * chr(add)
|
||||
data = pad(data)
|
||||
enctext = cryptor.encrypt(data.encode('utf8'))
|
||||
else:
|
||||
# 含有中文时
|
||||
data = data.encode()
|
||||
add = 16 - len(data) % 16
|
||||
data = data + add * (chr(add)).encode()
|
||||
enctext = cryptor.encrypt(data)
|
||||
encodestrs = base64.b64encode(enctext).decode('utf8')
|
||||
return encodestrs
|
||||
|
||||
|
||||
def aesDecrypt_Crypto(data, key, vi):
|
||||
# 该方法保留,暂时不使用
|
||||
# aes加密
|
||||
# @param data 被加密的数据
|
||||
# @param key 加解密密匙 16位
|
||||
# @param vi 16位
|
||||
|
||||
from crypto.Cipher import AES
|
||||
data = data.encode('utf8')
|
||||
encodebytes = base64.urlsafe_b64decode(data)
|
||||
cipher = AES.new(key.encode('utf8'), AES.MODE_CBC, vi.encode('utf8'))
|
||||
text_decrypted = cipher.decrypt(encodebytes)
|
||||
# 判断是否含有中文
|
||||
zhmodel = re.compile(u'[\u4e00-\u9fff]')
|
||||
match = zhmodel.search(text_decrypted)
|
||||
if match == False:
|
||||
# 无中文时补位
|
||||
unpad = lambda s: s[0:-s[-1]]
|
||||
text_decrypted = unpad(text_decrypted)
|
||||
text_decrypted = text_decrypted.decode('utf8').rstrip() # 去掉补位的右侧空格
|
||||
return text_decrypted
|
||||
|
||||
|
||||
def buildSoftLink(src, dst, force=False):
|
||||
'''
|
||||
建立软连接
|
||||
@@ -885,7 +1177,7 @@ def getLocalIp():
|
||||
try:
|
||||
ipaddress = readFile(filename)
|
||||
if not ipaddress or ipaddress == '127.0.0.1':
|
||||
cmd = "curl -4 -sS --connect-timeout 5 -m 60 https://v6r.ipip.net/?format=text"
|
||||
cmd = "curl --insecure -4 -sS --connect-timeout 5 -m 60 https://v6r.ipip.net/?format=text"
|
||||
ip = execShell(cmd)
|
||||
result = ip[0].strip()
|
||||
if result == '':
|
||||
@@ -894,7 +1186,7 @@ def getLocalIp():
|
||||
return result
|
||||
return ipaddress
|
||||
except Exception as e:
|
||||
cmd = "curl -6 -sS --connect-timeout 5 -m 60 https://v6r.ipip.net/?format=text"
|
||||
cmd = "curl --insecure -6 -sS --connect-timeout 5 -m 60 https://v6r.ipip.net/?format=text"
|
||||
ip = execShell(cmd)
|
||||
result = ip[0].strip()
|
||||
if result == '':
|
||||
@@ -923,6 +1215,12 @@ def formatDate(format="%Y-%m-%d %H:%M:%S", times=None):
|
||||
return time.strftime(format, time_local)
|
||||
|
||||
|
||||
def strfToTime(sdate):
|
||||
# 转换时间
|
||||
import time
|
||||
return time.strftime('%Y-%m-%d', time.strptime(sdate, '%b %d %H:%M:%S %Y %Z'))
|
||||
|
||||
|
||||
def checkIp(ip):
|
||||
# 检查是否为IPv4地址
|
||||
import re
|
||||
@@ -1019,6 +1317,15 @@ def setOwn(filename, user, group=None):
|
||||
return True
|
||||
|
||||
|
||||
def setMode(filename, mode):
|
||||
# 设置文件权限
|
||||
if not os.path.exists(filename):
|
||||
return False
|
||||
mode = int(str(mode), 8)
|
||||
os.chmod(filename, mode)
|
||||
return True
|
||||
|
||||
|
||||
def checkPort(port):
|
||||
# 检查端口是否合法
|
||||
ports = ['21', '443', '888']
|
||||
@@ -1104,7 +1411,7 @@ def makeConf():
|
||||
file = getRunDir() + '/data/json/config.json'
|
||||
if not os.path.exists(file):
|
||||
c = {}
|
||||
c['title'] = '大圣面板'
|
||||
c['title'] = '金灵面板'
|
||||
c['home'] = 'http://github/midoks/mdserver-web'
|
||||
c['recycle_bin'] = True
|
||||
c['template'] = 'default'
|
||||
@@ -1404,7 +1711,7 @@ def getCertName(certPath):
|
||||
result['notAfter'], "%Y-%m-%d")) - time.time()) / 86400)
|
||||
return result
|
||||
except Exception as e:
|
||||
# print(getTracebackInfo())
|
||||
writeFileLog(getTracebackInfo())
|
||||
return None
|
||||
|
||||
|
||||
@@ -1502,6 +1809,176 @@ def getMyORMDb():
|
||||
o = ormDb.ORM()
|
||||
return o
|
||||
|
||||
##################### notify start #########################################
|
||||
|
||||
|
||||
def initNotifyConfig():
|
||||
p = getNotifyPath()
|
||||
if not os.path.exists(p):
|
||||
writeFile(p, '{}')
|
||||
return True
|
||||
|
||||
|
||||
def getNotifyPath():
|
||||
path = 'data/notify.json'
|
||||
return path
|
||||
|
||||
|
||||
def getNotifyData(is_parse=False):
|
||||
initNotifyConfig()
|
||||
notify_file = getNotifyPath()
|
||||
notify_data = readFile(notify_file)
|
||||
|
||||
data = json.loads(notify_data)
|
||||
|
||||
if is_parse:
|
||||
tag_list = ['tgbot', 'email']
|
||||
for t in tag_list:
|
||||
if t in data and 'cfg' in data[t]:
|
||||
data[t]['data'] = json.loads(deDoubleCrypt(t, data[t]['cfg']))
|
||||
return data
|
||||
|
||||
|
||||
def writeNotify(data):
|
||||
p = getNotifyPath()
|
||||
return writeFile(p, json.dumps(data))
|
||||
|
||||
|
||||
def tgbotNotifyChatID():
|
||||
data = getNotifyData(True)
|
||||
if 'tgbot' in data and 'enable' in data['tgbot']:
|
||||
if data['tgbot']['enable']:
|
||||
t = data['tgbot']['data']
|
||||
return t['chat_id']
|
||||
return ''
|
||||
|
||||
|
||||
def tgbotNotifyObject():
|
||||
data = getNotifyData(True)
|
||||
if 'tgbot' in data and 'enable' in data['tgbot']:
|
||||
if data['tgbot']['enable']:
|
||||
t = data['tgbot']['data']
|
||||
import telebot
|
||||
bot = telebot.TeleBot(app_token)
|
||||
return True, bot
|
||||
return False, None
|
||||
|
||||
|
||||
def tgbotNotifyMessage(app_token, chat_id, msg):
|
||||
import telebot
|
||||
bot = telebot.TeleBot(app_token)
|
||||
try:
|
||||
data = bot.send_message(chat_id, msg)
|
||||
return True
|
||||
except Exception as e:
|
||||
writeFileLog(str(e))
|
||||
return False
|
||||
|
||||
|
||||
def tgbotNotifyHttpPost(app_token, chat_id, msg):
|
||||
try:
|
||||
url = 'https://api.telegram.org/bot' + app_token + '/sendMessage'
|
||||
post_data = {
|
||||
'chat_id': chat_id,
|
||||
'text': msg,
|
||||
}
|
||||
rdata = httpPost(url, post_data)
|
||||
return True
|
||||
except Exception as e:
|
||||
writeFileLog(str(e))
|
||||
return False
|
||||
|
||||
|
||||
def tgbotNotifyTest(app_token, chat_id):
|
||||
msg = 'MW-通知验证测试OK'
|
||||
return tgbotNotifyHttpPost(app_token, chat_id, msg)
|
||||
|
||||
|
||||
def emailNotifyMessage(data):
|
||||
'''
|
||||
邮件通知
|
||||
'''
|
||||
sys.path.append(os.getcwd() + "/class/plugin")
|
||||
import memail
|
||||
try:
|
||||
if data['smtp_ssl'] == 'ssl':
|
||||
memail.sendSSL(data['smtp_host'], data['smtp_port'],
|
||||
data['username'], data['password'],
|
||||
data['to_mail_addr'], data['subject'], data['content'])
|
||||
else:
|
||||
memail.send(data['smtp_host'], data['smtp_port'],
|
||||
data['username'], data['password'],
|
||||
data['to_mail_addr'], data['subject'], data['content'])
|
||||
return True
|
||||
except Exception as e:
|
||||
print(getTracebackInfo())
|
||||
return False
|
||||
|
||||
|
||||
def emailNotifyTest(data):
|
||||
# print(data)
|
||||
data['subject'] = 'MW通知测试'
|
||||
data['content'] = data['mail_test']
|
||||
return emailNotifyMessage(data)
|
||||
|
||||
|
||||
def notifyMessageTry(msg, stype='common', trigger_time=300, is_write_log=True):
|
||||
|
||||
lock_file = getPanelTmp() + '/notify_lock.json'
|
||||
if not os.path.exists(lock_file):
|
||||
writeFile(lock_file, '{}')
|
||||
|
||||
lock_data = json.loads(readFile(lock_file))
|
||||
if stype in lock_data:
|
||||
diff_time = time.time() - lock_data[stype]['do_time']
|
||||
if diff_time >= trigger_time:
|
||||
lock_data[stype]['do_time'] = time.time()
|
||||
else:
|
||||
return False
|
||||
else:
|
||||
lock_data[stype] = {'do_time': time.time()}
|
||||
|
||||
writeFile(lock_file, json.dumps(lock_data))
|
||||
|
||||
if is_write_log:
|
||||
writeLog("通知管理[" + stype + "]", msg)
|
||||
|
||||
data = getNotifyData(True)
|
||||
# tag_list = ['tgbot', 'email']
|
||||
# tagbot
|
||||
do_notify = False
|
||||
if 'tgbot' in data and 'enable' in data['tgbot']:
|
||||
if data['tgbot']['enable']:
|
||||
t = data['tgbot']['data']
|
||||
i = sys.version_info
|
||||
|
||||
# telebot 在python小于3.7无法使用
|
||||
if i[0] < 3 or i[1] < 7:
|
||||
do_notify = tgbotNotifyHttpPost(
|
||||
t['app_token'], t['chat_id'], msg)
|
||||
else:
|
||||
do_notify = tgbotNotifyMessage(
|
||||
t['app_token'], t['chat_id'], msg)
|
||||
|
||||
if 'email' in data and 'enable' in data['email']:
|
||||
if data['email']['enable']:
|
||||
t = data['email']['data']
|
||||
t['subject'] = 'MW通知'
|
||||
t['content'] = msg
|
||||
do_notify = emailNotifyMessage(t)
|
||||
return do_notify
|
||||
|
||||
|
||||
def notifyMessage(msg, stype='common', trigger_time=300, is_write_log=True):
|
||||
try:
|
||||
return notifyMessageTry(msg, stype, trigger_time, is_write_log)
|
||||
except Exception as e:
|
||||
writeFileLog(getTracebackInfo())
|
||||
return False
|
||||
|
||||
|
||||
##################### notify end #########################################
|
||||
|
||||
|
||||
##################### ssh start #########################################
|
||||
def getSshDir():
|
||||
@@ -1511,6 +1988,31 @@ def getSshDir():
|
||||
return '/root/.ssh'
|
||||
|
||||
|
||||
def processExists(pname, exe=None, cmdline=None):
|
||||
# 进程是否存在
|
||||
try:
|
||||
import psutil
|
||||
pids = psutil.pids()
|
||||
for pid in pids:
|
||||
try:
|
||||
p = psutil.Process(pid)
|
||||
if p.name() == pname:
|
||||
if not exe and not cmdline:
|
||||
return True
|
||||
else:
|
||||
if exe:
|
||||
if p.exe() == exe:
|
||||
return True
|
||||
if cmdline:
|
||||
if cmdline in p.cmdline():
|
||||
return True
|
||||
except:
|
||||
pass
|
||||
return False
|
||||
except:
|
||||
return True
|
||||
|
||||
|
||||
def createRsa():
|
||||
# ssh-keygen -t rsa -P "" -C "midoks@163.com"
|
||||
ssh_dir = getSshDir()
|
||||
|
||||
+33
-14
@@ -71,8 +71,9 @@ class plugins_api:
|
||||
if not mw.isNumber(sType):
|
||||
sType = 0
|
||||
|
||||
# print sPage
|
||||
data = self.getPluginList(sType, int(sPage))
|
||||
# print(sPage, sType)
|
||||
search = request.args.get('search', '').lower()
|
||||
data = self.getPluginList(sType, search, int(sPage))
|
||||
return mw.getJson(data)
|
||||
|
||||
def menuGetAbsPath(self, tag, path):
|
||||
@@ -91,7 +92,7 @@ class plugins_api:
|
||||
t = mw.readFile(menu_file)
|
||||
tlist = json.loads(t)
|
||||
for menu_data in tlist:
|
||||
if 'path' in menu_data:
|
||||
if tag == menu_data['name'] and 'path' in menu_data:
|
||||
tpath = self.menuGetAbsPath(tag, menu_data['path'])
|
||||
content = mw.readFile(tpath)
|
||||
data['plugin_content'] = content
|
||||
@@ -138,7 +139,7 @@ class plugins_api:
|
||||
def initApi(self):
|
||||
|
||||
plugin_names = {
|
||||
'openresty': '1.21.4.1',
|
||||
'openresty': '1.21.4.2',
|
||||
'php': '56',
|
||||
'swap': '1.1',
|
||||
'mysql': '5.7',
|
||||
@@ -256,13 +257,14 @@ class plugins_api:
|
||||
data = json.loads(t)
|
||||
|
||||
for idx in range(len(data)):
|
||||
if data[idx]['title'] == info['title'] and data[idx]['name'] == info['name']:
|
||||
if data[idx]['name'] == info['name']:
|
||||
data.remove(data[idx])
|
||||
break
|
||||
mw.writeFile(hookPath, json.dumps(data))
|
||||
|
||||
def hookInstall(self, info):
|
||||
valid_hook = ['backup', 'database']
|
||||
valid_list_hook = ['menu']
|
||||
valid_list_hook = ['menu', 'global_static', 'site_cb']
|
||||
if 'hook' in info:
|
||||
hooks = info['hook']
|
||||
for h in hooks:
|
||||
@@ -280,7 +282,7 @@ class plugins_api:
|
||||
|
||||
def hookUninstall(self, info):
|
||||
valid_hook = ['backup', 'database']
|
||||
valid_list_hook = ['menu']
|
||||
valid_list_hook = ['menu', 'global_static', 'site_cb']
|
||||
if 'hook' in info:
|
||||
hooks = info['hook']
|
||||
for h in hooks:
|
||||
@@ -780,7 +782,18 @@ class plugins_api:
|
||||
print(e)
|
||||
return plugins_info
|
||||
|
||||
def getAllListPage(self, sType='0', page=1, pageSize=10):
|
||||
def searchKey(self, info, kw):
|
||||
try:
|
||||
if info['title'].lower().find(kw) > -1:
|
||||
return True
|
||||
if info['ps'].lower().find(kw) > -1:
|
||||
return True
|
||||
if info['name'].lower().find(kw) > -1:
|
||||
return True
|
||||
except Exception as e:
|
||||
return False
|
||||
|
||||
def getAllListPage(self, sType='0', kw='', page=1, pageSize=10):
|
||||
plugins_info = []
|
||||
for dirinfo in os.listdir(self.__plugin_dir):
|
||||
if dirinfo[0:1] == '.':
|
||||
@@ -791,11 +804,14 @@ class plugins_api:
|
||||
if os.path.exists(json_file):
|
||||
try:
|
||||
data = json.loads(mw.readFile(json_file))
|
||||
# 判断是否搜索
|
||||
if kw != '' and not self.searchKey(data, kw):
|
||||
continue
|
||||
tmp_data = self.makeList(data, sType)
|
||||
for index in range(len(tmp_data)):
|
||||
plugins_info.append(tmp_data[index])
|
||||
except Exception as e:
|
||||
print(e)
|
||||
print(mw.getTracebackInfo())
|
||||
|
||||
start = (page - 1) * pageSize
|
||||
end = start + pageSize
|
||||
@@ -925,14 +941,14 @@ class plugins_api:
|
||||
|
||||
return plugins_info
|
||||
|
||||
def getPluginList(self, sType, sPage=1, sPageSize=10):
|
||||
# print sType, sPage, sPageSize
|
||||
def getPluginList(self, sType, kw='', sPage=1, sPageSize=10):
|
||||
# print(sType, kw, sPage, sPageSize)
|
||||
|
||||
ret = {}
|
||||
ret['type'] = json.loads(mw.readFile(self.__type))
|
||||
# plugins_info = self.getAllListThread(sType)
|
||||
# plugins_info = self.getAllListProcess(sType)
|
||||
data = self.getAllListPage(sType, sPage, sPageSize)
|
||||
data = self.getAllListPage(sType, kw, sPage, sPageSize)
|
||||
ret['data'] = data[0]
|
||||
|
||||
args = {}
|
||||
@@ -1014,8 +1030,11 @@ class plugins_api:
|
||||
|
||||
# shell 调用
|
||||
def run(self, name, func, version='', args='', script='index'):
|
||||
path = self.__plugin_dir + \
|
||||
'/' + name + '/' + script + '.py'
|
||||
|
||||
path = self.__plugin_dir + '/' + name + '/' + script + '.py'
|
||||
if not os.path.exists(path):
|
||||
path = self.__plugin_dir + '/' + name + '/' + name + '.py'
|
||||
|
||||
py = 'python3 ' + path
|
||||
|
||||
if args == '':
|
||||
|
||||
+167
-66
@@ -72,24 +72,60 @@ class site_api:
|
||||
mw.execShell("mkdir -p " + self.sslLetsDir +
|
||||
" && chmod -R 755 " + self.sslLetsDir)
|
||||
|
||||
def runHook(self, hook_name, func_name):
|
||||
# 站点操作Hook
|
||||
hook_file = 'data/hook_site_cb.json'
|
||||
hook_cfg = []
|
||||
if os.path.exists(hook_file):
|
||||
t = mw.readFile(hook_file)
|
||||
hook_cfg = json.loads(t)
|
||||
|
||||
hook_num = len(hook_cfg)
|
||||
if hook_num == 0:
|
||||
return
|
||||
|
||||
import plugins_api
|
||||
pa = plugins_api.plugins_api()
|
||||
|
||||
for x in range(hook_num):
|
||||
hook_data = hook_cfg[x]
|
||||
if func_name in hook_data:
|
||||
app_name = hook_data["name"]
|
||||
run_func = hook_data[func_name]['func']
|
||||
# print(app_name, run_func)
|
||||
pa.run(app_name, run_func)
|
||||
return True
|
||||
|
||||
##### ----- start ----- ###
|
||||
def listApi(self):
|
||||
limit = request.form.get('limit', '10')
|
||||
p = request.form.get('p', '1')
|
||||
type_id = request.form.get('type_id', '0').strip()
|
||||
search = request.form.get('search', '').strip()
|
||||
|
||||
start = (int(p) - 1) * (int(limit))
|
||||
|
||||
siteM = mw.M('sites').field('id,name,path,status,ps,addtime,edate')
|
||||
if type_id != '' and int(type_id) >= 0:
|
||||
siteM.where('type_id=?', (type_id,))
|
||||
|
||||
sql_where = ''
|
||||
if search != '':
|
||||
sql_where = " name like '%" + search + "%' or ps like '%" + search + "%' "
|
||||
|
||||
if type_id != '' and int(type_id) >= 0 and search != '':
|
||||
sql_where = sql_where + " and type_id=" + type_id + ""
|
||||
elif type_id != '' and int(type_id) >= 0:
|
||||
sql_where = " type_id=" + type_id
|
||||
|
||||
if sql_where != '':
|
||||
siteM.where(sql_where)
|
||||
|
||||
_list = siteM.limit((str(start)) + ',' +
|
||||
limit).order('id desc').select()
|
||||
|
||||
for i in range(len(_list)):
|
||||
_list[i]['backup_count'] = mw.M('backup').where(
|
||||
"pid=? AND type=?", (_list[i]['id'], 0)).count()
|
||||
if _list != None:
|
||||
for i in range(len(_list)):
|
||||
_list[i]['backup_count'] = mw.M('backup').where(
|
||||
"pid=? AND type=?", (_list[i]['id'], 0)).count()
|
||||
|
||||
_ret = {}
|
||||
_ret['data'] = _list
|
||||
@@ -550,7 +586,7 @@ class site_api:
|
||||
return mw.returnJson(False, '使用中,先关闭再删除')
|
||||
mw.execShell('rm -rf ' + ssl_lets_dir)
|
||||
elif ssl_type == 'acme':
|
||||
ssl_acme_dir = mw.getAcmeDir() + '/' + site_name
|
||||
ssl_acme_dir = mw.getAcmeDomainDir(site_name)
|
||||
csr_acme_path = ssl_acme_dir + '/fullchain.cer' # 生成证书路径
|
||||
if mw.md5(mw.readFile(csr_acme_path)) == mw.md5(mw.readFile(csr_path)):
|
||||
return mw.returnJson(False, '使用中,先关闭再删除')
|
||||
@@ -587,9 +623,9 @@ class site_api:
|
||||
csr_path = self.sslLetsDir + '/' + site_name + '/fullchain.pem' # 生成证书路径
|
||||
key_path = self.sslLetsDir + '/' + site_name + '/privkey.pem' # 密钥文件路径
|
||||
elif ssl_type == 'acme':
|
||||
csr_path = mw.getAcmeDir() + '/' + site_name + '/fullchain.cer' # 生成证书路径
|
||||
key_path = mw.getAcmeDir() + '/' + site_name + '/' + \
|
||||
site_name + '.key' # 密钥文件路径
|
||||
acme_dir = mw.getAcmeDomainDir(site_name)
|
||||
csr_path = acme_dir + '/fullchain.cer' # 生成证书路径
|
||||
key_path = acme_dir + '/' + site_name + '.key' # 密钥文件路径
|
||||
|
||||
key = mw.readFile(key_path)
|
||||
csr = mw.readFile(csr_path)
|
||||
@@ -751,6 +787,9 @@ class site_api:
|
||||
mw.execShell('echo "lets" > "' + path + '/README"')
|
||||
elif ssl_type == 'acme':
|
||||
ssl_acme_dir = mw.getAcmeDir() + '/' + site_name
|
||||
if not os.path.exists(ssl_acme_dir):
|
||||
ssl_acme_dir = mw.getAcmeDir() + '/' + site_name + '_ecc'
|
||||
|
||||
acme_csrpath = ssl_acme_dir + '/fullchain.cer'
|
||||
acme_keypath = ssl_acme_dir + '/' + site_name + '.key'
|
||||
if mw.md5(mw.readFile(acme_csrpath)) == mw.md5(mw.readFile(csr_path)):
|
||||
@@ -989,16 +1028,17 @@ class site_api:
|
||||
# print(cmd)
|
||||
result = mw.execShell(cmd)
|
||||
|
||||
src_path = acme_dir + '/' + domains[0]
|
||||
src_path = mw.getAcmeDomainDir(domains[0])
|
||||
src_cert = src_path + '/fullchain.cer'
|
||||
src_key = src_path + '/' + domains[0] + '.key'
|
||||
src_cert.replace("\*", "*")
|
||||
|
||||
msg = '签发失败,您尝试申请证书的失败次数已达上限!<p>1、检查域名是否绑定到对应站点</p>\
|
||||
<p>2、检查域名是否正确解析到本服务器,或解析还未完全生效</p>\
|
||||
<p>3、如果您的站点设置了反向代理,或使用了CDN,请先将其关闭</p>\
|
||||
<p>4、如果您的站点设置了301重定向,请先将其关闭</p>\
|
||||
<p>5、如果以上检查都确认没有问题,请尝试更换DNS服务商</p>'
|
||||
if not os.path.exists(src_cert.replace("\*", "*")):
|
||||
if not os.path.exists(src_cert):
|
||||
data = {}
|
||||
data['err'] = result
|
||||
data['out'] = result[0]
|
||||
@@ -1271,6 +1311,7 @@ class site_api:
|
||||
mw.M('domain').add('pid,name,port,addtime',
|
||||
(pid, domain_name, domain_port, mw.getDate()))
|
||||
|
||||
self.runHook('site_cb', 'add')
|
||||
return mw.returnJson(True, '域名添加成功!')
|
||||
|
||||
def addDirBindApi(self):
|
||||
@@ -1394,13 +1435,15 @@ class site_api:
|
||||
data['data'] = mw.readFile(filename)
|
||||
data['rlist'] = []
|
||||
for ds in os.listdir(self.rewritePath):
|
||||
if ds[0:1] == '.':
|
||||
continue
|
||||
if ds == 'list.txt':
|
||||
continue
|
||||
data['rlist'].append(ds[0:len(ds) - 5])
|
||||
data['filename'] = filename
|
||||
return mw.getJson(data)
|
||||
|
||||
# 修改物理路径
|
||||
# 修改物理路径
|
||||
def setPathApi(self):
|
||||
mid = request.form.get('id', '')
|
||||
path = request.form.get('path', '')
|
||||
@@ -1827,19 +1870,16 @@ class site_api:
|
||||
if _id == '' or _siteName == '':
|
||||
return mw.returnJson(False, "必填项不能为空!")
|
||||
|
||||
_old_config = mw.readFile(
|
||||
"{}/{}/{}.conf".format(self.proxyPath, _siteName, _id))
|
||||
if _old_config == False:
|
||||
return mw.returnJson(False, "非法操作")
|
||||
|
||||
mw.writeFile("{}/{}/{}.conf".format(self.proxyPath,
|
||||
_siteName, _id), _config)
|
||||
proxy_file = "{}/{}/{}.conf".format(self.proxyPath, _siteName, _id)
|
||||
mw.backFile(proxy_file)
|
||||
mw.writeFile(proxy_file, _config)
|
||||
rule_test = mw.checkWebConfig()
|
||||
if rule_test != True:
|
||||
mw.writeFile("{}/{}/{}.conf".format(self.proxyPath,
|
||||
_siteName, _id), _old_config)
|
||||
mw.restoreFile(proxy_file)
|
||||
mw.removeBackFile(proxy_file)
|
||||
return mw.returnJson(False, "OpenResty 配置测试不通过, 请重试: {}".format(rule_test))
|
||||
|
||||
mw.removeBackFile(proxy_file)
|
||||
self.operateRedirectConf(_siteName, 'start')
|
||||
mw.restartWeb()
|
||||
return mw.returnJson(True, "ok")
|
||||
@@ -1880,21 +1920,20 @@ class site_api:
|
||||
_from = request.form.get('from', '')
|
||||
_to = request.form.get('to', '')
|
||||
_host = request.form.get('host', '')
|
||||
_name = request.form.get('name', '')
|
||||
_open_proxy = request.form.get('open_proxy', '')
|
||||
_open_cache = request.form.get('open_cache', '')
|
||||
_cache_time = request.form.get('cache_time', '')
|
||||
_id = request.form.get('id', '')
|
||||
|
||||
if _siteName == "" or _from == "" or _to == "" or _host == "":
|
||||
# print(_name, _siteName, _from, _to, _host)
|
||||
if _name == "" or _siteName == "" or _from == "" or _to == "" or _host == "":
|
||||
return mw.returnJson(False, "必填项不能为空")
|
||||
|
||||
data_path = self.getProxyDataPath(_siteName)
|
||||
data_content = mw.readFile(
|
||||
data_path) if os.path.exists(data_path) else ""
|
||||
data = json.loads(data_content) if data_content != "" else []
|
||||
|
||||
rep = "http(s)?\:\/\/([a-zA-Z0-9][-a-zA-Z0-9]{0,62}\.)+([a-zA-Z0-9][a-zA-Z0-9]{0,62})+.?"
|
||||
if not re.match(rep, _to):
|
||||
return mw.returnJson(False, "错误的目标地址!")
|
||||
|
||||
# _to = _to.strip("/")
|
||||
# get host from url
|
||||
try:
|
||||
if _host == "$host":
|
||||
@@ -1903,7 +1942,11 @@ class site_api:
|
||||
except:
|
||||
return mw.returnJson(False, "错误的目标地址")
|
||||
|
||||
# location ~* ^{from}(.*)$ {
|
||||
proxy_site_path = self.getProxyDataPath(_siteName)
|
||||
data_content = mw.readFile(
|
||||
proxy_site_path) if os.path.exists(proxy_site_path) else ""
|
||||
data = json.loads(data_content) if data_content != "" else []
|
||||
|
||||
tpl = "#PROXY-START\n\
|
||||
location ^~ {from} {\n\
|
||||
proxy_pass {to};\n\
|
||||
@@ -1911,12 +1954,29 @@ location ^~ {from} {\n\
|
||||
proxy_set_header X-Real-IP $remote_addr;\n\
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;\n\
|
||||
proxy_set_header REMOTE-HOST $remote_addr;\n\
|
||||
proxy_set_header Upgrade $http_upgrade;\n\
|
||||
proxy_set_header Connection $connection_upgrade;\n\
|
||||
proxy_http_version 1.1;\n\
|
||||
\n\
|
||||
add_header X-Cache $upstream_cache_status;\n\
|
||||
proxy_ignore_headers Set-Cookie Cache-Control expires;\n\
|
||||
add_header Cache-Control no-cache;\n\
|
||||
\n\
|
||||
set $static_files_app 0;\n\
|
||||
{proxy_cache}\n\
|
||||
}\n\
|
||||
# PROXY-END"
|
||||
|
||||
tpl_proxy_cache = "\n\
|
||||
if ( $uri ~* \"\.(gif|png|jpg|css|js|woff|woff2)$\" )\n\
|
||||
{\n\
|
||||
expires {cache_time}m;\n\
|
||||
}\n\
|
||||
proxy_ignore_headers Set-Cookie Cache-Control expires;\n\
|
||||
proxy_cache mw_cache;\n\
|
||||
proxy_cache_key \"$host$uri$is_args$args\";\n\
|
||||
proxy_cache_valid 200 304 301 302 {cache_time}m;\n\
|
||||
"
|
||||
|
||||
tpl_proxy_nocache = "\n\
|
||||
set $static_files_app 0; \n\
|
||||
if ( $uri ~* \"\.(gif|png|jpg|css|js|woff|woff2)$\" )\n\
|
||||
{\n\
|
||||
set $static_files_app 1;\n\
|
||||
@@ -1926,8 +1986,7 @@ location ^~ {from} {\n\
|
||||
{\n\
|
||||
add_header Cache-Control no-cache;\n\
|
||||
}\n\
|
||||
}\n\
|
||||
#PROXY-END"
|
||||
"
|
||||
|
||||
# replace
|
||||
if _from[0] != '/':
|
||||
@@ -1935,28 +1994,69 @@ location ^~ {from} {\n\
|
||||
tpl = tpl.replace("{from}", _from, 999)
|
||||
tpl = tpl.replace("{to}", _to)
|
||||
tpl = tpl.replace("{host}", _host, 999)
|
||||
tpl = tpl.replace("{cache_time}", _cache_time, 999)
|
||||
|
||||
_id = mw.md5("{}+{}+{}".format(_from, _to, _siteName))
|
||||
for item in data:
|
||||
if item["id"] == _id:
|
||||
return mw.returnJson(False, "已存在该规则!")
|
||||
if item["from"] == _from:
|
||||
return mw.returnJson(False, "代理目录已存在!")
|
||||
data.append({
|
||||
"from": _from,
|
||||
"to": _to,
|
||||
"host": _host,
|
||||
"id": _id
|
||||
})
|
||||
if _open_cache == 'on':
|
||||
tpl_proxy_cache = tpl_proxy_cache.replace(
|
||||
"{cache_time}", _cache_time, 999)
|
||||
tpl = tpl.replace("{proxy_cache}", tpl_proxy_cache, 999)
|
||||
else:
|
||||
tpl = tpl.replace("{proxy_cache}", tpl_proxy_nocache, 999)
|
||||
|
||||
proxy_action = 'add'
|
||||
if _id == "":
|
||||
_id = mw.md5("{}".format(_name))
|
||||
else:
|
||||
proxy_action = 'edit'
|
||||
|
||||
conf_proxy = "{}/{}.conf".format(self.getProxyPath(_siteName), _id)
|
||||
conf_bk = "{}/{}.conf.txt".format(self.getProxyPath(_siteName), _id)
|
||||
|
||||
mw.writeFile(conf_proxy, tpl)
|
||||
|
||||
rule_test = mw.checkWebConfig()
|
||||
if rule_test != True:
|
||||
os.remove(conf_proxy)
|
||||
return mw.returnJson(False, "OpenResty配置测试不通过, 请重试: {}".format(rule_test))
|
||||
|
||||
if proxy_action == "add":
|
||||
# 添加代理
|
||||
_id = mw.md5("{}".format(_name))
|
||||
for item in data:
|
||||
if item["name"] == _name:
|
||||
return mw.returnJson(False, "名称重复!")
|
||||
if item["from"] == _from:
|
||||
return mw.returnJson(False, "代理目录已存在!")
|
||||
data.append({
|
||||
"name": _name,
|
||||
"from": _from,
|
||||
"to": _to,
|
||||
"host": _host,
|
||||
"open_cache": _open_cache,
|
||||
"open_proxy": _open_proxy,
|
||||
"cache_time": _cache_time,
|
||||
"id": _id,
|
||||
})
|
||||
else:
|
||||
# 修改代理
|
||||
dindex = -1
|
||||
for x in range(len(data)):
|
||||
if data[x]["id"] == _id:
|
||||
dindex = x
|
||||
break
|
||||
if dindex < 0:
|
||||
return mw.returnJson(False, "异常请求")
|
||||
data[dindex]['from'] = _from
|
||||
data[dindex]['to'] = _to
|
||||
data[dindex]['host'] = _host
|
||||
data[dindex]['open_cache'] = _open_cache
|
||||
data[dindex]['open_proxy'] = _open_proxy
|
||||
data[dindex]['cache_time'] = _cache_time
|
||||
|
||||
conf_file = "{}/{}.conf".format(self.getProxyPath(_siteName), _id)
|
||||
if _open_proxy != 'on':
|
||||
conf_file = "{}/{}.conf.txt".format(
|
||||
self.getProxyPath(_siteName), _id)
|
||||
|
||||
mw.writeFile(data_path, json.dumps(data))
|
||||
mw.writeFile(conf_file, tpl)
|
||||
os.rename(conf_proxy, conf_bk)
|
||||
|
||||
mw.writeFile(proxy_site_path, json.dumps(data))
|
||||
self.operateProxyConf(_siteName, 'start')
|
||||
mw.restartWeb()
|
||||
return mw.returnJson(True, "ok", {"hash": _id})
|
||||
@@ -1987,10 +2087,10 @@ location ^~ {from} {\n\
|
||||
self.getProxyPath(_siteName), _id)
|
||||
mw.execShell(cmd)
|
||||
except:
|
||||
return mw.returnJson(False, "删除失败!")
|
||||
return mw.returnJson(False, "删除反代失败!")
|
||||
|
||||
mw.restartWeb()
|
||||
return mw.returnJson(True, "删除成功!")
|
||||
return mw.returnJson(True, "删除反代成功!")
|
||||
|
||||
def getSiteTypesApi(self):
|
||||
# 取网站分类
|
||||
@@ -2057,7 +2157,7 @@ location ^~ {from} {\n\
|
||||
|
||||
##### ----- end ----- ###
|
||||
|
||||
# 域名编码转换
|
||||
# 域名编码转换
|
||||
def toPunycode(self, domain):
|
||||
import re
|
||||
if sys.version_info[0] == 2:
|
||||
@@ -2065,7 +2165,7 @@ location ^~ {from} {\n\
|
||||
tmp = domain.split('.')
|
||||
newdomain = ''
|
||||
for dkey in tmp:
|
||||
# 匹配非ascii字符
|
||||
# 匹配非ascii字符
|
||||
match = re.search(u"[\x80-\xff]+", dkey)
|
||||
if not match:
|
||||
newdomain += dkey + '.'
|
||||
@@ -2529,16 +2629,18 @@ location ^~ {from} {\n\
|
||||
self.createRootDir(self.sitePath)
|
||||
self.nginxAddConf()
|
||||
|
||||
mw.M('domain').add('pid,name,port,addtime',
|
||||
(pid, self.siteName, self.sitePort, mw.getDate()))
|
||||
|
||||
# 添加更多域名
|
||||
for domain in siteMenu['domainlist']:
|
||||
self.addDomain(domain, self.siteName, pid)
|
||||
|
||||
mw.M('domain').add('pid,name,port,addtime',
|
||||
(pid, self.siteName, self.sitePort, mw.getDate()))
|
||||
|
||||
data = {}
|
||||
data['siteStatus'] = False
|
||||
mw.restartWeb()
|
||||
|
||||
self.runHook('site_cb', 'add')
|
||||
return mw.returnJson(True, '添加成功')
|
||||
|
||||
def deleteWSLogs(self, webname):
|
||||
@@ -2588,6 +2690,8 @@ location ^~ {from} {\n\
|
||||
mw.execShell('rm -rf ' + ssl_acme_dir)
|
||||
|
||||
mw.M('sites').where("id=?", (sid,)).delete()
|
||||
mw.M('domain').where("pid=?", (sid,)).delete()
|
||||
mw.M('domain').where("name=?", (webname,)).delete()
|
||||
|
||||
# binding domain delete
|
||||
binding_list = mw.M('binding').field(
|
||||
@@ -2605,6 +2709,8 @@ location ^~ {from} {\n\
|
||||
|
||||
mw.M('binding').where("pid=?", (sid,)).delete()
|
||||
mw.restartWeb()
|
||||
|
||||
self.runHook('site_cb', 'delete')
|
||||
return mw.returnJson(True, '站点删除成功!')
|
||||
|
||||
def setEndDate(self, sid, edate):
|
||||
@@ -2614,7 +2720,7 @@ location ^~ {from} {\n\
|
||||
mw.writeLog('TYPE_SITE', '设置成功,站点到期后将自动停止!', (siteName, edate))
|
||||
return mw.returnJson(True, '设置成功,站点到期后将自动停止!')
|
||||
|
||||
# ssl相关方法 start
|
||||
# ssl相关方法 start
|
||||
def setSslConf(self, siteName):
|
||||
file = self.getHostConf(siteName)
|
||||
conf = mw.readFile(file)
|
||||
@@ -2713,9 +2819,4 @@ location ^~ {from} {\n\
|
||||
mw.execShell("chattr +i " + filename)
|
||||
|
||||
return mw.returnJson(True, '已打开防跨站设置!')
|
||||
|
||||
# 转换时间
|
||||
def strfToTime(self, sdate):
|
||||
import time
|
||||
return time.strftime('%Y-%m-%d', time.strptime(sdate, '%b %d %H:%M:%S %Y %Z'))
|
||||
# ssl相关方法 end
|
||||
# ssl相关方法 end
|
||||
|
||||
@@ -320,7 +320,7 @@ class ssh_terminal:
|
||||
|
||||
def getSshInfo(self, file):
|
||||
rdata = mw.readFile(file)
|
||||
destr = mw.deCrypt('mdserver-web', rdata)
|
||||
destr = mw.enDoubleCrypt('mdserver-web', rdata)
|
||||
return json.loads(destr)
|
||||
|
||||
def setAttr(self, sid, info):
|
||||
|
||||
@@ -166,6 +166,33 @@ class system_api:
|
||||
except:
|
||||
return False
|
||||
|
||||
def getEnvInfoApi(self, get=None):
|
||||
serverInfo = {}
|
||||
serverInfo['status'] = True
|
||||
sdir = mw.getServerDir()
|
||||
|
||||
serverInfo['webserver'] = '未安装'
|
||||
if os.path.exists(sdir + '/openresty/nginx/sbin/nginx'):
|
||||
serverInfo['webserver'] = 'OpenResty'
|
||||
serverInfo['php'] = []
|
||||
phpversions = ['52', '53', '54', '55', '56', '70', '71',
|
||||
'72', '73', '74', '80', '81', '82', '83', '84']
|
||||
phpPath = sdir + '/php/'
|
||||
for pv in phpversions:
|
||||
if not os.path.exists(phpPath + pv + '/bin/php'):
|
||||
continue
|
||||
serverInfo['php'].append(pv)
|
||||
serverInfo['mysql'] = False
|
||||
if os.path.exists(sdir + '/mysql/bin/mysql'):
|
||||
serverInfo['mysql'] = True
|
||||
import psutil
|
||||
try:
|
||||
diskInfo = psutil.disk_usage('/www')
|
||||
except:
|
||||
diskInfo = psutil.disk_usage('/')
|
||||
serverInfo['disk'] = diskInfo[2]
|
||||
return mw.returnJson(True, 'ok', serverInfo)
|
||||
|
||||
def getPanelInfo(self, get=None):
|
||||
# 取面板配置
|
||||
address = mw.GetLocalIp()
|
||||
@@ -724,6 +751,36 @@ class system_api:
|
||||
mw.execShell('rm -rf ' + toPath + '/mdserver-web-' + version)
|
||||
mw.execShell('rm -rf ' + toPath + '/mw.zip')
|
||||
|
||||
update_env = '''
|
||||
#!/bin/bash
|
||||
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
|
||||
|
||||
P_VER=`python3 -V | awk '{print $2}'`
|
||||
|
||||
if [ ! -f /www/server/mdserver-web/bin/activate ];then
|
||||
cd /www/server/mdserver-web && python3 -m venv .
|
||||
cd /www/server/mdserver-web && source /www/server/mdserver-web/bin/activate
|
||||
else
|
||||
cd /www/server/mdserver-web && source /www/server/mdserver-web/bin/activate
|
||||
fi
|
||||
|
||||
cn=$(curl -fsSL -m 10 http://ipinfo.io/json | grep "\"country\": \"CN\"")
|
||||
PIPSRC="https://pypi.python.org/simple"
|
||||
if [ ! -z "$cn" ];then
|
||||
PIPSRC="https://pypi.tuna.tsinghua.edu.cn/simple"
|
||||
fi
|
||||
|
||||
cd /www/server/mdserver-web && pip3 install -r /www/server/mdserver-web/requirements.txt -i $PIPSRC
|
||||
|
||||
P_VER_D=`echo "$P_VER"|awk -F '.' '{print $1}'`
|
||||
P_VER_M=`echo "$P_VER"|awk -F '.' '{print $2}'`
|
||||
NEW_P_VER=${P_VER_D}.${P_VER_M}
|
||||
|
||||
if [ -f /www/server/mdserver-web/version/r${NEW_P_VER}.txt ];then
|
||||
cd /www/server/mdserver-web && pip3 install -r /www/server/mdserver-web/version/r${NEW_P_VER}.txt -i $PIPSRC
|
||||
fi
|
||||
'''
|
||||
os.system(update_env)
|
||||
self.restartMw()
|
||||
return mw.returnJson(True, '安装更新成功!')
|
||||
|
||||
|
||||
@@ -0,0 +1,48 @@
|
||||
# coding:utf-8
|
||||
|
||||
# ---------------------------------------------------------------------------------
|
||||
# MW-Linux面板
|
||||
# ---------------------------------------------------------------------------------
|
||||
# copyright (c) 2018-∞(https://github.com/midoks/mdserver-web) All rights reserved.
|
||||
# ---------------------------------------------------------------------------------
|
||||
# Author: midoks <midoks@163.com>
|
||||
# ---------------------------------------------------------------------------------
|
||||
|
||||
# ---------------------------------------------------------------------------------
|
||||
# VIP用户管理
|
||||
# ---------------------------------------------------------------------------------
|
||||
|
||||
import mw
|
||||
|
||||
from flask import request
|
||||
import requests
|
||||
|
||||
|
||||
class vip_api:
|
||||
|
||||
api_url = 'https://wo.midoks.me/api/wp-json/vip'
|
||||
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
def loginApi(self):
|
||||
username = request.form.get('username', '')
|
||||
password = request.form.get('password', '')
|
||||
password = mw.aesEncrypt(password)
|
||||
|
||||
headers = {
|
||||
"Content-Type": "application/x-www-form-urlencoded",
|
||||
}
|
||||
|
||||
print("name:", str(username))
|
||||
print("pwd:", str(password))
|
||||
args = {
|
||||
'name': username,
|
||||
'pass': password
|
||||
}
|
||||
data = requests.post(self.api_url + '/v1/login',
|
||||
data=args, headers=headers)
|
||||
|
||||
print(data.text)
|
||||
|
||||
return mw.returnJson(False, "测试中!")
|
||||
@@ -0,0 +1,43 @@
|
||||
# coding: utf-8
|
||||
|
||||
import smtplib
|
||||
|
||||
from email import encoders
|
||||
from email.header import Header
|
||||
from email.mime.text import MIMEText
|
||||
from email.utils import parseaddr, formataddr
|
||||
|
||||
|
||||
def _format_addr(s):
|
||||
name, addr = parseaddr(s)
|
||||
return formataddr((Header(name, 'utf-8').encode(), addr))
|
||||
|
||||
|
||||
def send(smtp_host, smtp_port, username, password, to_mail, subject, content):
|
||||
|
||||
smtp = smtplib.SMTP()
|
||||
smtp.connect(smtp_host, port=smtp_port)
|
||||
smtp.login(user=username, password=password)
|
||||
|
||||
msg = MIMEText(content, 'plain', 'utf-8')
|
||||
msg['From'] = _format_addr(username)
|
||||
msg['To'] = _format_addr(to_mail)
|
||||
msg['Subject'] = Header(subject, 'utf-8').encode()
|
||||
|
||||
smtp.sendmail(from_addr=username, to_addrs=to_mail, msg=msg.as_string())
|
||||
return True
|
||||
|
||||
|
||||
def sendSSL(smtp_host, smtp_port, username, password, to_mail, subject, content):
|
||||
|
||||
smtp = smtplib.SMTP_SSL(smtp_host, port=smtp_port)
|
||||
smtp.login(user=username, password=password)
|
||||
|
||||
msg = MIMEText(content, 'plain', 'utf-8')
|
||||
msg['From'] = _format_addr(username)
|
||||
msg['To'] = _format_addr(to_mail)
|
||||
msg['Subject'] = Header(subject, 'utf-8').encode()
|
||||
|
||||
smtp.sendmail(from_addr=username, to_addrs=to_mail, msg=msg.as_string())
|
||||
smtp.quit()
|
||||
return True
|
||||
@@ -65,6 +65,9 @@ class ORM:
|
||||
def setCharset(self, charset):
|
||||
self.__DB_CHARSET = charset
|
||||
|
||||
def setHost(self, host):
|
||||
self.__DB_HOST = host
|
||||
|
||||
def setPort(self, port):
|
||||
self.__DB_PORT = port
|
||||
|
||||
|
||||
@@ -16,21 +16,21 @@ mw_start_task()
|
||||
{
|
||||
isStart=$(ps aux |grep 'task.py'|grep -v grep|awk '{print $2}')
|
||||
if [ "$isStart" == '' ];then
|
||||
echo -e "Starting mw-tasks... \c"
|
||||
echo -e "starting mw-tasks... \c"
|
||||
cd $DIR && python3 task.py >> ${DIR}/logs/task.log 2>&1 &
|
||||
sleep 0.3
|
||||
isStart=$(ps aux |grep 'task.py'|grep -v grep|awk '{print $2}')
|
||||
if [ "$isStart" == '' ];then
|
||||
echo -e "\033[31mfailed\033[0m"
|
||||
echo '------------------------------------------------------'
|
||||
tail -n 20 $DIR/logs/task.log
|
||||
echo '------------------------------------------------------'
|
||||
echo -e "\033[31mError: mw-tasks service startup failed.\033[0m"
|
||||
return;
|
||||
echo -e "\033[31mfailed\033[0m"
|
||||
echo '------------------------------------------------------'
|
||||
tail -n 20 $DIR/logs/task.log
|
||||
echo '------------------------------------------------------'
|
||||
echo -e "\033[31mError: mw-tasks service startup failed.\033[0m"
|
||||
return;
|
||||
fi
|
||||
echo -e "\033[32mdone\033[0m"
|
||||
else
|
||||
echo "Starting mw-tasks... mw-tasks (pid $(echo $isStart)) already running"
|
||||
echo "starting mw-tasks... mw-tasks (pid $(echo $isStart)) already running"
|
||||
fi
|
||||
}
|
||||
|
||||
@@ -62,14 +62,14 @@ mw_stop()
|
||||
PLIST=`ps -ef|grep app:app |grep -v grep|awk '{print $2}'`
|
||||
for i in $PLIST
|
||||
do
|
||||
kill -9 $i
|
||||
kill -9 $i > /dev/null 2>&1
|
||||
done
|
||||
|
||||
pids=`ps -ef|grep task.py | grep -v grep |awk '{print $2}'`
|
||||
arr=($pids)
|
||||
for p in ${arr[@]}
|
||||
do
|
||||
kill -9 $p
|
||||
kill -9 $p > /dev/null 2>&1
|
||||
done
|
||||
}
|
||||
|
||||
|
||||
+20
-17
@@ -16,6 +16,7 @@ cd /www/server/mdserver-web/plugins/php/versions && bash all_test.sh
|
||||
| CentOS 9 Stream |✅ |✅ |
|
||||
| Debian 10.3 |✅ |✅ |
|
||||
| Debian 11.3 |✅ |✅ |
|
||||
| Debian 12.0 |✅ |✅ |
|
||||
| Ubuntu 18.04 |✅ |✅ |
|
||||
| Ubuntu 20.04 |✅ |✅ |
|
||||
| Ubuntu 22.04 |✅ |✅ |
|
||||
@@ -28,23 +29,24 @@ cd /www/server/mdserver-web/plugins/php/versions && bash all_test.sh
|
||||
|
||||
|
||||
|
||||
| 系统名称 |PHP53 |PHP54 |PHP55 |PHP56 |PHP70 |PHP71 |PHP72 |PHP73 |PHP74 |PHP80 |PHP81 |
|
||||
| ----------------- |-------|-------|-------|-------|-------|-------|-------|-------|-------|-------|-------|
|
||||
| CentOS 7.9 |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |
|
||||
| CentOS 8.4 |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |
|
||||
| CentOS 8 Stream |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |
|
||||
| CentOS 9 Stream |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |
|
||||
| Debian 10.3 |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |
|
||||
| Debian 11.3 |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |
|
||||
| Ubuntu 18.04 |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |
|
||||
| Ubuntu 20.04 |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |
|
||||
| Ubuntu 22.04 |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |
|
||||
| Fedora 31 |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |
|
||||
| Fedora 32 |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |
|
||||
| AlmaLinux 9 |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |
|
||||
| RockyLinux 8.6 |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |
|
||||
| Arch Linux |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |
|
||||
| openSUSE 15.4 |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |
|
||||
| 系统名称 |PHP53 |PHP54 |PHP55 |PHP56 |PHP70 |PHP71 |PHP72 |PHP73 |PHP74 |PHP80 |PHP81 |PHP82 |
|
||||
| ----------------- |-------|-------|-------|-------|-------|-------|-------|-------|-------|-------|-------|-------|
|
||||
| CentOS 7.9 |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |
|
||||
| CentOS 8.4 |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |
|
||||
| CentOS 8 Stream |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |
|
||||
| CentOS 9 Stream |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |
|
||||
| Debian 10.3 |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |
|
||||
| Debian 11.3 |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |
|
||||
| Debian 12 |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |
|
||||
| Ubuntu 18.04 |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |
|
||||
| Ubuntu 20.04 |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |
|
||||
| Ubuntu 22.04 |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |
|
||||
| Fedora 31 |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |
|
||||
| Fedora 32 |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |
|
||||
| AlmaLinux 9 |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |
|
||||
| RockyLinux 8.6 |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |
|
||||
| Arch Linux |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |
|
||||
| openSUSE 15.4 |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |
|
||||
|
||||
|
||||
|
||||
@@ -56,6 +58,7 @@ cd /www/server/mdserver-web/plugins/php/versions && bash all_test.sh
|
||||
| CentOS 9 Stream |✅ |✅ |✅ |✅ |
|
||||
| Debian 10.3 |✅ |✅ |✅ |✅ |
|
||||
| Debian 11.3 |✅ |✅ |✅ |✅ |
|
||||
| Debian 12 |✅ |✅ |✅ |✅ |
|
||||
| Ubuntu 18.04 |✅ |✅ |✅ |✅ |
|
||||
| Ubuntu 20.04 |✅ |✅ |✅ |✅ |
|
||||
| Ubuntu 22.04 |✅ |✅ |✅ |✅ |
|
||||
|
||||
@@ -46,9 +46,8 @@ CREATE TABLE IF NOT EXISTS `firewall` (
|
||||
|
||||
INSERT INTO `firewall` (`id`, `port`, `ps`, `addtime`) VALUES
|
||||
(1, '80', '网站默认端口', '0000-00-00 00:00:00'),
|
||||
(2, '22', 'SSH远程管理服务', '0000-00-00 00:00:00'),
|
||||
(3, '443', 'HTTPS', '0000-00-00 00:00:00'),
|
||||
(4, '888', 'phpMyAdmin默认端口', '0000-00-00 00:00:00');
|
||||
(2, '443', 'HTTPS', '0000-00-00 00:00:00'),
|
||||
(3, '888', 'phpMyAdmin默认端口', '0000-00-00 00:00:00');
|
||||
|
||||
|
||||
|
||||
|
||||
+2
-9
@@ -39,20 +39,13 @@ server
|
||||
allow all;
|
||||
}
|
||||
|
||||
location ~ .*\\.(gif|jpg|jpeg|png|bmp|swf)$
|
||||
location ~ .*\\.(gif|jpg|jpeg|png|bmp|swf|js|css)$
|
||||
{
|
||||
expires 30d;
|
||||
error_log off;
|
||||
error_log /dev/null;
|
||||
access_log /dev/null;
|
||||
}
|
||||
|
||||
location ~ .*\\.(js|css)?$
|
||||
{
|
||||
expires 12h;
|
||||
error_log off;
|
||||
access_log /dev/null;
|
||||
}
|
||||
|
||||
access_log {$LOGPATH}/{$SERVER_NAME}.log main;
|
||||
error_log {$LOGPATH}/{$SERVER_NAME}.error.log;
|
||||
}
|
||||
@@ -35,18 +35,11 @@ server
|
||||
allow all;
|
||||
}
|
||||
|
||||
location ~ .*\\.(gif|jpg|jpeg|png|bmp|swf)$
|
||||
location ~ .*\\.(gif|jpg|jpeg|png|bmp|swf|js|css)$
|
||||
{
|
||||
expires 30d;
|
||||
error_log off;
|
||||
error_log /dev/null;
|
||||
access_log /dev/null;
|
||||
}
|
||||
|
||||
location ~ .*\\.(js|css)?$
|
||||
{
|
||||
expires 12h;
|
||||
error_log off;
|
||||
access_log /dev/null;
|
||||
expires 30d;
|
||||
}
|
||||
|
||||
access_log {$LOGPATH}/{$SERVER_MAIN}_{$DIRBIND}.log;
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
存放VIP关键数据
|
||||
@@ -194,14 +194,17 @@ def backupAllFunc(stype):
|
||||
'/scripts/backup.py ' + args
|
||||
|
||||
os.system(cmd)
|
||||
# 开始执行上传信息
|
||||
|
||||
bk_prefix = prefix_dict[stype]
|
||||
bk_name = stype
|
||||
# 开始执行上传信息.
|
||||
|
||||
if stype.find('database_') > -1:
|
||||
bk_name = 'database'
|
||||
plugin_name = stype.replace('database_', '')
|
||||
bk_prefix = plugin_name + '/db'
|
||||
stype = 'database'
|
||||
else:
|
||||
bk_prefix = prefix_dict[stype]
|
||||
bk_name = stype
|
||||
|
||||
find_path = mw.getBackupDir() + '/' + bk_name + '/' + bk_prefix + '_' + name
|
||||
find_new_file = "ls " + find_path + \
|
||||
@@ -212,10 +215,10 @@ def backupAllFunc(stype):
|
||||
mw.echoInfo("not find upload file!")
|
||||
return False
|
||||
|
||||
print("|-准备上传文件 {}".format(filename))
|
||||
ftp = FtpPSClient()
|
||||
ftp.uploadFile(filename, stype)
|
||||
|
||||
return True
|
||||
return ''
|
||||
|
||||
|
||||
def backupSite():
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 1.5 KiB |
Executable
+58
@@ -0,0 +1,58 @@
|
||||
<style type="text/css">
|
||||
|
||||
.docker-sub {
|
||||
border-bottom: 1px solid #ccc;
|
||||
height: 30px;
|
||||
line-height: 30px;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.docker-sub .on {
|
||||
border-bottom: 2px solid #20a53a;
|
||||
color: #20a53a;
|
||||
font-weight: bold;
|
||||
font-family: "宋体";
|
||||
}
|
||||
.docker-sub span {
|
||||
display: inline-block;
|
||||
font-size: 14px;
|
||||
height: 30px;
|
||||
padding: 0 25px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.docker-environments {
|
||||
resize: none;
|
||||
width: 330px;
|
||||
height: 80px;
|
||||
line-height: 20px;
|
||||
padding: 5px 8px;
|
||||
border: 1px solid #ddd;
|
||||
font-size: 12px;
|
||||
}
|
||||
</style>
|
||||
|
||||
<div class="bt-form">
|
||||
<div class='plugin_version'></div>
|
||||
<div class="bt-w-main">
|
||||
<div class="bt-w-menu">
|
||||
<p class="bgw" onclick="pluginService('docker');">服务</p>
|
||||
<p onclick="pluginInitD('docker');">自启动</p>
|
||||
<p onclick="dockerConList();">容器列表</p>
|
||||
<p onclick="dockerImageList();">镜像列表</p>
|
||||
<p onclick="dockerImageOutput();">镜像导出</p>
|
||||
<p onclick="dockerIpList();">IP地址池</p>
|
||||
<p onclick="repoList();">仓库</p>
|
||||
</div>
|
||||
<div class="bt-w-con pd15">
|
||||
<div class="soft-man-con"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script type="text/javascript">
|
||||
resetPluginWinWidth(800);
|
||||
resetPluginWinHeight(400);
|
||||
$.getScript( "/plugins/file?name=docker&f=js/docker.js", function(){
|
||||
pluginService('docker', $('.plugin_version').attr('version'));
|
||||
});
|
||||
</script>
|
||||
Executable
+860
@@ -0,0 +1,860 @@
|
||||
# coding:utf-8
|
||||
|
||||
import sys
|
||||
import io
|
||||
import os
|
||||
import time
|
||||
import re
|
||||
import json
|
||||
|
||||
sys.path.append(os.getcwd() + "/class/core")
|
||||
import mw
|
||||
|
||||
import docker
|
||||
|
||||
|
||||
app_debug = False
|
||||
if mw.isAppleSystem():
|
||||
app_debug = True
|
||||
|
||||
|
||||
def getDClient():
|
||||
try:
|
||||
client = docker.from_env()
|
||||
except Exception as e:
|
||||
client = docker.DockerClient(
|
||||
base_url='unix:///Users/midoks/.docker/run/docker.sock')
|
||||
return client
|
||||
|
||||
|
||||
def getPluginName():
|
||||
return 'docker'
|
||||
|
||||
|
||||
def getPluginDir():
|
||||
return mw.getPluginDir() + '/' + getPluginName()
|
||||
|
||||
|
||||
def getServerDir():
|
||||
return mw.getServerDir() + '/' + getPluginName()
|
||||
|
||||
|
||||
def getConf():
|
||||
path = getServerDir() + "/redis.conf"
|
||||
return path
|
||||
|
||||
|
||||
def getConfTpl():
|
||||
path = getPluginDir() + "/config/redis.conf"
|
||||
return path
|
||||
|
||||
|
||||
def getInitDTpl():
|
||||
path = getPluginDir() + "/init.d/" + getPluginName() + ".tpl"
|
||||
return path
|
||||
|
||||
|
||||
def getArgs():
|
||||
args = sys.argv[2:]
|
||||
tmp = {}
|
||||
args_len = len(args)
|
||||
|
||||
if args_len == 1:
|
||||
t = args[0].strip('{').strip('}')
|
||||
if t.strip() == '':
|
||||
tmp = []
|
||||
else:
|
||||
t = t.split(':', 1)
|
||||
tmp[t[0]] = t[1]
|
||||
tmp[t[0]] = t[1]
|
||||
elif args_len > 1:
|
||||
for i in range(len(args)):
|
||||
t = args[i].split(':', 1)
|
||||
tmp[t[0]] = t[1]
|
||||
return tmp
|
||||
|
||||
|
||||
def checkArgs(self, data, ck=[]):
|
||||
for i in range(len(ck)):
|
||||
print(data[i])
|
||||
if not ck[i] in data:
|
||||
return (False, mw.returnJson(False, '参数:(' + ck[i] + ')没有!'))
|
||||
return (True, mw.returnJson(True, 'ok'))
|
||||
|
||||
|
||||
def status():
|
||||
data = mw.execShell(
|
||||
"ps -ef|grep docker |grep -v grep | grep -v python | grep -v mdserver-web | awk '{print $2}'")
|
||||
|
||||
if data[0] == '':
|
||||
return 'stop'
|
||||
return 'start'
|
||||
|
||||
|
||||
def initDreplace():
|
||||
return ''
|
||||
|
||||
|
||||
def dockerOp(method):
|
||||
file = initDreplace()
|
||||
|
||||
if not mw.isAppleSystem():
|
||||
data = mw.execShell('systemctl ' + method + ' docker')
|
||||
if data[1] == '':
|
||||
return 'ok'
|
||||
return data[1]
|
||||
return 'fail'
|
||||
|
||||
|
||||
def start():
|
||||
return dockerOp('start')
|
||||
|
||||
|
||||
def stop():
|
||||
return dockerOp('stop')
|
||||
|
||||
|
||||
def restart():
|
||||
status = dockerOp('restart')
|
||||
|
||||
log_file = runLog()
|
||||
mw.execShell("echo '' > " + log_file)
|
||||
return status
|
||||
|
||||
|
||||
def reload():
|
||||
return dockerOp('reload')
|
||||
|
||||
|
||||
def initdStatus():
|
||||
if mw.isAppleSystem():
|
||||
return "Apple Computer does not support"
|
||||
|
||||
shell_cmd = 'systemctl status ' + \
|
||||
getPluginName() + ' | grep loaded | grep "enabled;"'
|
||||
data = mw.execShell(shell_cmd)
|
||||
if data[0] == '':
|
||||
return 'fail'
|
||||
return 'ok'
|
||||
|
||||
|
||||
def initdInstall():
|
||||
if mw.isAppleSystem():
|
||||
return "Apple Computer does not support"
|
||||
|
||||
mw.execShell('systemctl enable ' + getPluginName())
|
||||
return 'ok'
|
||||
|
||||
|
||||
def initdUinstall():
|
||||
if mw.isAppleSystem():
|
||||
return "Apple Computer does not support"
|
||||
|
||||
mw.execShell('systemctl disable ' + getPluginName())
|
||||
return 'ok'
|
||||
|
||||
# UTC时间转换为时间戳
|
||||
|
||||
|
||||
def utc_to_local(utc_time_str, utc_format='%Y-%m-%dT%H:%M:%S'):
|
||||
import pytz
|
||||
import datetime
|
||||
import time
|
||||
local_tz = pytz.timezone('Asia/Chongqing')
|
||||
local_format = "%Y-%m-%d %H:%M"
|
||||
utc_dt = datetime.datetime.strptime(utc_time_str, utc_format)
|
||||
local_dt = utc_dt.replace(tzinfo=pytz.utc).astimezone(local_tz)
|
||||
time_str = local_dt.strftime(local_format)
|
||||
return int(time.mktime(time.strptime(time_str, local_format)))
|
||||
|
||||
|
||||
def conList():
|
||||
c = getDClient()
|
||||
clist = c.containers.list(all=True)
|
||||
conList = []
|
||||
for con in clist:
|
||||
tmp = con.attrs
|
||||
tmp['Created'] = utc_to_local(tmp['Created'].split('.')[0])
|
||||
conList.append(tmp)
|
||||
return conList
|
||||
|
||||
|
||||
def conListData():
|
||||
try:
|
||||
clist = conList()
|
||||
except Exception as e:
|
||||
return mw.returnJson(False, '未开启Docker')
|
||||
return mw.returnJson(True, 'ok', clist)
|
||||
|
||||
|
||||
def dockerRemoveCon():
|
||||
args = getArgs()
|
||||
data = checkArgs(args, ['Hostname'])
|
||||
if not data[0]:
|
||||
return data[1]
|
||||
|
||||
Hostname = args['Hostname']
|
||||
|
||||
c = getDClient()
|
||||
try:
|
||||
conFind = c.containers.get(Hostname)
|
||||
try:
|
||||
path_list = conFind.attrs['GraphDriver'][
|
||||
'Data']['LowerDir'].split(':')
|
||||
for i in path_list:
|
||||
mw.execShell('chattr -R -i %s' % i)
|
||||
except:
|
||||
pass
|
||||
conFind.remove(force=True)
|
||||
return mw.returnJson(True, '成功删除!')
|
||||
except docker.errors.APIError as ex:
|
||||
return mw.returnJson(False, '删除失败!' + str(ex))
|
||||
|
||||
|
||||
def dockerLogCon():
|
||||
|
||||
args = getArgs()
|
||||
data = checkArgs(args, ['Hostname'])
|
||||
if not data[0]:
|
||||
return data[1]
|
||||
|
||||
Hostname = args['Hostname']
|
||||
|
||||
c = getDClient()
|
||||
try:
|
||||
conFind = c.containers.get(Hostname)
|
||||
if not conFind:
|
||||
return mw.returnJson(False, 'The specified container does not exist!')
|
||||
log = conFind.logs()
|
||||
if not isinstance(log, str):
|
||||
log = log.decode()
|
||||
return mw.returnJson(True, log)
|
||||
except docker.errors.APIError as ex:
|
||||
return mw.returnJson(False, 'Get Logs failed')
|
||||
|
||||
|
||||
def dockerRunCon():
|
||||
# 启动容器
|
||||
args = getArgs()
|
||||
data = checkArgs(args, ['Hostname'])
|
||||
if not data[0]:
|
||||
return data[1]
|
||||
|
||||
Hostname = args['Hostname']
|
||||
c = getDClient()
|
||||
try:
|
||||
conFind = c.containers.get(Hostname)
|
||||
if not conFind:
|
||||
return mw.returnJson(False, 'The specified container does not exist!')
|
||||
conFind.start()
|
||||
return mw.returnJson(True, '启动成功!')
|
||||
except docker.errors.APIError as ex:
|
||||
return mw.returnJson(False, '启动失败!' + str(ex))
|
||||
|
||||
|
||||
def dockerStopCon():
|
||||
# 停止容器
|
||||
args = getArgs()
|
||||
data = checkArgs(args, ['Hostname'])
|
||||
if not data[0]:
|
||||
return data[1]
|
||||
|
||||
Hostname = args['Hostname']
|
||||
c = getDClient()
|
||||
try:
|
||||
conFind = c.containers.get(Hostname)
|
||||
if not conFind:
|
||||
return mw.returnJson(False, 'The specified container does not exist!')
|
||||
conFind.stop()
|
||||
return mw.returnJson(True, '停止成功!')
|
||||
except docker.errors.APIError as ex:
|
||||
return mw.returnJson(False, '停止失败!' + str(ex))
|
||||
|
||||
|
||||
def dockerExec():
|
||||
# 容器执行命令
|
||||
args = getArgs()
|
||||
data = checkArgs(args, ['Hostname'])
|
||||
if not data[0]:
|
||||
return data[1]
|
||||
|
||||
Hostname = args['Hostname']
|
||||
|
||||
debug_path = 'data/debug.pl'
|
||||
if os.path.exists(debug_path):
|
||||
return mw.returnJson(False, '开发模式不能进入!')
|
||||
|
||||
c = getDClient()
|
||||
try:
|
||||
conFind = c.containers.get(Hostname)
|
||||
cmd = 'docker container exec -it %s /bin/sh' % Hostname
|
||||
return mw.returnJson(True, cmd)
|
||||
except docker.errors.APIError as ex:
|
||||
return mw.returnJson(False, '连接失败!')
|
||||
|
||||
|
||||
def imageList():
|
||||
imageList = []
|
||||
c = getDClient()
|
||||
ilist = c.images.list()
|
||||
for image in ilist:
|
||||
tmp_attrs = image.attrs
|
||||
if len(tmp_attrs['RepoTags']) == 1:
|
||||
tmp_image = {}
|
||||
tmp_image['Id'] = tmp_attrs['Id'].split(':')[1][:12]
|
||||
tmp_image['RepoTags'] = tmp_attrs['RepoTags'][0]
|
||||
tmp_image['Size'] = tmp_attrs['Size']
|
||||
tmp_image['Labels'] = tmp_attrs['Config']['Labels']
|
||||
tmp_image['Comment'] = tmp_attrs['Comment']
|
||||
tmp_image['Created'] = utc_to_local(
|
||||
tmp_attrs['Created'].split('.')[0])
|
||||
imageList.append(tmp_image)
|
||||
else:
|
||||
for i in range(len(tmp_attrs['RepoTags'])):
|
||||
tmp_image = {}
|
||||
tmp_image['Id'] = tmp_attrs['Id'].split(':')[1][:12]
|
||||
tmp_image['RepoTags'] = tmp_attrs['RepoTags'][i]
|
||||
tmp_image['Size'] = tmp_attrs['Size']
|
||||
tmp_image['Labels'] = tmp_attrs['Config']['Labels']
|
||||
tmp_image['Comment'] = tmp_attrs['Comment']
|
||||
tmp_image['Created'] = utc_to_local(
|
||||
tmp_attrs['Created'].split('.')[0])
|
||||
imageList.append(tmp_image)
|
||||
imageList = sorted(imageList, key=lambda x: x['Created'], reverse=True)
|
||||
return imageList
|
||||
|
||||
|
||||
def dockerPull():
|
||||
# pull Dockr 官方镜像
|
||||
args = getArgs()
|
||||
data = checkArgs(args, ['images'])
|
||||
if not data[0]:
|
||||
return data[1]
|
||||
|
||||
images = args['images']
|
||||
if ':' in images:
|
||||
pass
|
||||
else:
|
||||
images = images + ':latest'
|
||||
|
||||
c = getDClient()
|
||||
try:
|
||||
ret = c.images.pull(images)
|
||||
if ret:
|
||||
return mw.returnJson(True, '拉取成功!')
|
||||
else:
|
||||
return mw.returnJson(False, '拉取失败,请检查镜像名称或是否需要登录docker进行下载')
|
||||
except:
|
||||
ret = mw.execShell('docker image pull %s' % images)
|
||||
if 'invalid' in ret[-1]:
|
||||
return mw.returnJson(False, '拉取失败,请检查镜像名称或是否需要登录docker进行下载')
|
||||
else:
|
||||
return mw.returnJson(True, '拉取成功!')
|
||||
|
||||
|
||||
def dockerPlulPath(path):
|
||||
if not path and path == '':
|
||||
return mw.returnJson(False, 'Invalid address')
|
||||
|
||||
ret = mw.execShell('docker image pull %s' % path)
|
||||
if 'invalid' in ret[-1]:
|
||||
return mw.returnJson(False, '拉取失败,请检查镜像名称或是否需要登录docker进行下载')
|
||||
else:
|
||||
return mw.returnJson(True, '拉取成功!')
|
||||
|
||||
|
||||
def dockerPullReg():
|
||||
# pull Dockr 官方镜像
|
||||
args = getArgs()
|
||||
data = checkArgs(args, ['path'])
|
||||
if not data[0]:
|
||||
return data[1]
|
||||
|
||||
path = args['path']
|
||||
return dockerPlulPath(path)
|
||||
|
||||
|
||||
# 判断镜像是否存在
|
||||
def checkImage(path):
|
||||
image_list = imageList()
|
||||
for i in image_list:
|
||||
if path == i["RepoTags"]:
|
||||
return mw.returnData(False, '镜像已存在!')
|
||||
|
||||
|
||||
def dockerPullPrivateNew():
|
||||
# pull Dockr 官方镜像
|
||||
args = getArgs()
|
||||
data = checkArgs(args, ['path'])
|
||||
if not data[0]:
|
||||
return data[1]
|
||||
|
||||
path = args['path']
|
||||
check = checkImage(path)
|
||||
if check:
|
||||
return mw.getJson(check)
|
||||
|
||||
my_repo = repoList()
|
||||
if not my_repo:
|
||||
return mw.returnJson(False, '未登录任何私人存储库,请登录然后拉取')
|
||||
return dockerPlulPath(path)
|
||||
|
||||
|
||||
def imageListData():
|
||||
try:
|
||||
ilist = imageList()
|
||||
except Exception as e:
|
||||
return mw.returnJson(False, '未开启Docker')
|
||||
return mw.returnJson(True, 'ok', ilist)
|
||||
|
||||
|
||||
def dockerRemoveImage():
|
||||
args = getArgs()
|
||||
data = checkArgs(args, ['imageId', 'repoTags'])
|
||||
if not data[0]:
|
||||
return data[1]
|
||||
|
||||
repoTags = args['repoTags']
|
||||
imageId = args['imageId']
|
||||
|
||||
c = getDClient()
|
||||
try:
|
||||
c.images.remove(repoTags)
|
||||
return mw.returnJson(True, '成功删除')
|
||||
except:
|
||||
try:
|
||||
c.images.remove(imageId)
|
||||
return mw.returnJson(True, '成功删除!')
|
||||
except docker.errors.APIError as ex:
|
||||
return mw.returnJson(False, '删除失败, 当前镜像正在使用!')
|
||||
|
||||
|
||||
def getImageListFunc(dbname=''):
|
||||
bkDir = mw.getRootDir() + '/backup/docker'
|
||||
blist = os.listdir(bkDir)
|
||||
r = []
|
||||
|
||||
bname = 'db_' + dbname
|
||||
blen = len(bname)
|
||||
for x in blist:
|
||||
fbstr = x[0:blen]
|
||||
if fbstr == bname:
|
||||
r.append(x)
|
||||
return r
|
||||
|
||||
|
||||
def dockerImagePickDir():
|
||||
bkDir = mw.getRootDir() + '/backup/docker'
|
||||
return mw.returnJson(True, 'ok', bkDir)
|
||||
|
||||
|
||||
def dockerImagePickList():
|
||||
|
||||
bkDir = mw.getRootDir() + '/backup/docker'
|
||||
if not os.path.exists(bkDir):
|
||||
os.mkdir(bkDir)
|
||||
|
||||
r = os.listdir(bkDir)
|
||||
rr = []
|
||||
for x in range(0, len(r)):
|
||||
p = bkDir + '/' + r[x]
|
||||
data = {}
|
||||
data['name'] = r[x]
|
||||
|
||||
rsize = os.path.getsize(p)
|
||||
data['size'] = mw.toSize(rsize)
|
||||
|
||||
t = os.path.getctime(p)
|
||||
t = time.localtime(t)
|
||||
|
||||
data['time'] = time.strftime('%Y-%m-%d %H:%M:%S', t)
|
||||
rr.append(data)
|
||||
|
||||
data['file'] = p
|
||||
|
||||
return mw.returnJson(True, 'ok', rr)
|
||||
|
||||
|
||||
def dockerImagePickSave():
|
||||
# image 导出
|
||||
args = getArgs()
|
||||
data = checkArgs(args, ['images'])
|
||||
if not data[0]:
|
||||
return data[1]
|
||||
|
||||
bkDir = mw.getRootDir() + '/backup/docker/'
|
||||
images = args['images']
|
||||
try:
|
||||
file_name = bkDir + \
|
||||
str(time.strftime('%Y%m%d_%H%M%S', time.localtime())) + '.tar.gz'
|
||||
mw.execShell('docker image save %s | gzip > %s' %
|
||||
(images, file_name))
|
||||
return mw.returnJson(True, '导出镜像 {} 成功!'.format(file_name))
|
||||
except docker.errors.APIError as ex:
|
||||
return mw.returnJson(False, '操作失败: ' + str(ex))
|
||||
|
||||
|
||||
def dockerImagePickLoad():
|
||||
# 镜像文件导入
|
||||
args = getArgs()
|
||||
data = checkArgs(args, ['file'])
|
||||
if not data[0]:
|
||||
return data[1]
|
||||
try:
|
||||
file_path = args['file']
|
||||
if not os.path.exists(file_path):
|
||||
return mw.returnJson(False, '文件不存在')
|
||||
if file_path.endswith('.tar'):
|
||||
mw.execShell('docker image load < %s' % file_path)
|
||||
elif file_path.endswith('.tar.gz'):
|
||||
mw.execShell('gunzip -c %s | docker image load' % file_path)
|
||||
else:
|
||||
return mw.returnJson(False, '不支持改文件类型!')
|
||||
return mw.returnJson(True, '导入镜像文件成功!')
|
||||
except docker.errors.APIError as ex:
|
||||
return mw.returnJson(False, '操作失败: ' + str(ex))
|
||||
|
||||
|
||||
def dockerLoginCheck(user_name, user_pass, registry):
|
||||
# 登陆验证
|
||||
cmd = 'docker login -u=%s -p %s %s' % (user_name, user_pass, registry)
|
||||
# print(cmd)
|
||||
login_test = mw.execShell(cmd)
|
||||
# print(login_test)
|
||||
ret = 'required$|Error'
|
||||
ret2 = re.findall(ret, login_test[-1])
|
||||
if len(ret2) == 0:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
|
||||
def getDockerIpListData():
|
||||
# 取IP列表
|
||||
path = getServerDir()
|
||||
ipConf = path + '/iplist.json'
|
||||
if not os.path.exists(ipConf):
|
||||
return []
|
||||
iplist = json.loads(mw.readFile(ipConf))
|
||||
return iplist
|
||||
|
||||
|
||||
def getDockerIpList():
|
||||
data = getDockerIpListData()
|
||||
return mw.returnJson(True, 'ok!', data)
|
||||
|
||||
|
||||
def dockerAddIP():
|
||||
# 添加IP
|
||||
args = getArgs()
|
||||
data = checkArgs(args, ['address', 'netmask', 'gateway'])
|
||||
if not data[0]:
|
||||
return data[1]
|
||||
|
||||
path = getServerDir()
|
||||
ipConf = path + '/iplist.json'
|
||||
if not os.path.exists(ipConf):
|
||||
iplist = []
|
||||
mw.writeFile(ipConf, json.dumps(iplist))
|
||||
|
||||
iplist = json.loads(mw.readFile(ipConf))
|
||||
ipInfo = {
|
||||
'address': args['address'],
|
||||
'netmask': args['netmask'],
|
||||
'gateway': args['gateway'],
|
||||
}
|
||||
iplist.append(ipInfo)
|
||||
mw.writeFile(ipConf, json.dumps(iplist))
|
||||
return mw.returnJson(True, '添加成功!')
|
||||
|
||||
|
||||
def dockerDelIP():
|
||||
# 删除IP
|
||||
args = getArgs()
|
||||
data = checkArgs(args, ['address'])
|
||||
if not data[0]:
|
||||
return data[1]
|
||||
|
||||
path = getServerDir()
|
||||
ipConf = path + '/iplist.json'
|
||||
if not os.path.exists(ipConf):
|
||||
return mw.returnJson(False, '指定的IP不存在。!')
|
||||
iplist = json.loads(mw.readFile(ipConf))
|
||||
newList = []
|
||||
for ipInfo in iplist:
|
||||
if ipInfo['address'] == args['address']:
|
||||
continue
|
||||
newList.append(ipInfo)
|
||||
mw.writeFile(ipConf, json.dumps(newList))
|
||||
return mw.returnJson(True, '成功删除!')
|
||||
|
||||
|
||||
def getDockerCreateInfo():
|
||||
# 取创建依赖
|
||||
import psutil
|
||||
data = {}
|
||||
data['images'] = imageList()
|
||||
data['memSize'] = int(psutil.virtual_memory().total / 1024 / 1024)
|
||||
data['iplist'] = getDockerIpListData()
|
||||
return mw.returnJson(True, 'ok!', data)
|
||||
|
||||
|
||||
def __release_port(port):
|
||||
from collections import namedtuple
|
||||
try:
|
||||
import firewall_api
|
||||
firewall_api.firewall_api().addAcceptPortArgs(port, 'docker', 'port')
|
||||
return port
|
||||
except Exception as e:
|
||||
return "Release failed {}".format(e)
|
||||
|
||||
|
||||
def dockerPortCheck():
|
||||
args = getArgs()
|
||||
data = checkArgs(args, ['port'])
|
||||
if not data[0]:
|
||||
return data[1]
|
||||
|
||||
port = args['port']
|
||||
is_ok = IsPortExists(port)
|
||||
if is_ok:
|
||||
return mw.returnJson(True, 'ok')
|
||||
return mw.returnJson(False, 'fail')
|
||||
|
||||
|
||||
def IsPortExists(port):
|
||||
# 判断端口是否被占用
|
||||
ret = __check_dst_port(ip='localhost', port=port)
|
||||
ret2 = __check_dst_port(ip='0.0.0.0', port=port)
|
||||
if ret:
|
||||
return ret
|
||||
if not ret and ret2:
|
||||
return ret2
|
||||
if not ret and not ret2:
|
||||
return False
|
||||
|
||||
|
||||
def __check_dst_port(ip, port, timeout=3):
|
||||
# 端口检测
|
||||
import socket
|
||||
ok = True
|
||||
try:
|
||||
s = socket.socket()
|
||||
s.settimeout(timeout)
|
||||
s.connect((ip, port))
|
||||
s.close()
|
||||
except:
|
||||
ok = False
|
||||
return ok
|
||||
|
||||
|
||||
def dockerCreateCon():
|
||||
args = getArgs()
|
||||
data = checkArgs(args, ['environments', 'command',
|
||||
'entrypoint', 'image', 'mem_limit', 'ports', 'volumes'])
|
||||
if not data[0]:
|
||||
return data[1]
|
||||
|
||||
environments = args['environments']
|
||||
environments = environments.strip().split()
|
||||
|
||||
command = args['command']
|
||||
entrypoint = args['entrypoint']
|
||||
image = args['image']
|
||||
mem_limit = args['mem_limit']
|
||||
ports = args['ports']
|
||||
ports = ports.replace('[', '(').replace(']', ')')
|
||||
volumes = args['volumes']
|
||||
|
||||
# if __name__ == "__main__":
|
||||
# print(args)
|
||||
try:
|
||||
|
||||
c = getDClient()
|
||||
conObject = c.containers.run(
|
||||
image=image,
|
||||
mem_limit=mem_limit + 'M',
|
||||
ports=eval(ports),
|
||||
auto_remove=False,
|
||||
command=command,
|
||||
detach=True,
|
||||
stdin_open=True,
|
||||
tty=True,
|
||||
entrypoint=entrypoint,
|
||||
privileged=True,
|
||||
volumes=json.loads(volumes),
|
||||
cpu_shares=10,
|
||||
environment=environments
|
||||
)
|
||||
if conObject:
|
||||
__release_port(ports)
|
||||
return mw.returnJson(True, '创建成功!')
|
||||
|
||||
return mw.returnJson(False, '创建失败!')
|
||||
except docker.errors.APIError as ex:
|
||||
return mw.returnJson(False, '创建失败!' + str(ex))
|
||||
|
||||
|
||||
def dockerLogin():
|
||||
args = getArgs()
|
||||
|
||||
# print(args)
|
||||
data = checkArgs(args, ['user', 'passwd', 'hub_name',
|
||||
'namespace', 'registry', 'repository_name'])
|
||||
if not data[0]:
|
||||
return data[1]
|
||||
|
||||
user_name = args['user']
|
||||
user_pass = args['passwd']
|
||||
registry = args['registry']
|
||||
hub_name = args['hub_name']
|
||||
namespace = args['namespace']
|
||||
repository_name = args['repository_name']
|
||||
|
||||
ret_status = dockerLoginCheck(user_name, user_pass, registry)
|
||||
path = getServerDir()
|
||||
if ret_status:
|
||||
user_file = path + '/user.json'
|
||||
user_info = mw.readFile(user_file)
|
||||
if not user_info:
|
||||
user_info = []
|
||||
else:
|
||||
user_info = json.loads(user_info)
|
||||
|
||||
ret = {}
|
||||
ret['user_name'] = user_name
|
||||
ret['user_pass'] = user_pass
|
||||
ret['registry'] = registry
|
||||
ret['hub_name'] = hub_name
|
||||
ret['namespace'] = namespace
|
||||
ret['repository_name'] = repository_name
|
||||
if not registry:
|
||||
ret['registry'] = "docker.io"
|
||||
user_info.append(ret)
|
||||
mw.writeFile(user_file, json.dumps(user_info))
|
||||
return mw.returnJson(True, '成功登录!')
|
||||
return mw.returnJson(False, '登录失败!')
|
||||
|
||||
|
||||
# 删除用户信息
|
||||
def delete_user_info(registry):
|
||||
path = getServerDir()
|
||||
user_file = path + '/user.json'
|
||||
user_info = mw.readFile(user_file)
|
||||
if user_info:
|
||||
user_info = json.loads(user_info)
|
||||
for i in range(len(user_info)):
|
||||
if registry in user_info[i].values():
|
||||
del(user_info[i])
|
||||
mw.writeFile(user_file, json.dumps(user_info))
|
||||
return True
|
||||
|
||||
|
||||
def dockerLogout():
|
||||
args = getArgs()
|
||||
data = checkArgs(args, ['registry'])
|
||||
if not data[0]:
|
||||
return data[1]
|
||||
|
||||
registry = args['registry']
|
||||
if registry == "docker.io":
|
||||
registry = ""
|
||||
login_test = mw.execShell('docker logout %s' % registry)
|
||||
if registry == "":
|
||||
registry = "docker.io"
|
||||
ret = 'required$|Error'
|
||||
ret2 = re.findall(ret, login_test[-1])
|
||||
delete_user_info(registry)
|
||||
if len(ret2) == 0:
|
||||
return mw.returnJson(True, '退出成功')
|
||||
else:
|
||||
return mw.returnJson(True, '退出失败')
|
||||
|
||||
|
||||
def repoList():
|
||||
path = getServerDir()
|
||||
repostory_info = []
|
||||
user_file = path + '/user.json'
|
||||
|
||||
if os.path.exists(user_file):
|
||||
user_info = mw.readFile(user_file)
|
||||
user_info = json.loads(user_info)
|
||||
for i in user_info:
|
||||
tmp = {}
|
||||
tmp["hub_name"] = i["hub_name"]
|
||||
tmp["registry"] = i["registry"]
|
||||
tmp["namespace"] = i["namespace"]
|
||||
tmp['repository_name'] = i["repository_name"]
|
||||
repostory_info.append(tmp)
|
||||
|
||||
return mw.returnJson(True, 'ok', repostory_info)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
func = sys.argv[1]
|
||||
if func == 'status':
|
||||
print(status())
|
||||
elif func == 'start':
|
||||
print(start())
|
||||
elif func == 'stop':
|
||||
print(stop())
|
||||
elif func == 'restart':
|
||||
print(restart())
|
||||
elif func == 'reload':
|
||||
print(reload())
|
||||
elif func == 'initd_status':
|
||||
print(initdStatus())
|
||||
elif func == 'initd_install':
|
||||
print(initdInstall())
|
||||
elif func == 'initd_uninstall':
|
||||
print(initdUinstall())
|
||||
elif func == 'conf':
|
||||
print(getConf())
|
||||
elif func == 'con_list':
|
||||
print(conListData())
|
||||
elif func == 'docker_con_log':
|
||||
print(dockerLogCon())
|
||||
elif func == 'docker_remove_con':
|
||||
print(dockerRemoveCon())
|
||||
elif func == 'docker_run_con':
|
||||
print(dockerRunCon())
|
||||
elif func == 'docker_stop_con':
|
||||
print(dockerStopCon())
|
||||
elif func == 'docker_exec':
|
||||
print(dockerExec())
|
||||
elif func == 'docker_pull':
|
||||
print(dockerPull())
|
||||
elif func == 'docker_pull_reg':
|
||||
print(dockerPullReg())
|
||||
elif func == 'image_list':
|
||||
print(imageListData())
|
||||
elif func == 'image_pick_dir':
|
||||
print(dockerImagePickDir())
|
||||
elif func == 'image_pick_save':
|
||||
print(dockerImagePickSave())
|
||||
elif func == 'image_pick_load':
|
||||
print(dockerImagePickLoad())
|
||||
elif func == 'image_pick_list':
|
||||
print(dockerImagePickList())
|
||||
elif func == 'docker_get_iplist':
|
||||
print(getDockerIpList())
|
||||
elif func == 'docker_del_ip':
|
||||
print(dockerDelIP())
|
||||
elif func == 'docker_add_ip':
|
||||
print(dockerAddIP())
|
||||
elif func == 'get_docker_create_info':
|
||||
print(getDockerCreateInfo())
|
||||
elif func == 'docker_create_con':
|
||||
print(dockerCreateCon())
|
||||
elif func == 'docker_remove_image':
|
||||
print(dockerRemoveImage())
|
||||
elif func == 'docker_port_check':
|
||||
print(dockerPortCheck())
|
||||
elif func == 'docker_login':
|
||||
print(dockerLogin())
|
||||
elif func == 'docker_logout':
|
||||
print(dockerLogout())
|
||||
elif func == 'repo_list':
|
||||
print(repoList())
|
||||
else:
|
||||
print('error')
|
||||
Executable
+19
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"sort": 7,
|
||||
"ps": "Docker是一个开源的应用容器引擎",
|
||||
"name": "docker",
|
||||
"title": "Docker",
|
||||
"shell": "install.sh",
|
||||
"versions":["1.0"],
|
||||
"updates":["1.0"],
|
||||
"tip": "soft",
|
||||
"checks": "server/docker",
|
||||
"path": "server/docker",
|
||||
"display": 1,
|
||||
"author": "Zend",
|
||||
"date": "2017-04-01",
|
||||
"home": "https://docker.io",
|
||||
"doc1":"https://docker-py.readthedocs.io/",
|
||||
"type": 0,
|
||||
"pid": "2"
|
||||
}
|
||||
Executable
+84
@@ -0,0 +1,84 @@
|
||||
#!/bin/bash
|
||||
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
|
||||
export PATH
|
||||
|
||||
curPath=`pwd`
|
||||
rootPath=$(dirname "$curPath")
|
||||
rootPath=$(dirname "$rootPath")
|
||||
serverPath=$(dirname "$rootPath")
|
||||
|
||||
# cd /www/server/mdserver-web/plugins/docker && /bin/bash install.sh uninstall 1.0
|
||||
# cd /www/server/mdserver-web/plugins/docker && /bin/bash install.sh install 1.0
|
||||
|
||||
install_tmp=${rootPath}/tmp/mw_install.pl
|
||||
VERSION=$2
|
||||
|
||||
if [ -f ${rootPath}/bin/activate ];then
|
||||
source ${rootPath}/bin/activate
|
||||
fi
|
||||
|
||||
Install_Docker()
|
||||
{
|
||||
# which docker
|
||||
# if [ "$?" == "0" ];then
|
||||
# echo '安装已经完成docker' > $install_tmp
|
||||
# exit 0
|
||||
# fi
|
||||
|
||||
echo '正在安装脚本文件...' > $install_tmp
|
||||
mkdir -p $serverPath/source
|
||||
|
||||
if [ ! -d $serverPath/docker ];then
|
||||
curl -fsSL https://get.docker.com | bash
|
||||
mkdir -p $serverPath/docker
|
||||
fi
|
||||
|
||||
pip install docker
|
||||
pip install pytz
|
||||
|
||||
if [ -d $serverPath/docker ];then
|
||||
echo "${VERSION}" > $serverPath/docker/version.pl
|
||||
echo '安装完成' > $install_tmp
|
||||
|
||||
cd ${rootPath} && python3 ${rootPath}/plugins/docker/index.py start
|
||||
cd ${rootPath} && python3 ${rootPath}/plugins/docker/index.py initd_install
|
||||
fi
|
||||
}
|
||||
|
||||
Uninstall_Docker()
|
||||
{
|
||||
CMD=yum
|
||||
which apt
|
||||
if [ "$?" == "0" ];then
|
||||
CMD=apt
|
||||
fi
|
||||
|
||||
if [ -f /usr/lib/systemd/system/docker.service ];then
|
||||
systemctl stop docker
|
||||
systemctl disable docker
|
||||
rm -rf /usr/lib/systemd/system/docker.service
|
||||
systemctl daemon-reload
|
||||
fi
|
||||
|
||||
$CMD remove -y docker docker-ce-cli containerd.io
|
||||
# docker-client \
|
||||
# docker-client-latest \
|
||||
# docker-common \
|
||||
# docker-latest \
|
||||
# docker-latest-logrotate \
|
||||
# docker-logrotate \
|
||||
# docker-selinux \
|
||||
# docker-engine-selinux \
|
||||
# docker-engine \
|
||||
# docker-ce
|
||||
|
||||
rm -rf $serverPath/docker
|
||||
echo "Uninstall_Docker" > $install_tmp
|
||||
}
|
||||
|
||||
action=$1
|
||||
if [ "${1}" == 'install' ];then
|
||||
Install_Docker
|
||||
else
|
||||
Uninstall_Docker
|
||||
fi
|
||||
Executable
+1084
File diff suppressed because it is too large
Load Diff
@@ -33,7 +33,11 @@ if [ ! -d $WEB_PATH ];then
|
||||
mkdir -p $WEB_PATH
|
||||
rsync -vauP --delete --exclude=".*" $GIT_PROJECT_DIR/ $WEB_PATH
|
||||
else
|
||||
rsync -vauP --exclude=".*" $GIT_PROJECT_DIR/ $WEB_PATH
|
||||
if [ -f $GIT_PROJECT_DIR/exclude.list ];then
|
||||
rsync -vauP --delete --exclude-from="$GIT_PROJECT_DIR/exclude.list" $GIT_PROJECT_DIR/ $WEB_PATH
|
||||
else
|
||||
rsync -vauP --exclude=".*" $GIT_PROJECT_DIR/ $WEB_PATH
|
||||
fi
|
||||
fi
|
||||
|
||||
sysName=`uname`
|
||||
|
||||
@@ -8,8 +8,8 @@
|
||||
<p onclick="pluginConfig('gitea',null, 'conf');">配置文件</p>
|
||||
<p onclick="gogsSetConfig();">配置修改</p>
|
||||
<p onclick="giteaUserList();">用户列表</p>
|
||||
<p onclick="pluginLogs('gitea',null,'run_log');">运行日志</p>
|
||||
<p onclick="pluginLogs('gitea',null,'post_receive_log');" title="提交post-receive日志">提交日志</p>
|
||||
<!-- <p onclick="pluginLogs('gitea',null,'run_log');">运行日志</p> -->
|
||||
<!-- <p onclick="pluginLogs('gitea',null,'post_receive_log');" title="提交post-receive日志">提交日志</p> -->
|
||||
<p onclick="giteaRead();">使用说明</p>
|
||||
</div>
|
||||
<div class="bt-w-con pd15">
|
||||
|
||||
@@ -560,7 +560,7 @@ def userProjectList():
|
||||
data['data'] = ret_data
|
||||
data['args'] = args
|
||||
data['list'] = mw.getPage(
|
||||
{'count': dlist_sum, 'p': page, 'row': page_size, 'tojs': 'userProjectList'})
|
||||
{'count': dlist_sum, 'p': page, 'row': page_size, 'tojs': 'userProjectListPost'})
|
||||
|
||||
return mw.returnJson(True, 'OK', data)
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
{
|
||||
"ps": "Gitea 是一个开源社区驱动的轻量级代码托管解决方案。",
|
||||
"ps": "Gitea是一个开源社区驱动的轻量级代码托管解决方案。",
|
||||
"name": "gitea",
|
||||
"title": "Gitea",
|
||||
"versions": ["1.17.2"],
|
||||
"versions": ["1.17.2","1.18.5"],
|
||||
"tip": "soft",
|
||||
"install_pre_inspection":false,
|
||||
"uninstall_pre_inspection":true,
|
||||
|
||||
@@ -14,7 +14,7 @@ if [ -f ${rootPath}/bin/activate ];then
|
||||
fi
|
||||
|
||||
|
||||
URL_DOWNLOAD=https://dl.gitea.io/
|
||||
URL_DOWNLOAD=https://dl.gitea.com
|
||||
|
||||
|
||||
bash ${rootPath}/scripts/getos.sh
|
||||
|
||||
@@ -187,7 +187,7 @@ function userProjectList(user, search){
|
||||
<thead><tr><th>项目</th><th>操作</th></tr></thead>\
|
||||
<tbody></tbody>\
|
||||
</table>\
|
||||
<div class='dataTables_paginate paging_bootstrap pagination' style='margin-top:0px;'><ul id='gitea_page' class='page'></ul></div>\
|
||||
<div class='dataTables_paginate paging_bootstrap pagination' style='margin-top:0px;'><ul class='page'><div class='gitea_page'></div></ul></div>\
|
||||
</div>\
|
||||
</div>\
|
||||
</div>",
|
||||
@@ -230,7 +230,8 @@ function userProjectListPost(user, search){
|
||||
var project_list = rdata['data']['data'];
|
||||
for (i in project_list) {
|
||||
var name = project_list[i]['name'];
|
||||
list += '<tr><td>'+name+'</td>\
|
||||
list += '<tr>\
|
||||
<td>'+name+'</td>\
|
||||
<td>\
|
||||
<a class="btlink" target="_blank" href="'+rdata['data']['root_url']+user+'/'+name+'">源码</a> | \
|
||||
<a class="btlink" onclick="projectScript(\''+user+'\',\''+name+'\','+project_list[i]['has_hook']+');">脚本</a>\
|
||||
|
||||
@@ -739,7 +739,7 @@ def importDbBackup():
|
||||
|
||||
pwd = pSqliteDb('config').where('id=?', (1,)).getField('mysql_root')
|
||||
sock = getSocketFile()
|
||||
mysql_cmd = getServerDir() + '/bin/mysql -S ' + sock + ' -uroot -p' + pwd + \
|
||||
mysql_cmd = getServerDir() + '/bin/mariadb -S ' + sock + ' -uroot -p' + pwd + \
|
||||
' ' + name + ' < ' + file_path_sql
|
||||
|
||||
# print(mysql_cmd)
|
||||
@@ -800,7 +800,7 @@ def importDbExternal():
|
||||
sock = getSocketFile()
|
||||
|
||||
os.environ["MYSQL_PWD"] = pwd
|
||||
mysql_cmd = getServerDir() + '/bin/mysql -S ' + sock + ' -uroot -p' + \
|
||||
mysql_cmd = getServerDir() + '/bin/mariadb -S ' + sock + ' -uroot -p' + \
|
||||
pwd + ' ' + name + ' < ' + import_sql
|
||||
|
||||
# print(mysql_cmd)
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
"uninstall_pre_inspection":true,
|
||||
"checks": "server/mariadb",
|
||||
"path": "server/mariadb",
|
||||
"versions":["10.6","10.7","10.8","10.9"],
|
||||
"versions":["10.6","10.7","10.8","10.9","10.11","11.0"],
|
||||
"shell":"install.sh",
|
||||
"checks":"server/mariadb",
|
||||
"path":"server/mariadb",
|
||||
|
||||
@@ -1185,7 +1185,7 @@ function myLogs(){
|
||||
<span class="f14 c6 mr20">二进制日志 </span><span class="f14 c6 mr20">' + toSize(rdata.msg) + '</span>\
|
||||
'+line_status+'\
|
||||
<p class="f14 c6 mtb10" style="border-top:#ddd 1px solid; padding:10px 0">错误日志<button class="btn btn-default btn-clear btn-xs" style="float:right;" >清理日志</button></p>\
|
||||
<textarea readonly style="margin: 0px;width: 100%;height: 440px;background-color: #333;color:#fff; padding:0 5px" id="error_log"></textarea>\
|
||||
<textarea readonly style="margin: 0px;width: 100%;height: 438px;background-color: #333;color:#fff; padding:0 5px" id="error_log"></textarea>\
|
||||
</p>';
|
||||
$(".soft-man-con").html(limitCon);
|
||||
|
||||
|
||||
Executable
+119
@@ -0,0 +1,119 @@
|
||||
#!/bin/bash
|
||||
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
|
||||
export PATH
|
||||
|
||||
#https://dev.mysql.com/downloads/mysql/5.5.html#downloads
|
||||
#https://dev.mysql.com/downloads/file/?id=480541
|
||||
|
||||
curPath=`pwd`
|
||||
rootPath=$(dirname "$curPath")
|
||||
rootPath=$(dirname "$rootPath")
|
||||
serverPath=$(dirname "$rootPath")
|
||||
sysName=`uname`
|
||||
|
||||
install_tmp=${rootPath}/tmp/mw_install.pl
|
||||
mariadbDir=${serverPath}/source/mariadb
|
||||
|
||||
MY_VER=10.11.2
|
||||
|
||||
Install_app()
|
||||
{
|
||||
mkdir -p ${mariadbDir}
|
||||
echo '正在安装脚本文件...' > $install_tmp
|
||||
|
||||
if id mysql &> /dev/null ;then
|
||||
echo "mysql uid is `id -u www`"
|
||||
echo "mysql shell is `grep "^www:" /etc/passwd |cut -d':' -f7 `"
|
||||
else
|
||||
groupadd mysql
|
||||
useradd -g mysql mysql
|
||||
fi
|
||||
|
||||
if [ "$sysName" != "Darwin" ];then
|
||||
mkdir -p /var/log/mariadb
|
||||
touch /var/log/mariadb/mariadb.log
|
||||
fi
|
||||
|
||||
# ----- cpu start ------
|
||||
if [ -z "${cpuCore}" ]; then
|
||||
cpuCore="1"
|
||||
fi
|
||||
|
||||
if [ -f /proc/cpuinfo ];then
|
||||
cpuCore=`cat /proc/cpuinfo | grep "processor" | wc -l`
|
||||
fi
|
||||
|
||||
MEM_INFO=$(free -m|grep Mem|awk '{printf("%.f",($2)/1024)}')
|
||||
if [ "${cpuCore}" != "1" ] && [ "${MEM_INFO}" != "0" ];then
|
||||
if [ "${cpuCore}" -gt "${MEM_INFO}" ];then
|
||||
cpuCore="${MEM_INFO}"
|
||||
fi
|
||||
else
|
||||
cpuCore="1"
|
||||
fi
|
||||
|
||||
if [ "$cpuCore" -gt "2" ];then
|
||||
cpuCore=`echo "$cpuCore" | awk '{printf("%.f",($1)*0.8)}'`
|
||||
else
|
||||
cpuCore="1"
|
||||
fi
|
||||
# ----- cpu end ------
|
||||
|
||||
# if [ ! -f ${mariadbDir}/mariadb-${MY_VER}.tar.gz ];then
|
||||
# wget --no-check-certificate -O ${mariadbDir}/mariadb-${MY_VER}.tar.gz --tries=3 https://mirrors.aliyun.com/mariadb/mariadb-${MY_VER}/source/mariadb-${MY_VER}.tar.gz
|
||||
# fi
|
||||
|
||||
# https://downloads.mariadb.org/interstitial/mariadb-10.9.1/source/mariadb-10.9.1.tar.gz
|
||||
if [ ! -f ${mariadbDir}/mariadb-${MY_VER}.tar.gz ];then
|
||||
wget --no-check-certificate -O ${mariadbDir}/mariadb-${MY_VER}.tar.gz --tries=3 https://archive.mariadb.org/mariadb-${MY_VER}/source/mariadb-${MY_VER}.tar.gz
|
||||
fi
|
||||
|
||||
if [ ! -d ${mariadbDir}/mariadb-${MY_VER} ];then
|
||||
cd ${mariadbDir} && tar -zxvf ${mariadbDir}/mariadb-${MY_VER}.tar.gz
|
||||
fi
|
||||
|
||||
|
||||
if [ ! -d $serverPath/mariadb ];then
|
||||
cd ${mariadbDir}/mariadb-${MY_VER} && cmake \
|
||||
-DCMAKE_INSTALL_PREFIX=$serverPath/mariadb \
|
||||
-DMYSQL_DATADIR=$serverPath/mariadb/data/ \
|
||||
-DMYSQL_USER=mysql \
|
||||
-DMYSQL_UNIX_ADDR=$serverPath/mariadb/mysql.sock \
|
||||
-DWITH_MYISAM_STORAGE_ENGINE=1 \
|
||||
-DWITH_INNOBASE_STORAGE_ENGINE=1 \
|
||||
-DWITH_MEMORY_STORAGE_ENGINE=1 \
|
||||
-DENABLED_LOCAL_INFILE=1 \
|
||||
-DWITH_PARTITION_STORAGE_ENGINE=1 \
|
||||
-DEXTRA_CHARSETS=all \
|
||||
-DDEFAULT_CHARSET=utf8mb4 \
|
||||
-DDEFAULT_COLLATION=utf8mb4_general_ci \
|
||||
-DCMAKE_C_COMPILER=/usr/bin/gcc \
|
||||
-DCMAKE_CXX_COMPILER=/usr/bin/g++
|
||||
make -j${cpuCore} && make install && make clean
|
||||
|
||||
if [ -d $serverPath/mariadb ];then
|
||||
echo '10.11' > $serverPath/mariadb/version.pl
|
||||
echo '安装完成' > $install_tmp
|
||||
else
|
||||
echo '安装失败' > $install_tmp
|
||||
echo 'install fail'>&2
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
rm -rf ${mariadbDir}/mariadb-${MY_VER}
|
||||
rm -rf ${mariadbDir}/mariadb-${MY_VER}.tar.gz
|
||||
}
|
||||
|
||||
Uninstall_app()
|
||||
{
|
||||
rm -rf $serverPath/mariadb
|
||||
echo '卸载完成' > $install_tmp
|
||||
}
|
||||
|
||||
action=$1
|
||||
if [ "${1}" == 'install' ];then
|
||||
Install_app
|
||||
else
|
||||
Uninstall_app
|
||||
fi
|
||||
@@ -13,7 +13,7 @@ sysName=`uname`
|
||||
install_tmp=${rootPath}/tmp/mw_install.pl
|
||||
mariadbDir=${serverPath}/source/mariadb
|
||||
|
||||
MY_VER=10.6.9
|
||||
MY_VER=10.6.12
|
||||
|
||||
Install_app()
|
||||
{
|
||||
|
||||
@@ -14,7 +14,7 @@ sysName=`uname`
|
||||
install_tmp=${rootPath}/tmp/mw_install.pl
|
||||
mariadbDir=${serverPath}/source/mariadb
|
||||
|
||||
MY_VER=10.7.4
|
||||
MY_VER=10.7.8
|
||||
|
||||
Install_app()
|
||||
{
|
||||
|
||||
@@ -14,7 +14,7 @@ sysName=`uname`
|
||||
install_tmp=${rootPath}/tmp/mw_install.pl
|
||||
mariadbDir=${serverPath}/source/mariadb
|
||||
|
||||
MY_VER=10.8.5
|
||||
MY_VER=10.8.7
|
||||
|
||||
Install_app()
|
||||
{
|
||||
|
||||
@@ -14,7 +14,7 @@ sysName=`uname`
|
||||
install_tmp=${rootPath}/tmp/mw_install.pl
|
||||
mariadbDir=${serverPath}/source/mariadb
|
||||
|
||||
MY_VER=10.9.2
|
||||
MY_VER=10.9.5
|
||||
|
||||
Install_app()
|
||||
{
|
||||
|
||||
Executable
+119
@@ -0,0 +1,119 @@
|
||||
#!/bin/bash
|
||||
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
|
||||
export PATH
|
||||
|
||||
#https://dev.mysql.com/downloads/mysql/5.5.html#downloads
|
||||
#https://dev.mysql.com/downloads/file/?id=480541
|
||||
|
||||
curPath=`pwd`
|
||||
rootPath=$(dirname "$curPath")
|
||||
rootPath=$(dirname "$rootPath")
|
||||
serverPath=$(dirname "$rootPath")
|
||||
sysName=`uname`
|
||||
|
||||
install_tmp=${rootPath}/tmp/mw_install.pl
|
||||
mariadbDir=${serverPath}/source/mariadb
|
||||
|
||||
MY_VER=11.0.1
|
||||
|
||||
Install_app()
|
||||
{
|
||||
mkdir -p ${mariadbDir}
|
||||
echo '正在安装脚本文件...' > $install_tmp
|
||||
|
||||
if id mysql &> /dev/null ;then
|
||||
echo "mysql uid is `id -u www`"
|
||||
echo "mysql shell is `grep "^www:" /etc/passwd |cut -d':' -f7 `"
|
||||
else
|
||||
groupadd mysql
|
||||
useradd -g mysql mysql
|
||||
fi
|
||||
|
||||
if [ "$sysName" != "Darwin" ];then
|
||||
mkdir -p /var/log/mariadb
|
||||
touch /var/log/mariadb/mariadb.log
|
||||
fi
|
||||
|
||||
# ----- cpu start ------
|
||||
if [ -z "${cpuCore}" ]; then
|
||||
cpuCore="1"
|
||||
fi
|
||||
|
||||
if [ -f /proc/cpuinfo ];then
|
||||
cpuCore=`cat /proc/cpuinfo | grep "processor" | wc -l`
|
||||
fi
|
||||
|
||||
MEM_INFO=$(free -m|grep Mem|awk '{printf("%.f",($2)/1024)}')
|
||||
if [ "${cpuCore}" != "1" ] && [ "${MEM_INFO}" != "0" ];then
|
||||
if [ "${cpuCore}" -gt "${MEM_INFO}" ];then
|
||||
cpuCore="${MEM_INFO}"
|
||||
fi
|
||||
else
|
||||
cpuCore="1"
|
||||
fi
|
||||
|
||||
if [ "$cpuCore" -gt "2" ];then
|
||||
cpuCore=`echo "$cpuCore" | awk '{printf("%.f",($1)*0.8)}'`
|
||||
else
|
||||
cpuCore="1"
|
||||
fi
|
||||
# ----- cpu end ------
|
||||
|
||||
# if [ ! -f ${mariadbDir}/mariadb-${MY_VER}.tar.gz ];then
|
||||
# wget --no-check-certificate -O ${mariadbDir}/mariadb-${MY_VER}.tar.gz --tries=3 https://mirrors.aliyun.com/mariadb/mariadb-${MY_VER}/source/mariadb-${MY_VER}.tar.gz
|
||||
# fi
|
||||
|
||||
# https://downloads.mariadb.org/interstitial/mariadb-10.9.1/source/mariadb-10.9.1.tar.gz
|
||||
if [ ! -f ${mariadbDir}/mariadb-${MY_VER}.tar.gz ];then
|
||||
wget --no-check-certificate -O ${mariadbDir}/mariadb-${MY_VER}.tar.gz --tries=3 https://archive.mariadb.org/mariadb-${MY_VER}/source/mariadb-${MY_VER}.tar.gz
|
||||
fi
|
||||
|
||||
if [ ! -d ${mariadbDir}/mariadb-${MY_VER} ];then
|
||||
cd ${mariadbDir} && tar -zxvf ${mariadbDir}/mariadb-${MY_VER}.tar.gz
|
||||
fi
|
||||
|
||||
|
||||
if [ ! -d $serverPath/mariadb ];then
|
||||
cd ${mariadbDir}/mariadb-${MY_VER} && cmake \
|
||||
-DCMAKE_INSTALL_PREFIX=$serverPath/mariadb \
|
||||
-DMYSQL_DATADIR=$serverPath/mariadb/data/ \
|
||||
-DMYSQL_USER=mysql \
|
||||
-DMYSQL_UNIX_ADDR=$serverPath/mariadb/mysql.sock \
|
||||
-DWITH_MYISAM_STORAGE_ENGINE=1 \
|
||||
-DWITH_INNOBASE_STORAGE_ENGINE=1 \
|
||||
-DWITH_MEMORY_STORAGE_ENGINE=1 \
|
||||
-DENABLED_LOCAL_INFILE=1 \
|
||||
-DWITH_PARTITION_STORAGE_ENGINE=1 \
|
||||
-DEXTRA_CHARSETS=all \
|
||||
-DDEFAULT_CHARSET=utf8mb4 \
|
||||
-DDEFAULT_COLLATION=utf8mb4_general_ci \
|
||||
-DCMAKE_C_COMPILER=/usr/bin/gcc \
|
||||
-DCMAKE_CXX_COMPILER=/usr/bin/g++
|
||||
make -j${cpuCore} && make install && make clean
|
||||
|
||||
if [ -d $serverPath/mariadb ];then
|
||||
echo '10.11' > $serverPath/mariadb/version.pl
|
||||
echo '安装完成' > $install_tmp
|
||||
else
|
||||
echo '安装失败' > $install_tmp
|
||||
echo 'install fail'>&2
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
rm -rf ${mariadbDir}/mariadb-${MY_VER}
|
||||
rm -rf ${mariadbDir}/mariadb-${MY_VER}.tar.gz
|
||||
}
|
||||
|
||||
Uninstall_app()
|
||||
{
|
||||
rm -rf $serverPath/mariadb
|
||||
echo '卸载完成' > $install_tmp
|
||||
}
|
||||
|
||||
action=$1
|
||||
if [ "${1}" == 'install' ];then
|
||||
Install_app
|
||||
else
|
||||
Uninstall_app
|
||||
fi
|
||||
@@ -62,13 +62,16 @@ def getArgs():
|
||||
|
||||
if args_len == 1:
|
||||
t = args[0].strip('{').strip('}')
|
||||
t = t.split(':')
|
||||
if t.strip() == '':
|
||||
tmp = []
|
||||
else:
|
||||
t = t.split(':')
|
||||
tmp[t[0]] = t[1]
|
||||
tmp[t[0]] = t[1]
|
||||
elif args_len > 1:
|
||||
for i in range(len(args)):
|
||||
t = args[i].split(':')
|
||||
tmp[t[0]] = t[1]
|
||||
|
||||
return tmp
|
||||
|
||||
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 478 B |
Executable
+292
@@ -0,0 +1,292 @@
|
||||
<style>
|
||||
.migration_content {
|
||||
position: relative;
|
||||
height: 450px;
|
||||
}
|
||||
.step_head::after {
|
||||
background-color: #ddd;
|
||||
border-top: 2px solid #ccc;
|
||||
content: "";
|
||||
display: block;
|
||||
height: 3px;
|
||||
left: 84px;
|
||||
position: absolute;
|
||||
top: 14px;
|
||||
width: 500px;
|
||||
z-index: -1;
|
||||
}
|
||||
.step_head {
|
||||
margin: 20px 0;
|
||||
}
|
||||
.step_head ul li {
|
||||
width: 24.5%;
|
||||
display: inline-block;
|
||||
}
|
||||
.step_head ul li span {
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
line-height: 30px;
|
||||
display: block;
|
||||
border-radius: 15px;
|
||||
background-color: #ddd;
|
||||
color: #878787;
|
||||
margin: 0 auto;
|
||||
text-align: center;
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
}
|
||||
.step_head ul li p {
|
||||
text-align: center;
|
||||
margin-top: 15px;
|
||||
}
|
||||
.step_head ul li.active span {
|
||||
background-color: #20A53A;
|
||||
color: #fff;
|
||||
}
|
||||
.step_content {
|
||||
text-align: center;
|
||||
}
|
||||
.boxHide {
|
||||
display: none
|
||||
}
|
||||
.step_content .psync_info input {
|
||||
width: 300px;
|
||||
}
|
||||
.step_content .psync_info .panel_setp_span {
|
||||
height: 32px;
|
||||
line-height: 32px;
|
||||
overflow: hidden;
|
||||
padding-right: 20px;
|
||||
text-align: right;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
width: 130px;
|
||||
display: inline-block;
|
||||
float: left;
|
||||
}
|
||||
.step_content .psync_info .mtb20{
|
||||
padding-left: 40px;
|
||||
text-align: left;
|
||||
}
|
||||
.psync_path .table {
|
||||
text-align: left;
|
||||
}
|
||||
.psync_path .table > tbody > tr > td:nth-child(2n),
|
||||
.psync_path .table > thead > tr > th:nth-child(2n),
|
||||
.terlist .table > thead > tr > th:nth-child(2n),
|
||||
.terlist .table > tbody > tr > td:nth-child(2n),
|
||||
.terlist .table > tbody > tr > td:nth-child(1),
|
||||
.terlist .table > thead > tr > th:nth-child(1) {
|
||||
border-right: #ddd 1px solid;
|
||||
}
|
||||
|
||||
.checkbox_conten {
|
||||
background-color: #f3f3f3;
|
||||
border: #ddd 1px solid;
|
||||
border-radius: 4px;
|
||||
padding: 10px;
|
||||
margin: 0px 50px;
|
||||
}
|
||||
|
||||
.checkbox_item span {
|
||||
display: inline-block;
|
||||
height: 15px;
|
||||
overflow: hidden;
|
||||
text-align: left;
|
||||
display: block;
|
||||
padding-left: 20px;
|
||||
height: 20px;
|
||||
line-height: 20px;
|
||||
width: 152px;
|
||||
text-overflow: ellipsis;
|
||||
}
|
||||
|
||||
label.checkbox_label {
|
||||
margin-left: 6px;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
label.checkbox_label span {
|
||||
color: #000;
|
||||
}
|
||||
.psync_data {
|
||||
display: none;
|
||||
}
|
||||
.psync_data .checkbox_item input[type="checkbox"] {
|
||||
width: 15px;
|
||||
height: 15px;
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
margin-top: -6px;
|
||||
}
|
||||
|
||||
.checkbox_item label {
|
||||
font-weight: normal;
|
||||
white-space: nowrap;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.psync_data .checkbox_data {
|
||||
margin: 0 5px 5px;
|
||||
display: inline-block;
|
||||
width: 166px;
|
||||
vertical-align: text-top;
|
||||
}
|
||||
|
||||
.psync_data .checkbox_data:last-child {
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
.checkbox_item ul {
|
||||
background-color: #fff;
|
||||
max-height: 174px;
|
||||
overflow: auto;
|
||||
width: 100%;
|
||||
display: block;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
.checkbox_item ul li {
|
||||
padding: 0 6px;
|
||||
}
|
||||
|
||||
.psync_data .checkbox_con {
|
||||
display: block
|
||||
}
|
||||
|
||||
.progress {
|
||||
background-color: #e2e2e2;
|
||||
border-radius: 8px;
|
||||
height: 16px;
|
||||
line-height: 16px;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.progress-bar {
|
||||
background-color: #5ab76c;
|
||||
border-radius: 8px;
|
||||
height: 16px;
|
||||
max-width: 100%;
|
||||
position: absolute;
|
||||
text-align: right;
|
||||
transition: all 0.3s ease 0s;
|
||||
width: 0;
|
||||
}
|
||||
|
||||
.progress-text {
|
||||
font-size: 12px;
|
||||
color: #fff;
|
||||
padding: 0 10px;
|
||||
position: static;
|
||||
}
|
||||
|
||||
.qystatus {
|
||||
color: #666;
|
||||
margin-bottom: 10px;
|
||||
margin-left: 5px;
|
||||
}
|
||||
|
||||
.success {
|
||||
padding: 50px 0 60px;
|
||||
margin-left: -60px;
|
||||
}
|
||||
|
||||
.success p {
|
||||
margin-top: 20px;
|
||||
font-size: 16px;
|
||||
color: #666;
|
||||
}
|
||||
.psync_tips{
|
||||
color: red;
|
||||
font-size: 15px;
|
||||
background-color: #fbfbfb;
|
||||
border: 1px solid #eee;
|
||||
line-height: 46px;
|
||||
margin-bottom: 15px;
|
||||
padding-left: 10px;
|
||||
}
|
||||
.psync_tips span{
|
||||
font-size: 12px;
|
||||
}
|
||||
</style>
|
||||
<div class="migration_api pd15">
|
||||
<div class="migration_content">
|
||||
<div class="step_head">
|
||||
<ul>
|
||||
<li class="active"><span>1</span><p>填写信息</p></li>
|
||||
<li><span>2</span><p>检测环境</p></li>
|
||||
<li><span>3</span><p>选择数据</p></li>
|
||||
<li><span>4</span><p>一键迁移</p></li>
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="step_content">
|
||||
<div class="pd15 psync_info">
|
||||
<div class="psync_tips">
|
||||
<span class="glyphicon glyphicon-alert" style="color: #f39c12; margin-right: 10px;"></span>只需在发送数据服务器安装本软件,请填写<span>『 接收数据服务器 』</span>资料。
|
||||
<a href="#" target="_blank" class="bt-ico-ask" style="cursor: pointer;">?</a>
|
||||
</div>
|
||||
<div class="mtb20">
|
||||
<span class="panel_setp_span">接收数据的面板地址</span>
|
||||
<input type="text" class="bt-input-text" name="sync_url" value="" placeholder="接收数据面板地址,如: http://127.0.0.1:8888">
|
||||
</div>
|
||||
<div class="mtb20">
|
||||
<span class="panel_setp_span">接收数据的面板API</span>
|
||||
<input type="text" class="bt-input-text" name="sync_token" value="" placeholder="接收数据面板API密钥" />
|
||||
<a href="#" target="_blank" class="btlink ml5">获取API秘钥</a>
|
||||
</div>
|
||||
<div class="mtb20">
|
||||
<span class="panel_setp_span">IP白名单</span>
|
||||
<span style="height: 32px;line-height: 32px;">必须将本机器IP加入接收数据服务器API的IP白名单,<a href="#" href="javascript:;" target="_blank" class="btlink">如何添加白名单</a></span>
|
||||
</div>
|
||||
<div class="mtb20" style="text-align: left;margin-left: 130px;">
|
||||
<button class="btn btn-success infoNext">下一步</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="pa15 psync_path" style="margin:0 50px;"></div>
|
||||
<div class="pa15 psync_data" style="text-align: left;">
|
||||
<div class="checkbox_conten">
|
||||
<div class="checkbox_data">
|
||||
<div class="checkbox_item">
|
||||
<label class="checkbox_label">
|
||||
<input type="checkbox" id="sites_All" checked>
|
||||
<span>网站</span>
|
||||
</label>
|
||||
<ul id="sites_li"></ul>
|
||||
</div>
|
||||
</div>
|
||||
<div class="checkbox_data">
|
||||
<div class="checkbox_item">
|
||||
<label class="checkbox_label">
|
||||
<input type="checkbox" id="db_All" checked>
|
||||
<span>数据库</span>
|
||||
</label>
|
||||
<ul id="db_li"></ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="line mtb20" style="margin:20px 0 0 50px">
|
||||
<button class="btn btn-default btn-sm mr20 dataBack">上一步</button>
|
||||
<button class="btn btn-success btn-sm dataMigrate">一键迁移</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="pa15 psync_migrate"></div>
|
||||
</div>
|
||||
<hr style="margin-top: 5px;" />
|
||||
<div class="step_footer" style="margin: 5px 0 0 20px;text-align: left;">
|
||||
<ul class="help-info-text c7 mlr20" style="margin-top: 0px;">
|
||||
<li>一键迁移过程中是后台执行可以关闭当前窗口</li>
|
||||
<li>一键迁移迁移数据不涉及原来数据的增删(是将原来数据打包发送)</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<script type="text/javascript">
|
||||
resetPluginWinWidth(700);
|
||||
resetPluginWinWidth(700);
|
||||
$.getScript( "/plugins/file?name=migration_api&f=js/app.js", function(){
|
||||
initStep();
|
||||
});
|
||||
</script>
|
||||
Executable
+1090
File diff suppressed because it is too large
Load Diff
Executable
+18
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"sort": 7,
|
||||
"ps": "[<span style='color:red;'>潜龙勿用</span>]一键迁移,仅网站数据和MySQL数据(仅支持源码安装软件)",
|
||||
"name": "migration_api",
|
||||
"title": "一键迁移API",
|
||||
"shell": "install.sh",
|
||||
"versions":["1.0"],
|
||||
"updates":["1.0"],
|
||||
"tip": "soft",
|
||||
"checks": "server/migration_api",
|
||||
"path":"server/migration_api",
|
||||
"display": 1,
|
||||
"author": "midoks",
|
||||
"date": "2022-01-17",
|
||||
"home": "https://github.com/midoks/mdserver-web",
|
||||
"type": 0,
|
||||
"pid": "4"
|
||||
}
|
||||
Executable
+31
@@ -0,0 +1,31 @@
|
||||
#!/bin/bash
|
||||
|
||||
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
|
||||
export PATH
|
||||
|
||||
curPath=`pwd`
|
||||
rootPath=$(dirname "$curPath")
|
||||
rootPath=$(dirname "$rootPath")
|
||||
serverPath=$(dirname "$rootPath")
|
||||
|
||||
install_tmp=${rootPath}/tmp/mw_install.pl
|
||||
|
||||
VERSION=1.0
|
||||
|
||||
Install_App(){
|
||||
mkdir -p $serverPath/migration_api
|
||||
echo "${VERSION}" > $serverPath/migration_api/version.pl
|
||||
echo '正在安装脚本文件...' > $install_tmp
|
||||
}
|
||||
|
||||
Uninstall_App()
|
||||
{
|
||||
rm -rf $serverPath/migration_api
|
||||
}
|
||||
|
||||
action=$1
|
||||
if [ "${1}" == 'install' ];then
|
||||
Install_App
|
||||
else
|
||||
Uninstall_App
|
||||
fi
|
||||
Executable
+333
@@ -0,0 +1,333 @@
|
||||
|
||||
function maPostNoMsg(method,args,callback){
|
||||
var _args = null;
|
||||
if (typeof(args) == 'string'){
|
||||
_args = JSON.stringify(toArrayObject(args));
|
||||
} else {
|
||||
_args = JSON.stringify(args);
|
||||
}
|
||||
$.post('/plugins/run', {name:'migration_api', func:method, args:_args}, function(data) {
|
||||
if (!data.status){
|
||||
layer.msg(data.msg,{icon:0,time:2000,shade: [0.3, '#000']});
|
||||
return;
|
||||
}
|
||||
|
||||
if(typeof(callback) == 'function'){
|
||||
callback(data);
|
||||
}
|
||||
},'json');
|
||||
}
|
||||
|
||||
function maPost(method,args,callback, msg = '正在获取...'){
|
||||
var _args = null;
|
||||
if (typeof(args) == 'string'){
|
||||
_args = JSON.stringify(toArrayObject(args));
|
||||
} else {
|
||||
_args = JSON.stringify(args);
|
||||
}
|
||||
|
||||
var loadT = layer.msg(msg, { icon: 16, time: 0, shade: 0.3 });
|
||||
$.post('/plugins/run', {name:'migration_api', func:method, args:_args}, function(data) {
|
||||
layer.close(loadT);
|
||||
if (!data.status){
|
||||
layer.msg(data.msg,{icon:0,time:2000,shade: [0.3, '#000']});
|
||||
return;
|
||||
}
|
||||
|
||||
if(typeof(callback) == 'function'){
|
||||
callback(data);
|
||||
}
|
||||
},'json');
|
||||
}
|
||||
|
||||
function maAsyncPost(method,args){
|
||||
var _args = null;
|
||||
if (typeof(args) == 'string'){
|
||||
_args = JSON.stringify(toArrayObject(args));
|
||||
} else {
|
||||
_args = JSON.stringify(args);
|
||||
}
|
||||
return syncPost('/plugins/run', {name:'migration_api', func:method, args:_args});
|
||||
}
|
||||
|
||||
|
||||
function maPostCallbak(method, args, callback){
|
||||
var loadT = layer.msg('正在获取...', { icon: 16, time: 0, shade: 0.3 });
|
||||
|
||||
var req_data = {};
|
||||
req_data['name'] = 'migration_api';
|
||||
req_data['func'] = method;
|
||||
args['version'] = '1.0';
|
||||
|
||||
if (typeof(args) == 'string'){
|
||||
req_data['args'] = JSON.stringify(toArrayObject(args));
|
||||
} else {
|
||||
req_data['args'] = JSON.stringify(args);
|
||||
}
|
||||
|
||||
$.post('/plugins/callback', req_data, function(data) {
|
||||
layer.close(loadT);
|
||||
if (!data.status){
|
||||
layer.msg(data.msg,{icon:0,time:2000,shade: [0.3, '#000']});
|
||||
return;
|
||||
}
|
||||
|
||||
if(typeof(callback) == 'function'){
|
||||
callback(data);
|
||||
}
|
||||
},'json');
|
||||
}
|
||||
|
||||
|
||||
function selectProgress(val){
|
||||
$('.step_head li').removeClass('active');
|
||||
$('.step_head li').each(function(){
|
||||
var v = $(this).find('span').text();
|
||||
if (val == v){
|
||||
$(this).addClass('active');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function initStep1(){
|
||||
var url = $('input[name="sync_url"]').val();
|
||||
var token = $('input[name="sync_token"]').val();
|
||||
maPost('step_one',{url:url,token:token}, function(rdata){
|
||||
var rdata = $.parseJSON(rdata.data);
|
||||
showMsg(rdata.msg,function(){
|
||||
if (rdata.status){
|
||||
initStep2();
|
||||
}
|
||||
},{ icon: rdata.status ? 1 : 2 });
|
||||
},'API校验中...');
|
||||
}
|
||||
|
||||
function initStep2(){
|
||||
maPost('step_two',{}, function(rdata){
|
||||
var rdata = $.parseJSON(rdata.data);
|
||||
showMsg(rdata.msg,function(){
|
||||
if (rdata.status){
|
||||
selectProgress(2);
|
||||
$('.psync_info').hide();
|
||||
|
||||
var info = rdata.data;
|
||||
|
||||
var body = '<div class="divtable">\
|
||||
<table class="table table-hover">\
|
||||
<thead>\
|
||||
<tr><th style="border-right:1px solid #ddd">服务</th><th>当前服务器</th><th>远程服务器</th></tr>\
|
||||
</thead>\
|
||||
<tbody>\
|
||||
<tr>\
|
||||
<td style="border-right:1px solid #ddd">网站服务</td>\
|
||||
<td>'+info['local']['webserver']+'</td>\
|
||||
<td>'+info['remote']['webserver']+'</td>\
|
||||
</tr>\
|
||||
<tr>\
|
||||
<td style="border-right:1px solid #ddd">安装MySQL</td>\
|
||||
<td>'+(info['local']['mysql']?'是':'否')+'</td>\
|
||||
<td>'+(info['remote']['mysql']?'是':'否')+'</td>\
|
||||
</tr>\
|
||||
<tr>\
|
||||
<td style="border-right:1px solid #ddd">安装PHP</td>\
|
||||
<td>'+(info['local']['php'].join('/'))+'</td>\
|
||||
<td>'+(info['remote']['php'].join('/')) +'</td>\
|
||||
</tr>\
|
||||
<tr>\
|
||||
<td style="border-right:1px solid #ddd">可用磁盘</td>\
|
||||
<td>'+info['local']['disk']+'</td>\
|
||||
<td>'+info['remote']['disk'][0]['size'][0]+'</td>\
|
||||
</tr>\
|
||||
</tbody>\
|
||||
</table>\
|
||||
</div>';
|
||||
body += '<div class="line mtb20" style="text-align: left;">\
|
||||
<button class="btn btn-default btn-sm mr20 pathTestting">重新检测</button>\
|
||||
<button class="btn btn-default btn-sm mr20 pathBcak">上一步</button>\
|
||||
<button class="btn btn-success btn-sm psync-next pathNext">下一步</button>\
|
||||
</div>';
|
||||
|
||||
$('.psync_path').html(body);
|
||||
$('.psync_path').show();
|
||||
}
|
||||
},{ icon: rdata.status ? 1 : 2 });
|
||||
},'检测环境中...');
|
||||
}
|
||||
|
||||
function initStep3(){
|
||||
maPost('step_three',{}, function(rdata){
|
||||
var rdata = $.parseJSON(rdata.data);
|
||||
showMsg(rdata.msg,function(){
|
||||
if (rdata.status){
|
||||
selectProgress(3);
|
||||
var pdata = rdata.data;
|
||||
var site_li = '';
|
||||
for (var i = 0; i < pdata.sites.length; i++) {
|
||||
site_li+='<li>\
|
||||
<label>\
|
||||
<input type="checkbox" data-id="'+i+'" id="sites_'+pdata.sites[i]['name']+'" value="'+pdata.sites[i]['name']+'" name="sites" checked="">\
|
||||
<span title="'+pdata.sites[i]['name']+'">'+pdata.sites[i]['name']+'</span>\
|
||||
</label>\
|
||||
</li>';
|
||||
}
|
||||
|
||||
$('#sites_li').html(site_li);
|
||||
|
||||
|
||||
var db_li = '';
|
||||
for (var i = 0; i < pdata.databases.length; i++) {
|
||||
db_li+='<li>\
|
||||
<label>\
|
||||
<input type="checkbox" data-id="'+i+'" id="sites_'+pdata.databases[i]['name']+'" value="'+pdata.databases[i]['name']+'" name="databases" checked="">\
|
||||
<span title="'+pdata.databases[i]['name']+'">'+pdata.databases[i]['name']+'</span>\
|
||||
</label>\
|
||||
</li>';
|
||||
}
|
||||
$('#db_li').html(db_li);
|
||||
|
||||
$('.psync_path').hide();
|
||||
$('.psync_data').show();
|
||||
}
|
||||
},{ icon: rdata.status ? 1 : 2 });
|
||||
});
|
||||
}
|
||||
|
||||
function renderMigrationProgress(){
|
||||
maPostNoMsg('get_speed',{}, function(rdata){
|
||||
var rdata = $.parseJSON(rdata.data);
|
||||
// console.log('speed:',rdata.data);
|
||||
if (rdata.status){
|
||||
if (rdata['data']['action'] == 'True'){
|
||||
var end = '<div class="line">\
|
||||
<div class="success text-center" style="padding: 10px 0 15px;">\
|
||||
<p>数据迁移完成,请务必检查数据完整性!</p>\
|
||||
<p style="font-size: 14px;margin-top:2px;color: #939292;">传输大小: '+toSize(rdata['data']['total_size'])+',耗时: '+rdata['data']['total_time']+',平均速度: '+toSize(rdata['data']['speed'])+'/s</p>\
|
||||
<p class="mtb15">\
|
||||
<button class="btn btn-success btn-sm mr5 okBtn">确定完成</button>\
|
||||
<a class="btn btn-default btn-sm" style="margin-left: 10px;" target="_blank" href="/files/download?filename='+rdata['data']['log_file']+'">迁移日志</a>\
|
||||
</p>\
|
||||
</div>\
|
||||
</div>';
|
||||
$('.psync_migrate').html(end);
|
||||
} else{
|
||||
$('.psync_migrate .action').text(rdata['data']['action']);
|
||||
$('.psync_migrate .done').text(rdata['data']['done']);
|
||||
$('.psync_migrate pre').text(rdata['data']['log']);
|
||||
|
||||
var p = (rdata['data']['all_speed']/rdata['data']['all_total'])*100;
|
||||
if (p>100){
|
||||
p = 100;
|
||||
}
|
||||
$('.psync_migrate .progress_info_bar').width(p+'%');
|
||||
$('.psync_migrate .progress_info').text(p+'%');
|
||||
|
||||
renderMigrationProgress();
|
||||
}
|
||||
} else{
|
||||
layer.msg(rdata.msg,{icon:1});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function initStep4(){
|
||||
var site_checked = '';
|
||||
$('input[name="sites"]:checked').each(function(){
|
||||
site_checked += $(this).val()+',';
|
||||
});
|
||||
|
||||
var databases_checked = '';
|
||||
$('input[name="databases"]:checked').each(function(){
|
||||
databases_checked+=$(this).val()+',';
|
||||
});
|
||||
|
||||
maPost('step_four',{sites:site_checked,databases:databases_checked}, function(rdata){
|
||||
var rdata = $.parseJSON(rdata.data);
|
||||
selectProgress(4);
|
||||
|
||||
var progress = '<div style="margin: 0 40px;">\
|
||||
<div class="line">\
|
||||
<div style="text-align:left"><span class="action">--</span>\
|
||||
<span style="margin-left: 20px;" class="done">当前: --</span><img src="/static/img/ing.gif"><a style="position: absolute;right: 40px;" class="btlink psync_close" onclick="migrate.close();">[取消]</a></div>\
|
||||
<div class="bt-progress" style="border-radius:0;height:20px;line-height:19px">\
|
||||
<div class="bt-progress-bar progress_info_bar" style="border-radius: 0px; height: 20px; width: 0%;">\
|
||||
<span class="bt-progress-text progress_info"></span></div>\
|
||||
</div>\
|
||||
</div>\
|
||||
</div>\
|
||||
<pre style="height: 222px;text-align: left;margin:5px 38px 0;font-size: 12px;line-height: 20px;padding: 10px;background-color: #333;color: #fff;"></pre>\
|
||||
</div>';
|
||||
|
||||
|
||||
$('.psync_data').hide();
|
||||
$('.psync_migrate').html(progress);
|
||||
$('.psync_migrate').show();
|
||||
|
||||
renderMigrationProgress();
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
function initStep(){
|
||||
maPost('get_conf',{}, function(rdata){
|
||||
var rdata = $.parseJSON(rdata.data);
|
||||
$('input[name="sync_url"]').val(rdata.data['url']);
|
||||
$('input[name="sync_token"]').val(rdata.data['token']);
|
||||
});
|
||||
|
||||
$('.infoNext').click(function(){
|
||||
initStep1();
|
||||
});
|
||||
|
||||
// 重新检测按钮
|
||||
$('.psync_path').on('click', '.pathTestting', function () {
|
||||
initStep2();
|
||||
});
|
||||
|
||||
$('.psync_path').on('click', '.pathBcak', function(){
|
||||
$('.psync_path').hide();
|
||||
$('.psync_info').show();
|
||||
selectProgress(1);
|
||||
});
|
||||
|
||||
$('.psync_path').on('click', '.pathNext', function(){
|
||||
initStep3();
|
||||
});
|
||||
|
||||
|
||||
$('.psync_data').on('click', '.dataBack', function(){
|
||||
$('.psync_data').hide();
|
||||
$('.psync_path').show();
|
||||
selectProgress(2);
|
||||
});
|
||||
|
||||
|
||||
$('.psync_data').on('click', '.dataMigrate', function(){
|
||||
initStep4();
|
||||
});
|
||||
|
||||
$('#sites_All').on('click',function(){
|
||||
var ch = $(this).prop('checked');
|
||||
$('#sites_li input').prop('checked',ch);
|
||||
});
|
||||
|
||||
$('#db_All').on('click',function(){
|
||||
var ch = $(this).prop('checked');
|
||||
$('#db_li input').prop('checked',ch);
|
||||
});
|
||||
|
||||
$('.psync_migrate').on('click', '.okBtn', function(){
|
||||
$('.psync_migrate').hide();
|
||||
$('.psync_info').show();
|
||||
selectProgress(1);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -65,6 +65,7 @@ binlog-ignore-db = performance_schema
|
||||
#slave
|
||||
log-slave-updates
|
||||
#replicate-do-db
|
||||
slave_skip_errors=1062
|
||||
replicate-ignore-db = information_schema
|
||||
replicate-ignore-db = performance_schema
|
||||
replicate-ignore-db = mysql
|
||||
|
||||
@@ -62,6 +62,7 @@ binlog-ignore-db = performance_schema
|
||||
#slave
|
||||
log_replica_updates
|
||||
#replicate-do-db
|
||||
slave_skip_errors=1062
|
||||
replicate-ignore-db = information_schema
|
||||
replicate-ignore-db = performance_schema
|
||||
replicate-ignore-db = mysql
|
||||
|
||||
@@ -2474,7 +2474,8 @@ def doFullSyncUser(version=''):
|
||||
|
||||
if not os.path.exists(bak_file):
|
||||
dump_sql_data = getServerDir() + "/bin/mysqldump " + dmp_option + " --force --opt --default-character-set=utf8 --single-transaction -h" + ip + " -P" + \
|
||||
port + " -u" + user + " -p" + apass + " " + sync_db + " > " + bak_file
|
||||
port + " -u" + user + " -p" + apass + \
|
||||
" --ssl-mode=DISABLED " + sync_db + " > " + bak_file
|
||||
mw.execShell(dump_sql_data)
|
||||
|
||||
writeDbSyncStatus({'code': 2, 'msg': '本地导入数据...', 'progress': 40})
|
||||
@@ -2682,6 +2683,9 @@ def uninstallPreInspection(version):
|
||||
if mw.isDebugMode():
|
||||
return 'ok'
|
||||
|
||||
import plugins_api
|
||||
plugins_api.plugins_api().removeIndex(getPluginName(), version)
|
||||
|
||||
return "请手动删除MySQL[{}]<br/> rm -rf {}".format(version, getServerDir())
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
@@ -1208,7 +1208,7 @@ function myLogs(){
|
||||
<span class="f14 c6 mr20">二进制日志 </span><span class="f14 c6 mr20">' + toSize(rdata.msg) + '</span>\
|
||||
'+line_status+'\
|
||||
<p class="f14 c6 mtb10" style="border-top:#ddd 1px solid; padding:10px 0">错误日志<button class="btn btn-default btn-clear btn-xs" style="float:right;" >清理日志</button></p>\
|
||||
<textarea readonly style="margin: 0px;width: 100%;height: 440px;background-color: #333;color:#fff; padding:0 5px" id="error_log"></textarea>\
|
||||
<textarea readonly style="margin: 0px;width: 100%;height: 438px;background-color: #333;color:#fff; padding:0 5px" id="error_log"></textarea>\
|
||||
</p>';
|
||||
$(".soft-man-con").html(limitCon);
|
||||
|
||||
|
||||
@@ -66,6 +66,7 @@ binlog-ignore-db = performance_schema
|
||||
#slave
|
||||
log-slave-updates
|
||||
#replicate-do-db
|
||||
slave_skip_errors=1062
|
||||
replicate-ignore-db = information_schema
|
||||
replicate-ignore-db = performance_schema
|
||||
replicate-ignore-db = mysql
|
||||
|
||||
@@ -62,6 +62,7 @@ binlog-ignore-db = performance_schema
|
||||
#slave
|
||||
log_replica_updates
|
||||
#replicate-do-db
|
||||
slave_skip_errors=1062
|
||||
replicate-ignore-db = information_schema
|
||||
replicate-ignore-db = performance_schema
|
||||
replicate-ignore-db = mysql
|
||||
|
||||
@@ -2434,7 +2434,8 @@ def doFullSyncUser(version=''):
|
||||
|
||||
if not os.path.exists(bak_file):
|
||||
dump_sql_data = getServerDir() + "/bin/mysqldump " + dmp_option + " --force --opt --default-character-set=utf8 --single-transaction -h" + ip + " -P" + \
|
||||
port + " -u" + user + " -p" + apass + " " + sync_db + " > " + bak_file
|
||||
port + " -u" + user + " -p" + apass + \
|
||||
" --ssl-mode=DISABLED " + sync_db + " > " + bak_file
|
||||
mw.execShell(dump_sql_data)
|
||||
|
||||
writeDbSyncStatus({'code': 2, 'msg': '本地导入数据...', 'progress': 40})
|
||||
@@ -2639,6 +2640,9 @@ def uninstallPreInspection(version):
|
||||
if mw.isDebugMode():
|
||||
return 'ok'
|
||||
|
||||
import plugins_api
|
||||
plugins_api.plugins_api().removeIndex(getPluginName(), version)
|
||||
|
||||
return "请手动删除MySQL[{}]<br/> rm -rf {}".format(version, getServerDir())
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
@@ -1208,7 +1208,7 @@ function myLogs(){
|
||||
<span class="f14 c6 mr20">二进制日志 </span><span class="f14 c6 mr20">' + toSize(rdata.msg) + '</span>\
|
||||
'+line_status+'\
|
||||
<p class="f14 c6 mtb10" style="border-top:#ddd 1px solid; padding:10px 0">错误日志<button class="btn btn-default btn-clear btn-xs" style="float:right;" >清理日志</button></p>\
|
||||
<textarea readonly style="margin: 0px;width: 100%;height: 440px;background-color: #333;color:#fff; padding:0 5px" id="error_log"></textarea>\
|
||||
<textarea readonly style="margin: 0px;width: 100%;height: 438px;background-color: #333;color:#fff; padding:0 5px" id="error_log"></textarea>\
|
||||
</p>';
|
||||
$(".soft-man-con").html(limitCon);
|
||||
|
||||
|
||||
@@ -39,7 +39,7 @@ max_connections = 500
|
||||
max_connect_errors = 100
|
||||
open_files_limit = 65535
|
||||
|
||||
skip-name-resolve=1
|
||||
skip_name_resolve=1
|
||||
#skip-grant-tables
|
||||
#skip-networking
|
||||
#skip-external-locking
|
||||
@@ -70,6 +70,7 @@ log-slave-updates = 1
|
||||
# Prevent replication from starting automatically with MySQL
|
||||
#skip-slave-start = 1
|
||||
#replicate-do-db
|
||||
slave_skip_errors=1062
|
||||
replicate-ignore-db = information_schema
|
||||
replicate-ignore-db = performance_schema
|
||||
replicate-ignore-db = mysql
|
||||
|
||||
@@ -41,7 +41,7 @@ max_connections = 500
|
||||
max_connect_errors = 100
|
||||
open_files_limit = 65535
|
||||
|
||||
skip-name-resolve=1
|
||||
skip_name_resolve=1
|
||||
#skip-grant-tables
|
||||
#skip-networking
|
||||
#skip-external-locking
|
||||
@@ -72,6 +72,7 @@ log-slave-updates = 1
|
||||
# Prevent replication from starting automatically with MySQL
|
||||
#skip-slave-start = 1
|
||||
#replicate-do-db
|
||||
slave_skip_errors=1062
|
||||
replicate-ignore-db = information_schema
|
||||
replicate-ignore-db = performance_schema
|
||||
replicate-ignore-db = mysql
|
||||
|
||||
@@ -17,6 +17,7 @@ datadir = {$SERVER_APP_PATH}/data
|
||||
log-error = {$SERVER_APP_PATH}/data/error.log
|
||||
server-id = {$SERVER_ID}
|
||||
|
||||
explicit_defaults_for_timestamp=true
|
||||
default_storage_engine = InnoDB
|
||||
|
||||
|
||||
@@ -40,7 +41,7 @@ max_connect_errors = 100
|
||||
open_files_limit = 2560
|
||||
max_allowed_packet = 128M
|
||||
|
||||
skip_name_resolve = 1
|
||||
skip_name_resolve=1
|
||||
skip-ssl
|
||||
#skip-networking
|
||||
#skip-external-locking
|
||||
@@ -73,6 +74,7 @@ binlog-ignore-db = performance_schema
|
||||
# Prevent replication from starting automatically with MySQL
|
||||
#skip-slave-start = 1
|
||||
#replicate-do-db
|
||||
slave_skip_errors=1062
|
||||
replicate-ignore-db = information_schema
|
||||
replicate-ignore-db = performance_schema
|
||||
replicate-ignore-db = mysql
|
||||
@@ -94,7 +96,7 @@ innodb_file_per_table = 1
|
||||
innodb_large_prefix = 1
|
||||
|
||||
secure-file-priv={$SERVER_APP_PATH}/tmp
|
||||
|
||||
#secure-file-priv=disabled
|
||||
[mysqldump]
|
||||
quick
|
||||
|
||||
|
||||
@@ -8,8 +8,8 @@ default-character-set = UTF8MB4
|
||||
[mysqld]
|
||||
!include {$SERVER_APP_PATH}/etc/mode/classic.cnf
|
||||
|
||||
sha256_password_private_key_path=mysql.pem
|
||||
sha256_password_public_key_path=mysql.pub
|
||||
sha256_password_private_key_path={$SERVER_APP_PATH}/data/mysql.pem
|
||||
sha256_password_public_key_path={$SERVER_APP_PATH}/data/mysql.pub
|
||||
authentication_policy=mysql_native_password
|
||||
|
||||
pid-file = {$SERVER_APP_PATH}/data/mysql.pid
|
||||
@@ -40,7 +40,7 @@ max_connect_errors = 100
|
||||
open_files_limit = 2560
|
||||
max_allowed_packet = 128M
|
||||
|
||||
skip_name_resolve = 1
|
||||
skip_name_resolve=1
|
||||
#skip-networking
|
||||
#skip-external-locking
|
||||
#loose-skip-innodb
|
||||
@@ -71,6 +71,7 @@ log_replica_updates = 1
|
||||
# Prevent replication from starting automatically with MySQL
|
||||
#skip_replica_start = 1
|
||||
#replicate-do-db
|
||||
replica_skip_errors=1062
|
||||
replicate-ignore-db = information_schema
|
||||
replicate-ignore-db = performance_schema
|
||||
replicate-ignore-db = mysql
|
||||
|
||||
@@ -16,7 +16,7 @@
|
||||
<p onclick="masterOrSlaveConf($('.plugin_version').attr('version'))">主从配置</p>
|
||||
</div>
|
||||
<div class="bt-w-con pd15">
|
||||
<div class="soft-man-con" style="height: 530px; overflow: auto;"></div>
|
||||
<div class="soft-man-con"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
+93
-24
@@ -48,19 +48,20 @@ def getInitDFile():
|
||||
|
||||
def getArgs():
|
||||
args = sys.argv[2:]
|
||||
|
||||
tmp = {}
|
||||
args_len = len(args)
|
||||
|
||||
if args_len == 1:
|
||||
t = args[0].strip('{').strip('}')
|
||||
t = t.split(':')
|
||||
if t.strip() == '':
|
||||
tmp = []
|
||||
else:
|
||||
t = t.split(':', 1)
|
||||
tmp[t[0]] = t[1]
|
||||
tmp[t[0]] = t[1]
|
||||
elif args_len > 1:
|
||||
for i in range(len(args)):
|
||||
t = args[i].split(':')
|
||||
t = args[i].split(':', 1)
|
||||
tmp[t[0]] = t[1]
|
||||
|
||||
return tmp
|
||||
|
||||
|
||||
@@ -166,7 +167,6 @@ def pMysqlDb():
|
||||
|
||||
def makeInitRsaKey(version=''):
|
||||
datadir = getServerDir() + "/data"
|
||||
|
||||
mysql_pem = datadir + "/mysql.pem"
|
||||
if not os.path.exists(mysql_pem):
|
||||
rdata = mw.execShell(
|
||||
@@ -175,12 +175,11 @@ def makeInitRsaKey(version=''):
|
||||
rdata = mw.execShell(
|
||||
'cd ' + datadir + ' && openssl rsa -in mysql.pem -pubout -out mysql.pub')
|
||||
# print(rdata)
|
||||
|
||||
if not mw.isAppleSystem():
|
||||
mw.execShell('cd ' + datadir + ' && chmod 400 mysql.pem')
|
||||
mw.execShell('cd ' + datadir + ' && chmod 444 mysql.pub')
|
||||
mw.execShell('cd ' + datadir + ' && chown mysql:mysql mysql.pem')
|
||||
mw.execShell('cd ' + datadir + ' && chown mysql:mysql mysql.pub')
|
||||
if not mw.isAppleSystem():
|
||||
mw.execShell('cd ' + datadir + ' && chmod 400 mysql.pem')
|
||||
mw.execShell('cd ' + datadir + ' && chmod 444 mysql.pub')
|
||||
mw.execShell('cd ' + datadir + ' && chown mysql:mysql mysql.pem')
|
||||
mw.execShell('cd ' + datadir + ' && chown mysql:mysql mysql.pub')
|
||||
|
||||
|
||||
def initDreplace(version=''):
|
||||
@@ -250,6 +249,14 @@ def initDreplace(version=''):
|
||||
return file_bin
|
||||
|
||||
|
||||
def process_status():
|
||||
cmd = "ps -ef|grep mysql |grep -v grep | grep -v python | awk '{print $2}'"
|
||||
data = mw.execShell(cmd)
|
||||
if data[0] == '':
|
||||
return 'stop'
|
||||
return 'start'
|
||||
|
||||
|
||||
def status(version=''):
|
||||
path = getConf()
|
||||
if not os.path.exists(path):
|
||||
@@ -382,7 +389,7 @@ def initMysql57Data():
|
||||
myconf = serverdir + "/etc/my.cnf"
|
||||
user = pGetDbUser()
|
||||
cmd = 'cd ' + serverdir + ' && ./bin/mysqld --defaults-file=' + myconf + \
|
||||
' --initialize-insecure --explicit_defaults_for_timestamp'
|
||||
' --initialize-insecure --explicit_defaults_for_timestamp --user=mysql'
|
||||
data = mw.execShell(cmd)
|
||||
# print(data)
|
||||
return False
|
||||
@@ -449,7 +456,7 @@ def initMysqlPwd():
|
||||
|
||||
|
||||
def initMysql8Pwd():
|
||||
time.sleep(2)
|
||||
time.sleep(8)
|
||||
|
||||
serverdir = getServerDir()
|
||||
myconf = serverdir + "/etc/my.cnf"
|
||||
@@ -539,7 +546,12 @@ def my8cmd(version, method):
|
||||
else:
|
||||
mw.execShell('systemctl start mysql')
|
||||
|
||||
initMysql8Pwd()
|
||||
for x in range(10):
|
||||
mydb_status = process_status()
|
||||
if mydb_status == 'start':
|
||||
initMysql8Pwd()
|
||||
break
|
||||
time.sleep(1)
|
||||
|
||||
if mw.isAppleSystem():
|
||||
cmd_init_stop = init_file + ' stop'
|
||||
@@ -561,11 +573,11 @@ def my8cmd(version, method):
|
||||
|
||||
|
||||
def appCMD(version, action):
|
||||
makeInitRsaKey(version)
|
||||
if version == '8.0' or version == '5.7':
|
||||
status = my8cmd(version, action)
|
||||
else:
|
||||
status = myOp(version, action)
|
||||
makeInitRsaKey(version)
|
||||
return status
|
||||
|
||||
|
||||
@@ -842,12 +854,29 @@ def setDbBackup():
|
||||
return data[1]
|
||||
|
||||
scDir = mw.getRunDir() + '/scripts/backup.py'
|
||||
|
||||
cmd = 'python3 ' + scDir + ' database ' + args['name'] + ' 3'
|
||||
os.system(cmd)
|
||||
return mw.returnJson(True, 'ok')
|
||||
|
||||
|
||||
# 数据库密码处理
|
||||
def myPass(act, root):
|
||||
conf_file = getConf()
|
||||
mw.execShell("sed -i '/user=root/d' {}".format(conf_file))
|
||||
mw.execShell("sed -i '/password=/d' {}".format(conf_file))
|
||||
if act:
|
||||
mycnf = mw.readFile(conf_file)
|
||||
src_dump = "[mysqldump]\n"
|
||||
sub_dump = src_dump + "user=root\npassword=\"{}\"\n".format(root)
|
||||
if not mycnf:
|
||||
return False
|
||||
mycnf = mycnf.replace(src_dump, sub_dump)
|
||||
if len(mycnf) > 100:
|
||||
mw.writeFile(conf_file, mycnf)
|
||||
return True
|
||||
return True
|
||||
|
||||
|
||||
def importDbExternal():
|
||||
args = getArgs()
|
||||
data = checkArgs(args, ['file', 'name'])
|
||||
@@ -902,11 +931,16 @@ def importDbExternal():
|
||||
pwd = pSqliteDb('config').where('id=?', (1,)).getField('mysql_root')
|
||||
sock = getSocketFile()
|
||||
|
||||
os.environ["MYSQL_PWD"] = pwd
|
||||
mysql_cmd = getServerDir() + '/bin/mysql -S ' + sock + ' -uroot -p' + \
|
||||
pwd + ' ' + name + ' < ' + import_sql
|
||||
my_cnf = getConf()
|
||||
|
||||
myPass(True, pwd)
|
||||
mysql_cmd = getServerDir() + '/bin/mysql --defaults-file=' + my_cnf + \
|
||||
' -uroot -p\"' + pwd + '\" -f ' + name + ' < ' + import_sql
|
||||
|
||||
# print(mysql_cmd)
|
||||
rdata = mw.execShell(mysql_cmd)
|
||||
myPass(False, pwd)
|
||||
# print(rdata)
|
||||
if ext != 'sql':
|
||||
os.remove(import_sql)
|
||||
|
||||
@@ -2371,6 +2405,25 @@ def initSlaveStatus(version=''):
|
||||
return initSlaveStatusSyncUser(version)
|
||||
|
||||
|
||||
def makeSyncUsercmd(u, version=''):
|
||||
mode = recognizeDbMode()
|
||||
sql = ''
|
||||
|
||||
ip = u['ip']
|
||||
port = u['port']
|
||||
username = u['user']
|
||||
password = u['pass']
|
||||
if mode == "gtid":
|
||||
sql = "CHANGE MASTER TO MASTER_HOST='" + ip + "', MASTER_PORT=" + port + ", MASTER_USER='" + \
|
||||
username + "', MASTER_PASSWORD='" + \
|
||||
password + "', MASTER_AUTO_POSITION=1"
|
||||
if version == '8.0':
|
||||
sql = "CHANGE REPLICATION SOURCE TO SOURCE_HOST='" + ip + "', SOURCE_PORT=" + port + ", SOURCE_USER='" + \
|
||||
username + "', SOURCE_PASSWORD='" + \
|
||||
password + "', MASTER_AUTO_POSITION=1"
|
||||
return sql
|
||||
|
||||
|
||||
def initSlaveStatusSyncUser(version=''):
|
||||
conn = pSqliteDb('slave_sync_user')
|
||||
data = conn.field('ip,port,user,pass,mode,cmd').find()
|
||||
@@ -2393,8 +2446,19 @@ def initSlaveStatusSyncUser(version=''):
|
||||
if local_mode != mode_name:
|
||||
return mw.returnJson(False, '同步模式不一致!')
|
||||
|
||||
t = db.query(u['cmd'])
|
||||
# print(t)
|
||||
if u['cmd'] == '' and local_mode == 'gtid':
|
||||
sql = makeSyncUsercmd(u, version)
|
||||
# print(sql)
|
||||
t = db.query(sql)
|
||||
else:
|
||||
if u['cmd'] == '':
|
||||
return mw.returnJson(False, '经典模式下,必须手写同步命令!')
|
||||
# print(u['cmd'])
|
||||
t = db.query(u['cmd'])
|
||||
isError = isSqlError(t)
|
||||
if isError:
|
||||
return isError
|
||||
|
||||
db.query("start slave user='{}' password='{}';".format(
|
||||
u['user'], u['pass']))
|
||||
return mw.returnJson(True, '初始化成功!')
|
||||
@@ -2612,8 +2676,10 @@ def doFullSyncUser(version=''):
|
||||
writeDbSyncStatus({'code': 1, 'msg': '远程导出数据...', 'progress': 20})
|
||||
|
||||
if not os.path.exists(bak_file):
|
||||
dump_sql_data = getServerDir() + "/bin/mysqldump " + dmp_option + " --force --opt --default-character-set=utf8 --single-transaction -h" + ip + " -P" + \
|
||||
port + " -u" + user + " -p" + apass + " " + sync_db + " > " + bak_file
|
||||
dump_sql_data = getServerDir() + "/bin/mysqldump " + dmp_option + " --force --opt --default-character-set=utf8 --single-transaction -h" + ip + " -P" + \
|
||||
port + " -u" + user + " -p\"" + apass + \
|
||||
"\" --ssl-mode=DISABLED " + sync_db + " > " + bak_file
|
||||
# print(dump_sql_data)
|
||||
mw.execShell(dump_sql_data)
|
||||
|
||||
writeDbSyncStatus({'code': 2, 'msg': '本地导入数据...', 'progress': 40})
|
||||
@@ -2804,6 +2870,9 @@ def uninstallPreInspection(version):
|
||||
if mw.isDebugMode():
|
||||
return 'ok'
|
||||
|
||||
import plugins_api
|
||||
plugins_api.plugins_api().removeIndex(getPluginName(), version)
|
||||
|
||||
return "请手动删除MySQL[{}]<br/> rm -rf {}".format(version, getServerDir())
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
@@ -181,7 +181,7 @@ function changeMySQLDataPath(act) {
|
||||
$.post('/database?action=GetMySQLInfo', '', function(rdata) {
|
||||
var LimitCon = '<p class="conf_p">\
|
||||
<input id="datadir" class="phpUploadLimit bt-input-text mr5" style="width:350px;" type="text" value="' + rdata.datadir + '" name="datadir">\
|
||||
<span onclick="ChangePath(\'datadir\')" class="glyphicon glyphicon-folder-open cursor mr20" style="width:auto"></span><button class="btn btn-success btn-sm" onclick="changeMySQLDataPath(1)">' + lan.soft.mysql_to + '</button>\
|
||||
<span onclick="changePath(\'datadir\')" class="glyphicon glyphicon-folder-open cursor mr20" style="width:auto"></span><button class="btn btn-success btn-sm" onclick="changeMySQLDataPath(1)">' + lan.soft.mysql_to + '</button>\
|
||||
</p>';
|
||||
$(".soft-man-con").html(LimitCon);
|
||||
});
|
||||
@@ -1208,7 +1208,7 @@ function myLogs(){
|
||||
<span class="f14 c6 mr20">二进制日志 </span><span class="f14 c6 mr20">' + toSize(rdata.msg) + '</span>\
|
||||
'+line_status+'\
|
||||
<p class="f14 c6 mtb10" style="border-top:#ddd 1px solid; padding:10px 0">错误日志<button class="btn btn-default btn-clear btn-xs" style="float:right;" >清理日志</button></p>\
|
||||
<textarea readonly style="margin: 0px;width: 100%;height: 440px;background-color: #333;color:#fff; padding:0 5px" id="error_log"></textarea>\
|
||||
<textarea readonly style="margin: 0px;width: 100%;height: 438px;background-color: #333;color:#fff; padding:0 5px" id="error_log"></textarea>\
|
||||
</p>';
|
||||
$(".soft-man-con").html(limitCon);
|
||||
|
||||
|
||||
@@ -86,6 +86,7 @@ Install_mysql()
|
||||
make -j${cpuCore} && make install && make clean
|
||||
|
||||
if [ -d $serverPath/mysql ];then
|
||||
rm -rf ${mysqlDir}/mysql-${VERSION}
|
||||
echo '5.5' > $serverPath/mysql/version.pl
|
||||
echo '安装完成' > $install_tmp
|
||||
else
|
||||
|
||||
@@ -105,6 +105,7 @@ Install_mysql()
|
||||
|
||||
|
||||
if [ -d $serverPath/mysql ];then
|
||||
rm -rf ${mysqlDir}/mysql-${VERSION}
|
||||
echo '5.6' > $serverPath/mysql/version.pl
|
||||
echo '安装完成' > $install_tmp
|
||||
else
|
||||
|
||||
@@ -118,6 +118,7 @@ Install_mysql()
|
||||
make -j${cpuCore} && make install && make clean
|
||||
|
||||
if [ -d $serverPath/mysql ];then
|
||||
rm -rf ${mysqlDir}/mysql-${VERSION}
|
||||
echo '5.7' > $serverPath/mysql/version.pl
|
||||
echo '安装完成' > $install_tmp
|
||||
else
|
||||
|
||||
@@ -210,6 +210,7 @@ Install_mysql()
|
||||
make -j${cpuCore} && make install && make clean
|
||||
|
||||
if [ -d $serverPath/mysql ];then
|
||||
rm -rf ${mysqlDir}/mysql-${VERSION}
|
||||
echo '8.0' > $serverPath/mysql/version.pl
|
||||
echo '安装完成' > $install_tmp
|
||||
else
|
||||
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 878 B |
Executable
+26
@@ -0,0 +1,26 @@
|
||||
<div class="bt-form">
|
||||
<div class="bt-w-main">
|
||||
<div class='plugin_version'></div>
|
||||
<div class="bt-w-menu">
|
||||
<p class="bgw" onclick="pluginService('nezha',$('.plugin_version').attr('version'));">面板服务</p>
|
||||
<p onclick="pluginInitD('nezha',$('.plugin_version').attr('version'));">面板自启动</p>
|
||||
<p onclick="nezha.cfg($('.plugin_version').attr('version'));">面板配置</p>
|
||||
<p onclick="pluginConfig('nezha', $('.plugin_version').attr('version'), 'conf');">面板配置文件</p>
|
||||
<p onclick="pluginService('nezha', $('.plugin_version').attr('version'), 'agent');">AGENT服务</p>
|
||||
<p onclick="pluginInitD('nezha', $('.plugin_version').attr('version'), 'agent');">AGENT自启动</p>
|
||||
<p onclick="nezha.agent_cfg($('.plugin_version').attr('version'));">AGENT配置</p>
|
||||
<p onclick="nezha.readme();">说明</p>
|
||||
</div>
|
||||
<div class="bt-w-con pd15">
|
||||
<div class="soft-man-con"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script type="text/javascript">
|
||||
resetPluginWinHeight(530);
|
||||
$.getScript( "/plugins/file?name=nezha&f=js/nezha.js", function(){
|
||||
nezha.init();
|
||||
pluginService('nezha');
|
||||
});
|
||||
</script>
|
||||
Executable
+364
@@ -0,0 +1,364 @@
|
||||
# coding:utf-8
|
||||
|
||||
import sys
|
||||
import io
|
||||
import os
|
||||
import time
|
||||
import re
|
||||
import socket
|
||||
import json
|
||||
|
||||
from datetime import datetime
|
||||
|
||||
sys.path.append(os.getcwd() + "/class/core")
|
||||
import mw
|
||||
|
||||
app_debug = False
|
||||
if mw.isAppleSystem():
|
||||
app_debug = True
|
||||
|
||||
|
||||
class App:
|
||||
__setupPath = '/www/server/nezha'
|
||||
__cfg = ''
|
||||
__agent_cfg = ''
|
||||
|
||||
def __init__(self):
|
||||
self.__setupPath = self.getServerDir()
|
||||
self.__cfg = self.__setupPath + '/nezha.cfg'
|
||||
self.__agent_cfg = self.__setupPath + '/agent.cfg'
|
||||
|
||||
def getArgs(self):
|
||||
args = sys.argv[3:]
|
||||
tmp = {}
|
||||
args_len = len(args)
|
||||
|
||||
if args_len == 1:
|
||||
t = args[0].strip('{').strip('}')
|
||||
t = t.split(':', 1)
|
||||
tmp[t[0]] = t[1]
|
||||
elif args_len > 1:
|
||||
for i in range(len(args)):
|
||||
t = args[i].split(':', 1)
|
||||
tmp[t[0]] = t[1]
|
||||
|
||||
return tmp
|
||||
|
||||
def checkArgs(self, data, ck=[]):
|
||||
for i in range(len(ck)):
|
||||
if not ck[i] in data:
|
||||
return (False, mw.returnJson(False, '参数:(' + ck[i] + ')没有!'))
|
||||
return (True, mw.returnJson(True, 'ok'))
|
||||
|
||||
def __release_port(self, port):
|
||||
from collections import namedtuple
|
||||
try:
|
||||
import firewall_api
|
||||
firewall_api.firewall_api().addAcceptPortArgs(port, 'nezha', 'port')
|
||||
return port
|
||||
except Exception as e:
|
||||
return "Release failed {}".format(e)
|
||||
|
||||
def openPort(self):
|
||||
for i in ["9527", "5555"]:
|
||||
self.__release_port(i)
|
||||
return True
|
||||
|
||||
def getPluginName(self):
|
||||
return 'nezha'
|
||||
|
||||
def getPluginDir(self):
|
||||
return mw.getPluginDir() + '/' + self.getPluginName()
|
||||
|
||||
def getServerDir(self):
|
||||
return mw.getServerDir() + '/' + self.getPluginName()
|
||||
|
||||
def getInitdConfTpl(self):
|
||||
path = self.getPluginDir() + "/init.d/nezha.tpl"
|
||||
return path
|
||||
|
||||
def getInitdAgentConfTpl(self):
|
||||
path = self.getPluginDir() + "/init.d/nezha-agent.tpl"
|
||||
return path
|
||||
|
||||
def getHomeDir(self):
|
||||
if mw.isAppleSystem():
|
||||
user = mw.execShell(
|
||||
"who | sed -n '2, 1p' |awk '{print $1}'")[0].strip()
|
||||
return '/Users/' + user
|
||||
else:
|
||||
return '/root'
|
||||
|
||||
def getRunUser(self):
|
||||
if mw.isAppleSystem():
|
||||
user = mw.execShell(
|
||||
"who | sed -n '2, 1p' |awk '{print $1}'")[0].strip()
|
||||
return user
|
||||
else:
|
||||
return 'root'
|
||||
|
||||
def status(self):
|
||||
cmd = "ps -ef|grep " + self.getPluginName() + \
|
||||
" |grep -v grep | grep -v nezha-agent | grep -v python | awk '{print $2}'"
|
||||
data = mw.execShell(cmd)
|
||||
if data[0] == '':
|
||||
return "stop"
|
||||
return 'start'
|
||||
|
||||
def status_agent(self):
|
||||
cmd = "ps -ef | grep nezha-agent | grep -v grep | grep -v python | awk '{print $2}'"
|
||||
data = mw.execShell(cmd)
|
||||
if data[0] == '':
|
||||
return 'stop'
|
||||
return 'start'
|
||||
|
||||
def contentReplace(self, content):
|
||||
|
||||
service_path = mw.getServerDir()
|
||||
content = content.replace('{$ROOT_PATH}', mw.getRootDir())
|
||||
content = content.replace('{$SERVER_PATH}', service_path)
|
||||
content = content.replace('{$RUN_USER}', self.getRunUser())
|
||||
content = content.replace('{$HOME_DIR}', self.getHomeDir())
|
||||
|
||||
return content
|
||||
|
||||
def initDreplace(self):
|
||||
|
||||
file_tpl = self.getInitdConfTpl()
|
||||
service_path = mw.getServerDir()
|
||||
|
||||
initD_path = self.getServerDir() + '/init.d'
|
||||
if not os.path.exists(initD_path):
|
||||
os.mkdir(initD_path)
|
||||
self.openPort()
|
||||
|
||||
file_bin = initD_path + '/' + self.getPluginName()
|
||||
|
||||
if not os.path.exists(file_bin):
|
||||
content = mw.readFile(file_tpl)
|
||||
content = self.contentReplace(content)
|
||||
mw.writeFile(file_bin, content)
|
||||
mw.execShell('chmod +x ' + file_bin)
|
||||
|
||||
# systemd
|
||||
systemDir = mw.systemdCfgDir()
|
||||
systemService = systemDir + '/nezha.service'
|
||||
systemServiceTpl = self.getPluginDir() + '/init.d/nezha.service.tpl'
|
||||
if os.path.exists(systemDir) and not os.path.exists(systemService):
|
||||
service_path = mw.getServerDir()
|
||||
se_content = mw.readFile(systemServiceTpl)
|
||||
se_content = self.contentReplace(se_content)
|
||||
mw.writeFile(systemService, se_content)
|
||||
mw.execShell('systemctl daemon-reload')
|
||||
|
||||
return file_bin
|
||||
|
||||
def contentAgentReplace(self, content):
|
||||
path = self.__agent_cfg
|
||||
if os.path.exists(path):
|
||||
data = self.get_agent_cfg()
|
||||
content = content.replace('{$APP_HOST}', data['host'])
|
||||
content = content.replace('{$APP_SECRET}', data['secret'])
|
||||
|
||||
return content
|
||||
|
||||
def initDAgent(self):
|
||||
file_tpl = self.getInitdAgentConfTpl()
|
||||
|
||||
initD_path = self.getServerDir() + '/init.d'
|
||||
if not os.path.exists(initD_path):
|
||||
os.mkdir(initD_path)
|
||||
|
||||
file_agent_bin = initD_path + '/nezha-agent'
|
||||
|
||||
content = mw.readFile(file_tpl)
|
||||
content = self.contentReplace(content)
|
||||
content = self.contentAgentReplace(content)
|
||||
mw.writeFile(file_agent_bin, content)
|
||||
mw.execShell('chmod +x ' + file_agent_bin)
|
||||
|
||||
# systemd
|
||||
sysDir = mw.systemdCfgDir()
|
||||
sysService = sysDir + '/nezha-agent.service'
|
||||
sysServiceTpl = self.getPluginDir() + '/init.d/nezha-agent.service.tpl'
|
||||
service_path = mw.getServerDir()
|
||||
content = mw.readFile(sysServiceTpl)
|
||||
content = self.contentReplace(content)
|
||||
content = self.contentAgentReplace(content)
|
||||
mw.writeFile(sysService, content)
|
||||
mw.execShell('systemctl daemon-reload')
|
||||
|
||||
return file_agent_bin
|
||||
|
||||
def init_cfg(self):
|
||||
self.initDreplace()
|
||||
self.initDAgent()
|
||||
|
||||
def imOp(self, method):
|
||||
|
||||
file = self.initDreplace()
|
||||
|
||||
if not mw.isAppleSystem():
|
||||
cmd = 'systemctl {} {}'.format(method, self.getPluginName())
|
||||
data = mw.execShell(cmd)
|
||||
if data[1] == '':
|
||||
return 'ok'
|
||||
return 'fail'
|
||||
|
||||
data = mw.execShell(file + ' ' + method)
|
||||
if data[1] == '':
|
||||
return 'ok'
|
||||
return data[0]
|
||||
|
||||
def start(self):
|
||||
return self.imOp('start')
|
||||
|
||||
def stop(self):
|
||||
return self.imOp('stop')
|
||||
|
||||
def restart(self):
|
||||
return self.imOp('restart')
|
||||
|
||||
def reload(self):
|
||||
return self.imOp('reload')
|
||||
|
||||
def agOp(self, method):
|
||||
file = self.initDAgent()
|
||||
|
||||
path = self.__agent_cfg
|
||||
if not os.path.exists(path):
|
||||
return '请先设置Agent配置!'
|
||||
|
||||
if not mw.isAppleSystem():
|
||||
cmd = 'systemctl {} {}'.format(method, 'nezha-agent')
|
||||
data = mw.execShell(cmd)
|
||||
if data[1] == '':
|
||||
return 'ok'
|
||||
return 'fail'
|
||||
|
||||
data = mw.execShell(file + ' ' + method)
|
||||
if data[1] == '':
|
||||
return 'ok'
|
||||
return data[0]
|
||||
|
||||
def start_agent(self):
|
||||
return self.agOp('start')
|
||||
|
||||
def stop_agent(self):
|
||||
return self.agOp('stop')
|
||||
|
||||
def restart_agent(self):
|
||||
return self.agOp('restart')
|
||||
|
||||
def reload_agent(self):
|
||||
return self.agentOp('reload')
|
||||
|
||||
def initd_status(self):
|
||||
cmd = 'systemctl status nezha | grep loaded | grep "enabled;"'
|
||||
data = mw.execShell(cmd)
|
||||
if data[0] == '':
|
||||
return 'fail'
|
||||
return 'ok'
|
||||
|
||||
def initd_install(self):
|
||||
mw.execShell('systemctl enable nezha')
|
||||
return 'ok'
|
||||
|
||||
def initd_uninstall(self):
|
||||
mw.execShell('systemctl disable nezha')
|
||||
return 'ok'
|
||||
|
||||
def initd_status_agent(self):
|
||||
cmd = 'systemctl status nezha-agent | grep loaded | grep "enabled;"'
|
||||
data = mw.execShell(cmd)
|
||||
if data[0] == '':
|
||||
return 'fail'
|
||||
return 'ok'
|
||||
|
||||
def initd_install_agent(self):
|
||||
mw.execShell('systemctl enable nezha-agent')
|
||||
return 'ok'
|
||||
|
||||
def initd_uninstall_agent(self):
|
||||
mw.execShell('systemctl disable nezha-agent')
|
||||
return 'ok'
|
||||
|
||||
def conf(self):
|
||||
return self.getServerDir() + '/dashboard/data/config.yaml'
|
||||
|
||||
def nezha_cfg(self):
|
||||
path = self.__cfg
|
||||
if not os.path.exists(path):
|
||||
d = {}
|
||||
cmd_un = 'cd ' + self.getServerDir() + '/dashboard && ./nezha conf -u ""'
|
||||
mw.execShell(cmd_un)
|
||||
td = mw.execShell(cmd_un)
|
||||
d['username'] = td[0].strip()
|
||||
|
||||
pwd = mw.getRandomString(16)
|
||||
cmd_pwd = 'cd ' + self.getServerDir() + '/dashboard && ./nezha conf -u "" -p ' + pwd
|
||||
td = mw.execShell(cmd_pwd)
|
||||
d['password'] = pwd
|
||||
|
||||
mw.writeFile(path, mw.enDoubleCrypt('nezha', mw.getJson(d)))
|
||||
|
||||
info = mw.readFile(path)
|
||||
info = mw.deDoubleCrypt('nezha', info)
|
||||
|
||||
info = json.loads(info)
|
||||
return mw.returnJson(True, 'ok', info)
|
||||
|
||||
def nezha_save_cfg(self):
|
||||
args = self.getArgs()
|
||||
data = self.checkArgs(args, ['username', 'password'])
|
||||
if not data[0]:
|
||||
return data[1]
|
||||
path = self.__cfg
|
||||
|
||||
cmd = 'cd ' + self.getServerDir() + '/dashboard && ./nezha conf -su "' + \
|
||||
args['username'] + '" -p ' + args['password']
|
||||
# print(cmd)
|
||||
t = mw.execShell(cmd)
|
||||
# print(t)
|
||||
mw.writeFile(path, mw.enDoubleCrypt('nezha', mw.getJson(args)))
|
||||
return mw.returnJson(True, '修改成功!')
|
||||
|
||||
def get_agent_cfg(self):
|
||||
path = self.__agent_cfg
|
||||
info = mw.readFile(path)
|
||||
info = mw.deDoubleCrypt('agent', info)
|
||||
info = json.loads(info)
|
||||
return info
|
||||
|
||||
def agent_cfg(self):
|
||||
path = self.__agent_cfg
|
||||
if not os.path.exists(path):
|
||||
d = {}
|
||||
d['host'] = '127.0.0.1:5555'
|
||||
d['secret'] = 'secret'
|
||||
mw.writeFile(path, mw.enDoubleCrypt('agent', mw.getJson(d)))
|
||||
|
||||
info = self.get_agent_cfg()
|
||||
return mw.returnJson(True, 'ok', info)
|
||||
|
||||
def agent_save_cfg(self):
|
||||
args = self.getArgs()
|
||||
data = self.checkArgs(args, ['host', 'secret'])
|
||||
if not data[0]:
|
||||
return data[1]
|
||||
path = self.__agent_cfg
|
||||
mw.writeFile(path, mw.enDoubleCrypt('agent', mw.getJson(args)))
|
||||
return mw.returnJson(True, '修改成功!')
|
||||
|
||||
def run_log(self):
|
||||
return self.getServerDir() + '/logs/nezha.log'
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
func = sys.argv[1]
|
||||
classApp = App()
|
||||
try:
|
||||
data = eval("classApp." + func + "()")
|
||||
print(data)
|
||||
except Exception as e:
|
||||
print('error:' + str(e))
|
||||
Executable
+18
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"sort": 7,
|
||||
"ps": "哪吒监控面板/改造版",
|
||||
"name": "nezha",
|
||||
"title": "哪吒监控",
|
||||
"shell": "install.sh",
|
||||
"versions":["0.15.2"],
|
||||
"tip": "soft",
|
||||
"checks": "server/nezha",
|
||||
"path": "server/nezha",
|
||||
"display": 1,
|
||||
"author": "midoks",
|
||||
"date": "2023-06-26",
|
||||
"home": "https://github.com/midoks/nezha",
|
||||
"doc1": "https://nezha.wiki/",
|
||||
"type": 0,
|
||||
"pid": "5"
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
[Unit]
|
||||
Description=Nezha Agent
|
||||
After=syslog.target
|
||||
#After=network.target
|
||||
#After=nezha-dashboard.service
|
||||
|
||||
[Service]
|
||||
# Modify these two values and uncomment them if you have
|
||||
# repos with lots of files and get an HTTP error 500 because
|
||||
# of that
|
||||
###
|
||||
#LimitMEMLOCK=infinity
|
||||
#LimitNOFILE=65535
|
||||
Type=simple
|
||||
User=root
|
||||
Group=root
|
||||
WorkingDirectory={$SERVER_PATH}/nezha/agent/
|
||||
ExecStart={$SERVER_PATH}/nezha/agent/nezha-agent -s {$APP_HOST} -p {$APP_SECRET}
|
||||
Restart=always
|
||||
#Environment=DEBUG=true
|
||||
|
||||
# Some distributions may not support these hardening directives. If you cannot start the service due
|
||||
# to an unknown option, comment out the ones not supported by your version of systemd.
|
||||
#ProtectSystem=full
|
||||
#PrivateDevices=yes
|
||||
#PrivateTmp=yes
|
||||
#NoNewPrivileges=true
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
@@ -0,0 +1,90 @@
|
||||
#!/bin/bash
|
||||
# chkconfig: 2345 55 25
|
||||
# description: Nezha Agent Service
|
||||
|
||||
### BEGIN INIT INFO
|
||||
# Provides: Nezha Agent
|
||||
# Required-Start: $all
|
||||
# Required-Stop: $all
|
||||
# Default-Start: 2 3 4 5
|
||||
# Default-Stop: 0 1 6
|
||||
# Short-Description: starts Nezha Agent
|
||||
# Description: starts the Nezha Agent
|
||||
### END INIT INFO
|
||||
|
||||
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
|
||||
|
||||
if [ -f /etc/init.d/functions ];then
|
||||
. /etc/init.d/functions
|
||||
fi
|
||||
|
||||
if [ -f /etc/rc.d/init.d/functions ];then
|
||||
. /etc/rc.d/init.d/functions
|
||||
fi
|
||||
|
||||
SERVICENAME=nezha-agent
|
||||
LOG_PATH={$SERVER_PATH}/nezha/logs
|
||||
APP_PATH={$SERVER_PATH}/nezha/agent
|
||||
APP_HOST={$APP_HOST}
|
||||
APP_SECRET={$APP_SECRET}
|
||||
|
||||
|
||||
app_start(){
|
||||
isStart=`ps -ef|grep "${SERVICENAME}" | grep -v grep | grep -v python | grep -v "/bin/bash" | awk '{print $2}'`
|
||||
if [ "$isStart" == '' ];then
|
||||
echo -e "Starting ${SERVICENAME}... \c"
|
||||
cd $APP_PATH
|
||||
exec nohup ${APP_PATH}/${SERVICENAME} -d -s ${APP_HOST} -p ${APP_SECRET} >> ${LOG_PATH}/${SERVICENAME}.log 2>&1 &
|
||||
isStart=""
|
||||
while [[ "$isStart" == "" ]];
|
||||
do
|
||||
echo -e ".\c"
|
||||
sleep 0.5
|
||||
isStart=$(ps -ef|grep "${SERVICENAME}" |grep -v grep | grep -v python | grep -v "/bin/bash" | awk '{print $2}')
|
||||
let n+=1
|
||||
if [ $n -gt 15 ];then
|
||||
break;
|
||||
fi
|
||||
done
|
||||
if [ "$isStart" == '' ];then
|
||||
echo -e "\033[31mError: ${SERVICENAME} service startup failed.\033[0m"
|
||||
return;
|
||||
fi
|
||||
echo -e "\033[32mdone\033[0m"
|
||||
else
|
||||
echo "Starting ${SERVICENAME}(pid $(echo $isStart)) already running"
|
||||
fi
|
||||
}
|
||||
|
||||
app_stop(){
|
||||
pids=`ps -ef | grep "${SERVICENAME}" | grep -v grep | grep -v python | grep -v "/bin/bash" |awk '{print $2}'`
|
||||
arr=($pids)
|
||||
echo -e "Stopping ${SERVICENAME}... \c"
|
||||
for p in ${arr[@]}
|
||||
do
|
||||
# echo "$p"
|
||||
kill -9 $p
|
||||
done
|
||||
echo -e "\033[32mdone\033[0m"
|
||||
}
|
||||
|
||||
app_status(){
|
||||
isStart=`ps -ef | grep "${SERVICENAME}" | grep -v grep | grep -v python | grep -v "/bin/bash" | awk '{print $2}'`
|
||||
if [ "$isStart" == '' ];then
|
||||
echo -e "${SERVICENAME} not running"
|
||||
else
|
||||
echo -e "${SERVICENAME}(pid $(echo $isStart)) already running"
|
||||
fi
|
||||
}
|
||||
|
||||
case "$1" in
|
||||
'start') app_start;;
|
||||
'stop') app_stop;;
|
||||
'status') app_status;;
|
||||
'reload')
|
||||
app_stop
|
||||
app_start;;
|
||||
'restart')
|
||||
app_stop
|
||||
app_start;;
|
||||
esac
|
||||
@@ -0,0 +1,30 @@
|
||||
[Unit]
|
||||
Description=Nezha Dashborad
|
||||
After=syslog.target
|
||||
After=network.target
|
||||
After=mariadb.service mysqld.service postgresql.service memcached.service redis.service
|
||||
|
||||
[Service]
|
||||
# Modify these two values and uncomment them if you have
|
||||
# repos with lots of files and get an HTTP error 500 because
|
||||
# of that
|
||||
###
|
||||
#LimitMEMLOCK=infinity
|
||||
#LimitNOFILE=65535
|
||||
Type=simple
|
||||
#User=root
|
||||
#Group=root
|
||||
WorkingDirectory={$SERVER_PATH}/nezha/dashboard/
|
||||
ExecStart={$SERVER_PATH}/nezha/dashboard/nezha web
|
||||
Restart=always
|
||||
#Environment=DEBUG=true
|
||||
|
||||
# Some distributions may not support these hardening directives. If you cannot start the service due
|
||||
# to an unknown option, comment out the ones not supported by your version of systemd.
|
||||
ProtectSystem=full
|
||||
PrivateDevices=yes
|
||||
PrivateTmp=yes
|
||||
NoNewPrivileges=true
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
@@ -0,0 +1,87 @@
|
||||
#!/bin/bash
|
||||
# chkconfig: 2345 55 25
|
||||
# description: Nezha Service
|
||||
|
||||
### BEGIN INIT INFO
|
||||
# Provides: Nezha
|
||||
# Required-Start: $all
|
||||
# Required-Stop: $all
|
||||
# Default-Start: 2 3 4 5
|
||||
# Default-Stop: 0 1 6
|
||||
# Short-Description: starts Nezha
|
||||
# Description: starts the Nezha
|
||||
### END INIT INFO
|
||||
|
||||
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
|
||||
|
||||
if [ -f /etc/init.d/functions ];then
|
||||
. /etc/init.d/functions
|
||||
fi
|
||||
|
||||
if [ -f /etc/rc.d/init.d/functions ];then
|
||||
. /etc/rc.d/init.d/functions
|
||||
fi
|
||||
|
||||
SERVICENAME=nezha
|
||||
LOG_PATH={$SERVER_PATH}/${SERVICENAME}/logs
|
||||
APP_PATH={$SERVER_PATH}/${SERVICENAME}/dashboard
|
||||
|
||||
|
||||
app_start(){
|
||||
isStart=`ps -ef|grep "${SERVICENAME} web" | grep -v grep | grep -v python | grep -v "/bin/bash" | awk '{print $2}'`
|
||||
if [ "$isStart" == '' ];then
|
||||
echo -e "Starting ${SERVICENAME}... \c"
|
||||
cd $APP_PATH && exec nohup ${APP_PATH}/${SERVICENAME} web >> ${LOG_PATH}/${SERVICENAME}.log 2>&1 &
|
||||
isStart=""
|
||||
while [[ "$isStart" == "" ]];
|
||||
do
|
||||
echo -e ".\c"
|
||||
sleep 0.5
|
||||
isStart=$(ps -ef|grep "${SERVICENAME} web" |grep -v grep | grep -v python | grep -v "/bin/bash" | awk '{print $2}')
|
||||
let n+=1
|
||||
if [ $n -gt 15 ];then
|
||||
break;
|
||||
fi
|
||||
done
|
||||
if [ "$isStart" == '' ];then
|
||||
echo -e "\033[31mError: ${SERVICENAME} service startup failed.\033[0m"
|
||||
return;
|
||||
fi
|
||||
echo -e "\033[32mdone\033[0m"
|
||||
else
|
||||
echo "Starting ${SERVICENAME}(pid $(echo $isStart)) already running"
|
||||
fi
|
||||
}
|
||||
|
||||
app_stop(){
|
||||
pids=`ps -ef | grep "${SERVICENAME} web" | grep -v grep | grep -v python | grep -v "/bin/bash" |awk '{print $2}'`
|
||||
arr=($pids)
|
||||
echo -e "Stopping ${SERVICENAME}... \c"
|
||||
for p in ${arr[@]}
|
||||
do
|
||||
# echo "$p"
|
||||
kill -9 $p
|
||||
done
|
||||
echo -e "\033[32mdone\033[0m"
|
||||
}
|
||||
|
||||
app_status(){
|
||||
isStart=`ps -ef | grep "${SERVICENAME} web" | grep -v grep | grep -v python | grep -v "/bin/bash" | awk '{print $2}'`
|
||||
if [ "$isStart" == '' ];then
|
||||
echo -e "${SERVICENAME} not running"
|
||||
else
|
||||
echo -e "${SERVICENAME}(pid $(echo $isStart)) already running"
|
||||
fi
|
||||
}
|
||||
|
||||
case "$1" in
|
||||
'start') app_start;;
|
||||
'stop') app_stop;;
|
||||
'status') app_status;;
|
||||
'reload')
|
||||
app_stop
|
||||
app_start;;
|
||||
'restart')
|
||||
app_stop
|
||||
app_start;;
|
||||
esac
|
||||
Executable
+43
@@ -0,0 +1,43 @@
|
||||
#!/bin/bash
|
||||
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
|
||||
export PATH
|
||||
|
||||
curPath=`pwd`
|
||||
rootPath=$(dirname "$curPath")
|
||||
rootPath=$(dirname "$rootPath")
|
||||
serverPath=$(dirname "$rootPath")
|
||||
|
||||
install_tmp=${rootPath}/tmp/mw_install.pl
|
||||
|
||||
action=$1
|
||||
type=$2
|
||||
|
||||
if [ "${2}" == "" ];then
|
||||
echo '缺少安装脚本...' > $install_tmp
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [ ! -d $curPath/versions/$2 ];then
|
||||
echo '缺少安装脚本2...' > $install_tmp
|
||||
exit 0
|
||||
fi
|
||||
|
||||
sh -x $curPath/versions/$2/install.sh $1
|
||||
|
||||
if [ "${action}" == "uninstall" ];then
|
||||
if [ -f /usr/lib/systemd/system/nezha.service ] || [ -f /lib/systemd/system/nezha.service ] ;then
|
||||
systemctl stop nezha
|
||||
systemctl disable nezha
|
||||
rm -rf /usr/lib/systemd/system/nezha.service
|
||||
rm -rf /lib/systemd/system/nezha.service
|
||||
systemctl daemon-reload
|
||||
fi
|
||||
|
||||
if [ -f /usr/lib/systemd/system/nezha-agent.service ] || [ -f /lib/systemd/system/nezha-agent.service ] ;then
|
||||
systemctl stop nezha-agent
|
||||
systemctl disable nezha-agent
|
||||
rm -rf /usr/lib/systemd/system/nezha-agent.service
|
||||
rm -rf /lib/systemd/system/nezha-agent.service
|
||||
systemctl daemon-reload
|
||||
fi
|
||||
fi
|
||||
Executable
+184
@@ -0,0 +1,184 @@
|
||||
var nezha = {
|
||||
plugin_name: 'nezha',
|
||||
init: function () {
|
||||
var _this = this;
|
||||
},
|
||||
|
||||
send:function(info){
|
||||
var tips = info['tips'];
|
||||
var method = info['method'];
|
||||
var args = info['data'];
|
||||
var callback = info['success'];
|
||||
|
||||
var loadT = layer.msg(tips, { icon: 16, time: 0, shade: 0.3 });
|
||||
|
||||
var data = {};
|
||||
data['name'] = 'nezha';
|
||||
data['func'] = method;
|
||||
data['version'] = $('.plugin_version').attr('version');
|
||||
|
||||
if (typeof(args) == 'string'){
|
||||
data['args'] = JSON.stringify(toArrayObject(args));
|
||||
} else {
|
||||
data['args'] = JSON.stringify(args);
|
||||
}
|
||||
|
||||
$.post('/plugins/run', data, function(res) {
|
||||
layer.close(loadT);
|
||||
if (!res.status){
|
||||
layer.msg(res.msg,{icon:2,time:10000});
|
||||
return;
|
||||
}
|
||||
|
||||
var ret_data = $.parseJSON(res.data);
|
||||
console.log("send:",ret_data);
|
||||
// if (!ret_data.status){
|
||||
// layer.msg(ret_data.msg,{icon:2,time:2000});
|
||||
// return;
|
||||
// }
|
||||
|
||||
// console.log("send2:",ret_data);
|
||||
|
||||
if(typeof(callback) == 'function'){
|
||||
callback(ret_data);
|
||||
}
|
||||
},'json');
|
||||
},
|
||||
|
||||
postCallback:function(info){
|
||||
var tips = info['tips'];
|
||||
var method = info['method'];
|
||||
var args = info['data'];
|
||||
var callback = info['success'];
|
||||
|
||||
var loadT = layer.msg(tips, { icon: 16, time: 0, shade: 0.3 });
|
||||
|
||||
var data = {};
|
||||
data['name'] = 'nezha';
|
||||
data['func'] = method;
|
||||
data['version'] = $('.plugin_version').attr('version');
|
||||
|
||||
if (typeof(args) == 'string'){
|
||||
data['args'] = JSON.stringify(toArrayObject(args));
|
||||
} else {
|
||||
data['args'] = JSON.stringify(args);
|
||||
}
|
||||
|
||||
$.post('/plugins/callback', data, function(res) {
|
||||
layer.close(loadT);
|
||||
if (!res.status){
|
||||
layer.msg(res.msg,{icon:2,time:10000});
|
||||
return;
|
||||
}
|
||||
|
||||
var ret_data = $.parseJSON(res.data);
|
||||
if (!ret_data.status){
|
||||
layer.msg(ret_data.msg,{icon:2,time:2000});
|
||||
return;
|
||||
}
|
||||
|
||||
if(typeof(callback) == 'function'){
|
||||
callback(res);
|
||||
}
|
||||
},'json');
|
||||
},
|
||||
|
||||
repeatPwd:function (a, id) {
|
||||
$("#"+id).val(randomStrPwd(a))
|
||||
},
|
||||
|
||||
save_cfg:function(version){
|
||||
var username = $("input[name='username']").val();
|
||||
var password = $("input[name='password']").val();
|
||||
|
||||
this.send({
|
||||
tips:'正在设置中...',
|
||||
data:{'username':username,'password':password},
|
||||
method:'nezha_save_cfg',
|
||||
success:function(rdata){
|
||||
layer.msg(rdata.msg, { icon: rdata.status ? 1 : 2 });
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
cfg:function(version){
|
||||
this.send({
|
||||
tips:'正在获取中...',
|
||||
method:'nezha_cfg',
|
||||
success:function(data){
|
||||
var d = data.data;
|
||||
var value = '<p>\
|
||||
<span>用户名</span>\
|
||||
<input id="nz_username" style="width: 160px;" class="bt-input-text mr5" name="username" value="'+d['username']+'" type="text">\
|
||||
,<span title="随机用户名" class="glyphicon glyphicon-repeat cursor" onclick="nezha.repeatPwd(8,\'nz_username\')"></span>\
|
||||
</p>';
|
||||
|
||||
value += '<p>\
|
||||
<span>密码</span>\
|
||||
<input id="nz_password" style="width: 160px;" class="bt-input-text mr5" name="password" value="'+d['password']+'" type="text">\
|
||||
,<span title="随机密码" class="glyphicon glyphicon-repeat cursor" onclick="nezha.repeatPwd(16,\'nz_password\')"></span>\
|
||||
</p>';
|
||||
var conf = '<style>.conf_p p{margin-bottom: 2px;} .conf_p span {width: 50px;}</style><div class="conf_p" style="margin-bottom:0">\
|
||||
' + value + '\
|
||||
<div style="margin-top:10px; padding-right:15px" class="text-right">\
|
||||
<button class="btn btn-success btn-sm mr5" onclick="nezha.cfg(\'' + version + '\')">刷新</button>\
|
||||
<button class="btn btn-success btn-sm" onclick="nezha.save_cfg(\'' + version + '\')">保存</button>\
|
||||
</div>\
|
||||
</div>'
|
||||
$(".soft-man-con").html(conf);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
agent_save_cfg:function(version){
|
||||
var host = $("input[name='host']").val();
|
||||
var secret = $("input[name='secret']").val();
|
||||
|
||||
this.send({
|
||||
tips:'正在设置中...',
|
||||
data:{'host':host,'secret':secret},
|
||||
method:'agent_save_cfg',
|
||||
success:function(rdata){
|
||||
layer.msg(rdata.msg, { icon: rdata.status ? 1 : 2 });
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
agent_cfg:function(version){
|
||||
this.send({
|
||||
tips:'正在获取中...',
|
||||
method:'agent_cfg',
|
||||
success:function(data){
|
||||
var d = data.data;
|
||||
var value = '<p>\
|
||||
<span>地址</span>\
|
||||
<input id="nz_username" style="width: 160px;" class="bt-input-text mr5" name="host" value="'+d['host']+'" type="text">\
|
||||
,<font>如1.1.1.1:5444</font>\
|
||||
</p>';
|
||||
|
||||
value += '<p>\
|
||||
<span>密钥</span>\
|
||||
<input id="nz_password" style="width: 160px;" class="bt-input-text mr5" name="secret" value="'+d['secret']+'" type="text">\
|
||||
,<font>密钥</font>\
|
||||
</p>';
|
||||
var conf = '<style>.conf_p p{margin-bottom: 2px;} .conf_p span {width: 50px;}</style><div class="conf_p" style="margin-bottom:0">\
|
||||
' + value + '\
|
||||
<div style="margin-top:10px; padding-right:15px" class="text-right">\
|
||||
<button class="btn btn-success btn-sm mr5" onclick="nezha.agent_cfg(\'' + version + '\')">刷新</button>\
|
||||
<button class="btn btn-success btn-sm" onclick="nezha.agent_save_cfg(\'' + version + '\')">保存</button>\
|
||||
</div>\
|
||||
</div>'
|
||||
$(".soft-man-con").html(conf);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
readme:function (){
|
||||
var readme = '<ul class="help-info-text c7">';
|
||||
readme += '<li>安装时不会自动启动。</li>';
|
||||
readme += '<li>哪吒面板是改造版,用户名和密码登录【面板配置】,不依赖github/gitee/gitlab。</li>';
|
||||
readme += '<li>Agent需要先手动填写正确信息。</li>';
|
||||
readme += '</ul>';
|
||||
$('.soft-man-con').html(readme);
|
||||
}
|
||||
}
|
||||
Executable
+158
@@ -0,0 +1,158 @@
|
||||
#!/bin/bash
|
||||
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
|
||||
export PATH
|
||||
|
||||
curPath=`pwd`
|
||||
rootPath=$(dirname "$curPath")
|
||||
rootPath=$(dirname "$rootPath")
|
||||
serverPath=$(dirname "$rootPath")
|
||||
|
||||
install_tmp=${rootPath}/tmp/mw_install.pl
|
||||
VERSION=0.15.2
|
||||
|
||||
# bash install.sh install 0.15.2
|
||||
# cd /www/server/mdserver-web/plugins/nezha && bash install.sh install 0.15.2
|
||||
# cd /Users/midoks/Desktop/mwdev/server/mdserver-web/plugins/nezha && bash install.sh install 0.15.2
|
||||
|
||||
bash ${rootPath}/scripts/getos.sh
|
||||
OSNAME=`cat ${rootPath}/data/osname.pl`
|
||||
OSNAME_ID=`cat /etc/*-release | grep VERSION_ID | awk -F = '{print $2}' | awk -F "\"" '{print $2}'`
|
||||
|
||||
|
||||
ARCH="amd64"
|
||||
|
||||
get_arch() {
|
||||
TMP_ARCH=`arch`
|
||||
if [ "$TMP_ARCH" == "x86_64" ];then
|
||||
ARCH="amd64"
|
||||
elif [ "$TMP_ARCH" == "aarch64" ];then
|
||||
ARCH="arm64"
|
||||
else
|
||||
echo $ARCH
|
||||
fi
|
||||
}
|
||||
|
||||
load_vars() {
|
||||
OS=$(uname | tr '[:upper:]' '[:lower:]')
|
||||
TARGET_DIR="$serverPath/nezha/dashboard"
|
||||
|
||||
|
||||
## China_IP
|
||||
if [[ -z "${CN}" ]]; then
|
||||
if [[ $(curl -m 10 -s https://ipapi.co/json | grep 'China') != "" ]]; then
|
||||
CN=true
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ -z "${CN}" ]]; then
|
||||
GITHUB_RAW_URL="raw.githubusercontent.com/midoks/nezha/main"
|
||||
GITHUB_URL="github.com"
|
||||
else
|
||||
GITHUB_RAW_URL="cdn.jsdelivr.net/gh/midoks/nezha@main"
|
||||
GITHUB_URL="dn-dao-github-mirror.daocloud.io"
|
||||
fi
|
||||
|
||||
echo $GITHUB_RAW_URL
|
||||
echo $GITHUB_URL
|
||||
}
|
||||
|
||||
# download file
|
||||
download_file() {
|
||||
url="${1}"
|
||||
destination="${2}"
|
||||
|
||||
printf "Fetching ${url} \n\n"
|
||||
|
||||
if test -x "$(command -v curl)"; then
|
||||
code=$(curl --connect-timeout 15 -w '%{http_code}' -L "${url}" -o "${destination}")
|
||||
elif test -x "$(command -v wget)"; then
|
||||
code=$(wget -t2 -T15 -O "${destination}" --server-response "${url}" 2>&1 | awk '/^ HTTP/{print $2}' | tail -1)
|
||||
else
|
||||
printf "\e[1;31mNeither curl nor wget was available to perform http requests.\e[0m\n"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ "${code}" != 200 ]; then
|
||||
printf "\e[1;31mRequest failed with code %s\e[0m\n" $code
|
||||
exit 1
|
||||
else
|
||||
printf "\n\e[1;33mDownload succeeded\e[0m\n"
|
||||
fi
|
||||
}
|
||||
|
||||
|
||||
Install_dashborad(){
|
||||
echo '正在安装哪吒监控...' > $install_tmp
|
||||
mkdir -p $serverPath/source
|
||||
|
||||
if [ ! -f $TARGET_DIR/nezha ];then
|
||||
|
||||
DOWNLOAD_URL="https://${GITHUB_URL}/midoks/nezha/releases/download/v${VERSION}/nezha-${OS}-${ARCH}.zip"
|
||||
|
||||
DOWNLOAD_FILE="$(mktemp).zip"
|
||||
download_file $DOWNLOAD_URL $DOWNLOAD_FILE
|
||||
|
||||
if [ ! -d $TARGET_DIR ]; then
|
||||
mkdir -p $TARGET_DIR
|
||||
fi
|
||||
|
||||
unzip $DOWNLOAD_FILE -d $TARGET_DIR
|
||||
rm -rf $DOWNLOAD_FILE
|
||||
fi
|
||||
|
||||
}
|
||||
|
||||
Install_agent(){
|
||||
echo -e "正在下载监控端" > $install_tmp
|
||||
mkdir -p $serverPath/source
|
||||
|
||||
version=v0.15.1
|
||||
|
||||
AGENT_TARGET_DIR="$serverPath/nezha/agent"
|
||||
|
||||
DOWNLOAD_URL="https://${GITHUB_URL}/nezhahq/agent/releases/download/${version}/nezha-agent_${OS}_${ARCH}.zip"
|
||||
DOWNLOAD_FILE="$(mktemp).zip"
|
||||
|
||||
if [ ! -f $AGENT_TARGET_DIR/nezha-agent ];then
|
||||
download_file $DOWNLOAD_URL $DOWNLOAD_FILE
|
||||
|
||||
if [ ! -d $AGENT_TARGET_DIR ]; then
|
||||
mkdir -p $AGENT_TARGET_DIR
|
||||
fi
|
||||
|
||||
unzip $DOWNLOAD_FILE -d $AGENT_TARGET_DIR
|
||||
rm -rf $DOWNLOAD_FILE
|
||||
fi
|
||||
}
|
||||
|
||||
Install_App()
|
||||
{
|
||||
load_vars
|
||||
get_arch
|
||||
|
||||
Install_dashborad
|
||||
Install_agent
|
||||
|
||||
if [ -d $serverPath/nezha ];then
|
||||
echo "$VERSION" > $serverPath/nezha/version.pl
|
||||
cd ${rootPath} && python3 ${rootPath}/plugins/nezha/index.py init_cfg
|
||||
fi
|
||||
echo 'install successful' > $install_tmp
|
||||
}
|
||||
|
||||
Uninstall_App()
|
||||
{
|
||||
cd ${rootPath} && python3 ${rootPath}/plugins/nezha/index.py initd_uninstall
|
||||
cd ${rootPath} && python3 ${rootPath}/plugins/nezha/index.py initd_uninstall_agent
|
||||
cd ${rootPath} && python3 ${rootPath}/plugins/nezha/index.py stop
|
||||
cd ${rootPath} && python3 ${rootPath}/plugins/nezha/index.py stop_agent
|
||||
rm -rf $serverPath/nezha
|
||||
echo "install fail" > $install_tmp
|
||||
}
|
||||
|
||||
action=$1
|
||||
if [ "${1}" == 'install' ];then
|
||||
Install_App
|
||||
else
|
||||
Uninstall_App
|
||||
fi
|
||||
@@ -0,0 +1,2 @@
|
||||
## 指定共享内存
|
||||
lua_shared_dict healthcheck 10m;
|
||||
@@ -0,0 +1,64 @@
|
||||
location / {
|
||||
proxy_pass http://{$UPSTREAM_NAME};
|
||||
|
||||
client_max_body_size 10m;
|
||||
client_body_buffer_size 128k;
|
||||
|
||||
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
|
||||
proxy_set_header X-Real-IP $remote_addr;
|
||||
proxy_set_header Host $proxy_host;
|
||||
proxy_connect_timeout 90;
|
||||
proxy_send_timeout 90;
|
||||
proxy_read_timeout 90;
|
||||
proxy_buffer_size 4k;
|
||||
proxy_buffers 4 32k;
|
||||
proxy_busy_buffers_size 64k;
|
||||
proxy_temp_file_write_size 64k;
|
||||
}
|
||||
|
||||
|
||||
#location /upstream_status {
|
||||
# allow 127.0.0.1;
|
||||
# deny all;
|
||||
# access_log off;
|
||||
# default_type text/plain;
|
||||
# content_by_lua_block {
|
||||
# local hc = require "resty.upstream.healthcheck"
|
||||
# ngx.say("OpenResty Worker PID: ", ngx.worker.pid())
|
||||
# ngx.print(hc.status_page())
|
||||
# }
|
||||
#}
|
||||
|
||||
location /upstream_status_{$UPSTREAM_NAME} {
|
||||
allow 127.0.0.1;
|
||||
deny all;
|
||||
access_log off;
|
||||
default_type text/plain;
|
||||
content_by_lua_block {
|
||||
local json = require "cjson"
|
||||
local ok, upstream = pcall(require, "ngx.upstream")
|
||||
if not ok then
|
||||
ngx.print("[]")
|
||||
return
|
||||
end
|
||||
|
||||
local get_primary_peers = upstream.get_primary_peers
|
||||
local get_backup_peers = upstream.get_backup_peers
|
||||
local upstream_name = "{$UPSTREAM_NAME}"
|
||||
|
||||
peers, err = get_primary_peers(upstream_name)
|
||||
if not peers then
|
||||
ngx.print("[]")
|
||||
return
|
||||
end
|
||||
|
||||
peers_backup, err = get_backup_peers(upstream_name)
|
||||
if peers_backup then
|
||||
for k, v in pairs(peers_backup) do
|
||||
table.insert(peers,v)
|
||||
end
|
||||
end
|
||||
|
||||
ngx.print(json.encode(peers))
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,5 @@
|
||||
upstream {$UPSTREAM_NAME}
|
||||
{
|
||||
{$NODE_ALGO}
|
||||
{$NODE_SERVER_LIST}
|
||||
}
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 853 B |
Executable
+21
@@ -0,0 +1,21 @@
|
||||
<div class="bt-form">
|
||||
<div class="bt-w-main">
|
||||
<div class="bt-w-menu">
|
||||
<p class="bgw" onclick="pluginService('op_load_balance');">服务</p>
|
||||
<p onclick="loadBalanceList();">负载均衡</p>
|
||||
</div>
|
||||
<div class="bt-w-con pd15">
|
||||
<div class="soft-man-con"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<script type="text/javascript" src="/plugins/file?name=op_load_balance&f=js/app.js"></script>
|
||||
<script type="text/javascript">
|
||||
resetPluginWinWidth(800);
|
||||
resetPluginWinHeight(300);
|
||||
$.getScript( "/plugins/file?name=op_load_balance&f=js/app.js", function(){
|
||||
pluginService('op_load_balance');
|
||||
});
|
||||
</script>
|
||||
Executable
+474
@@ -0,0 +1,474 @@
|
||||
# coding:utf-8
|
||||
|
||||
import sys
|
||||
import io
|
||||
import os
|
||||
import time
|
||||
import subprocess
|
||||
import json
|
||||
import re
|
||||
|
||||
sys.path.append(os.getcwd() + "/class/core")
|
||||
import mw
|
||||
|
||||
|
||||
app_debug = False
|
||||
if mw.isAppleSystem():
|
||||
app_debug = True
|
||||
|
||||
|
||||
def getPluginName():
|
||||
return 'op_load_balance'
|
||||
|
||||
|
||||
def getPluginDir():
|
||||
return mw.getPluginDir() + '/' + getPluginName()
|
||||
|
||||
|
||||
def getServerDir():
|
||||
return mw.getServerDir() + '/' + getPluginName()
|
||||
|
||||
|
||||
def getArgs():
|
||||
args = sys.argv[2:]
|
||||
tmp = {}
|
||||
args_len = len(args)
|
||||
|
||||
if args_len == 1:
|
||||
t = args[0].strip('{').strip('}')
|
||||
if t.strip() == '':
|
||||
tmp = []
|
||||
else:
|
||||
t = t.split(':')
|
||||
tmp[t[0]] = t[1]
|
||||
tmp[t[0]] = t[1]
|
||||
elif args_len > 1:
|
||||
for i in range(len(args)):
|
||||
t = args[i].split(':')
|
||||
tmp[t[0]] = t[1]
|
||||
return tmp
|
||||
|
||||
|
||||
def checkArgs(data, ck=[]):
|
||||
for i in range(len(ck)):
|
||||
if not ck[i] in data:
|
||||
return (False, mw.returnJson(False, '参数:(' + ck[i] + ')没有!'))
|
||||
return (True, mw.returnJson(True, 'ok'))
|
||||
|
||||
|
||||
def getConf():
|
||||
path = getServerDir() + "/cfg.json"
|
||||
|
||||
if not os.path.exists(path):
|
||||
mw.writeFile(path, '[]')
|
||||
|
||||
c = mw.readFile(path)
|
||||
return json.loads(c)
|
||||
|
||||
|
||||
def writeConf(data):
|
||||
path = getServerDir() + "/cfg.json"
|
||||
mw.writeFile(path, json.dumps(data))
|
||||
|
||||
|
||||
def contentReplace(content):
|
||||
service_path = mw.getServerDir()
|
||||
content = content.replace('{$ROOT_PATH}', mw.getRootDir())
|
||||
content = content.replace('{$SERVER_PATH}', service_path)
|
||||
content = content.replace('{$APP_PATH}', app_path)
|
||||
return content
|
||||
|
||||
|
||||
def restartWeb():
|
||||
mw.opWeb('stop')
|
||||
mw.opWeb('start')
|
||||
|
||||
|
||||
def loadBalanceConf():
|
||||
path = mw.getServerDir() + '/web_conf/nginx/vhost/load_balance.conf'
|
||||
return path
|
||||
|
||||
|
||||
def initDreplace():
|
||||
|
||||
dst_conf_tpl = getPluginDir() + '/conf/load_balance.conf'
|
||||
dst_conf = loadBalanceConf()
|
||||
|
||||
if not os.path.exists(dst_conf):
|
||||
con = mw.readFile(dst_conf_tpl)
|
||||
mw.writeFile(dst_conf, con)
|
||||
|
||||
|
||||
def status():
|
||||
if not mw.getWebStatus():
|
||||
return 'stop'
|
||||
|
||||
dst_conf = loadBalanceConf()
|
||||
if not os.path.exists(dst_conf):
|
||||
return 'stop'
|
||||
|
||||
return 'start'
|
||||
|
||||
|
||||
def start():
|
||||
initDreplace()
|
||||
restartWeb()
|
||||
return 'ok'
|
||||
|
||||
|
||||
def stop():
|
||||
dst_conf = loadBalanceConf()
|
||||
os.remove(dst_conf)
|
||||
|
||||
deleteLoadBalanceAllCfg()
|
||||
restartWeb()
|
||||
return 'ok'
|
||||
|
||||
|
||||
def restart():
|
||||
restartWeb()
|
||||
return 'ok'
|
||||
|
||||
|
||||
def reload():
|
||||
restartWeb()
|
||||
return 'ok'
|
||||
|
||||
|
||||
def installPreInspection():
|
||||
check_op = mw.getServerDir() + "/openresty"
|
||||
if not os.path.exists(check_op):
|
||||
return "请先安装OpenResty"
|
||||
return 'ok'
|
||||
|
||||
|
||||
def deleteLoadBalanceAllCfg():
|
||||
cfg = getConf()
|
||||
upstream_dir = mw.getServerDir() + '/web_conf/nginx/upstream'
|
||||
lua_dir = mw.getServerDir() + '/web_conf/nginx/lua/init_worker_by_lua_file'
|
||||
rewrite_dir = mw.getServerDir() + '/web_conf/nginx/rewrite'
|
||||
vhost_dir = mw.getServerDir() + '/web_conf/nginx/vhost'
|
||||
|
||||
for conf in cfg:
|
||||
upstream_file = upstream_dir + '/' + conf['upstream_name'] + '.conf'
|
||||
if os.path.exists(upstream_file):
|
||||
os.remove(upstream_file)
|
||||
|
||||
lua_file = lua_dir + '/' + conf['upstream_name'] + '.lua'
|
||||
if os.path.exists(lua_file):
|
||||
os.remove(lua_file)
|
||||
|
||||
rewrite_file = rewrite_dir + '/' + conf['domain'] + '.conf'
|
||||
mw.writeFile(rewrite_file, '')
|
||||
|
||||
path = vhost_dir + '/' + conf['domain'] + '.conf'
|
||||
|
||||
content = mw.readFile(path)
|
||||
content = re.sub('include ' + upstream_file + ';' + "\n", '', content)
|
||||
mw.writeFile(path, content)
|
||||
|
||||
mw.opLuaInitWorkerFile()
|
||||
|
||||
|
||||
def makeConfServerList(data):
|
||||
slist = ''
|
||||
for x in data:
|
||||
slist += 'server '
|
||||
slist += x['ip'] + ':' + x['port']
|
||||
|
||||
if x['state'] == '0':
|
||||
slist += ' down;\n\t'
|
||||
continue
|
||||
|
||||
if x['state'] == '2':
|
||||
slist += ' backup;\n\t'
|
||||
continue
|
||||
|
||||
slist += ' weight=' + x['weight']
|
||||
slist += ' max_fails=' + x['max_fails']
|
||||
slist += ' fail_timeout=' + x['fail_timeout'] + "s;\n\t"
|
||||
return slist
|
||||
|
||||
|
||||
def makeLoadBalanceAllCfg(row):
|
||||
# 生成所有配置
|
||||
cfg = getConf()
|
||||
|
||||
upstream_dir = mw.getServerDir() + '/web_conf/nginx/upstream'
|
||||
rewrite_dir = mw.getServerDir() + '/web_conf/nginx/rewrite'
|
||||
vhost_dir = mw.getServerDir() + '/web_conf/nginx/vhost'
|
||||
upstream_tpl = getPluginDir() + '/conf/upstream.tpl.conf'
|
||||
rewrite_tpl = getPluginDir() + '/conf/rewrite.tpl.conf'
|
||||
|
||||
if not os.path.exists(upstream_dir):
|
||||
os.makedirs(upstream_dir)
|
||||
|
||||
conf = cfg[row]
|
||||
|
||||
# replace vhost start
|
||||
vhost_file = vhost_dir + '/' + conf['domain'] + '.conf'
|
||||
vcontent = mw.readFile(vhost_file)
|
||||
|
||||
vhost_find_str = 'upstream/' + conf['upstream_name'] + '.conf'
|
||||
vhead = 'include ' + mw.getServerDir() + '/web_conf/nginx/' + \
|
||||
vhost_find_str + ';'
|
||||
|
||||
vpos = vcontent.find(vhost_find_str)
|
||||
if vpos < 0:
|
||||
vcontent = vhead + "\n" + vcontent
|
||||
mw.writeFile(vhost_file, vcontent)
|
||||
# replace vhost end
|
||||
|
||||
# make upstream start
|
||||
upstream_file = upstream_dir + '/' + conf['upstream_name'] + '.conf'
|
||||
content = ''
|
||||
if len(conf['node_list']) > 0:
|
||||
content = mw.readFile(upstream_tpl)
|
||||
slist = makeConfServerList(conf['node_list'])
|
||||
content = content.replace('{$NODE_SERVER_LIST}', slist)
|
||||
content = content.replace('{$UPSTREAM_NAME}', conf['upstream_name'])
|
||||
if conf['node_algo'] != 'polling':
|
||||
content = content.replace('{$NODE_ALGO}', conf['node_algo'] + ';')
|
||||
else:
|
||||
content = content.replace('{$NODE_ALGO}', '')
|
||||
mw.writeFile(upstream_file, content)
|
||||
# make upstream end
|
||||
|
||||
# make rewrite start
|
||||
rewrite_file = rewrite_dir + '/' + conf['domain'] + '.conf'
|
||||
rcontent = ''
|
||||
if len(conf['node_list']) > 0:
|
||||
rcontent = mw.readFile(rewrite_tpl)
|
||||
rcontent = rcontent.replace('{$UPSTREAM_NAME}', conf['upstream_name'])
|
||||
mw.writeFile(rewrite_file, rcontent)
|
||||
# make rewrite end
|
||||
|
||||
# health check start
|
||||
lua_dir = mw.getServerDir() + '/web_conf/nginx/lua/init_worker_by_lua_file'
|
||||
lua_init_worker_file = lua_dir + '/' + conf['upstream_name'] + '.lua'
|
||||
if conf['node_health_check'] == 'ok':
|
||||
lua_dir_tpl = getPluginDir() + '/lua/health_check.lua.tpl'
|
||||
content = mw.readFile(lua_dir_tpl)
|
||||
content = content.replace('{$UPSTREAM_NAME}', conf['upstream_name'])
|
||||
content = content.replace('{$DOMAIN}', conf['domain'])
|
||||
mw.writeFile(lua_init_worker_file, content)
|
||||
else:
|
||||
if os.path.exists(lua_init_worker_file):
|
||||
os.remove(lua_init_worker_file)
|
||||
|
||||
mw.opLuaInitWorkerFile()
|
||||
# health check end
|
||||
return True
|
||||
|
||||
|
||||
def add_load_balance(args):
|
||||
|
||||
data = checkArgs(
|
||||
args, ['domain', 'upstream_name', 'node_algo', 'node_list', 'node_health_check'])
|
||||
if not data[0]:
|
||||
return data[1]
|
||||
|
||||
domain_json = args['domain']
|
||||
|
||||
tmp = json.loads(domain_json)
|
||||
domain = tmp['domain']
|
||||
|
||||
cfg = getConf()
|
||||
cfg_len = len(cfg)
|
||||
tmp = {}
|
||||
tmp['domain'] = domain
|
||||
tmp['data'] = args['domain']
|
||||
tmp['upstream_name'] = args['upstream_name']
|
||||
tmp['node_algo'] = args['node_algo']
|
||||
tmp['node_list'] = args['node_list']
|
||||
tmp['node_health_check'] = args['node_health_check']
|
||||
cfg.append(tmp)
|
||||
writeConf(cfg)
|
||||
|
||||
import site_api
|
||||
sobj = site_api.site_api()
|
||||
domain_path = mw.getWwwDir() + '/' + domain
|
||||
|
||||
ps = '负载均衡[' + domain + ']'
|
||||
data = sobj.add(domain_json, '80', ps, domain_path, '00')
|
||||
|
||||
makeLoadBalanceAllCfg(cfg_len)
|
||||
mw.restartWeb()
|
||||
return mw.returnJson(True, '添加成功', data)
|
||||
|
||||
|
||||
def edit_load_balance(args):
|
||||
data = checkArgs(
|
||||
args, ['row', 'node_algo', 'node_list', 'node_health_check'])
|
||||
if not data[0]:
|
||||
return data[1]
|
||||
|
||||
row = int(args['row'])
|
||||
|
||||
cfg = getConf()
|
||||
tmp = cfg[row]
|
||||
tmp['node_algo'] = args['node_algo']
|
||||
tmp['node_list'] = args['node_list']
|
||||
tmp['node_health_check'] = args['node_health_check']
|
||||
cfg[row] = tmp
|
||||
writeConf(cfg)
|
||||
|
||||
makeLoadBalanceAllCfg(row)
|
||||
mw.restartWeb()
|
||||
return mw.returnJson(True, '修改成功', data)
|
||||
|
||||
|
||||
def loadBalanceList():
|
||||
cfg = getConf()
|
||||
return mw.returnJson(True, 'ok', cfg)
|
||||
|
||||
|
||||
def loadBalanceDelete():
|
||||
args = getArgs()
|
||||
data = checkArgs(args, ['row'])
|
||||
if not data[0]:
|
||||
return data[1]
|
||||
|
||||
row = int(args['row'])
|
||||
|
||||
cfg = getConf()
|
||||
data = cfg[row]
|
||||
|
||||
import site_api
|
||||
sobj = site_api.site_api()
|
||||
|
||||
sid = mw.M('sites').where('name=?', (data['domain'],)).getField('id')
|
||||
|
||||
if type(sid) == list:
|
||||
del(cfg[row])
|
||||
writeConf(cfg)
|
||||
return mw.returnJson(False, '已经删除了!')
|
||||
|
||||
status = sobj.delete(sid, data['domain'], 1)
|
||||
status_data = json.loads(status)
|
||||
|
||||
if status_data['status']:
|
||||
del(cfg[row])
|
||||
writeConf(cfg)
|
||||
|
||||
upstream_dir = mw.getServerDir() + '/web_conf/nginx/upstream'
|
||||
rewrite_dir = mw.getServerDir() + '/web_conf/nginx/rewrite'
|
||||
|
||||
upstream_file = upstream_dir + '/' + data['upstream_name'] + '.conf'
|
||||
if os.path.exists(upstream_file):
|
||||
os.remove(upstream_file)
|
||||
|
||||
rewrite_file = rewrite_dir + '/' + data['domain'] + '.conf'
|
||||
if os.path.exists(rewrite_file):
|
||||
mw.writeFile(rewrite_file, '')
|
||||
|
||||
return mw.returnJson(status_data['status'], status_data['msg'])
|
||||
|
||||
|
||||
def http_get(url):
|
||||
ret = re.search(r'https://', url)
|
||||
if ret:
|
||||
try:
|
||||
from gevent import monkey
|
||||
monkey.patch_ssl()
|
||||
import requests
|
||||
ret = requests.get(url=str(url), verify=False, timeout=10)
|
||||
status = [200, 301, 302, 404, 403]
|
||||
if ret.status_code in status:
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
except:
|
||||
return False
|
||||
else:
|
||||
try:
|
||||
if sys.version_info[0] == 2:
|
||||
import urllib2
|
||||
rec = urllib2.urlopen(url, timeout=3)
|
||||
else:
|
||||
import urllib.request
|
||||
rec = urllib.request.urlopen(url, timeout=3)
|
||||
status = [200, 301, 302, 404, 403]
|
||||
if rec.getcode() in status:
|
||||
return True
|
||||
return False
|
||||
except:
|
||||
return False
|
||||
|
||||
|
||||
def checkUrl():
|
||||
args = getArgs()
|
||||
data = checkArgs(args, ['ip', 'port', 'path'])
|
||||
if not data[0]:
|
||||
return data[1]
|
||||
|
||||
ip = args['ip']
|
||||
port = args['port']
|
||||
path = args['path']
|
||||
|
||||
if port == '443':
|
||||
url = 'https://' + str(ip) + ':' + str(port) + str(path.strip())
|
||||
else:
|
||||
url = 'http://' + str(ip) + ':' + str(port) + str(path.strip())
|
||||
ret = http_get(url)
|
||||
if not ret:
|
||||
return mw.returnJson(False, '访问节点[%s]失败' % url)
|
||||
return mw.returnJson(True, '访问节点[%s]成功' % url)
|
||||
|
||||
|
||||
def getHealthStatus():
|
||||
args = getArgs()
|
||||
data = checkArgs(args, ['row'])
|
||||
if not data[0]:
|
||||
return data[1]
|
||||
|
||||
row = int(args['row'])
|
||||
|
||||
cfg = getConf()
|
||||
data = cfg[row]
|
||||
|
||||
url = 'http://' + data['domain'] + \
|
||||
'/upstream_status_' + data['upstream_name']
|
||||
|
||||
url_data = mw.httpGet(url)
|
||||
return mw.returnJson(True, 'ok', json.loads(url_data))
|
||||
|
||||
|
||||
def getLogs():
|
||||
args = getArgs()
|
||||
data = checkArgs(args, ['domain'])
|
||||
if not data[0]:
|
||||
return data[1]
|
||||
|
||||
domain = args['domain']
|
||||
logs = mw.getLogsDir() + '/' + domain + '.log'
|
||||
return logs
|
||||
|
||||
if __name__ == "__main__":
|
||||
func = sys.argv[1]
|
||||
if func == 'status':
|
||||
print(status())
|
||||
elif func == 'start':
|
||||
print(start())
|
||||
elif func == 'stop':
|
||||
print(stop())
|
||||
elif func == 'restart':
|
||||
print(restart())
|
||||
elif func == 'reload':
|
||||
print(reload())
|
||||
elif func == 'install_pre_inspection':
|
||||
print(installPreInspection())
|
||||
elif func == 'add_load_balance':
|
||||
print(addLoadBalance())
|
||||
elif func == 'load_balance_list':
|
||||
print(loadBalanceList())
|
||||
elif func == 'load_balance_delete':
|
||||
print(loadBalanceDelete())
|
||||
elif func == 'check_url':
|
||||
print(checkUrl())
|
||||
elif func == 'get_logs':
|
||||
print(getLogs())
|
||||
elif func == 'get_health_status':
|
||||
print(getHealthStatus())
|
||||
else:
|
||||
print('error')
|
||||
Executable
+16
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"title":"OP负载均衡",
|
||||
"tip":"soft",
|
||||
"name":"op_load_balance",
|
||||
"type":"其他插件",
|
||||
"install_pre_inspection":true,
|
||||
"ps":"基于OpenResty的负载均衡",
|
||||
"shell":"install.sh",
|
||||
"checks":"server/op_load_balance",
|
||||
"path":"server/op_load_balance",
|
||||
"author":"midoks",
|
||||
"home":"https://github.com/midoks",
|
||||
"date":"2023-02-02",
|
||||
"pid": "1",
|
||||
"versions": ["1.0"]
|
||||
}
|
||||
Executable
+45
@@ -0,0 +1,45 @@
|
||||
#!/bin/bash
|
||||
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
|
||||
export PATH
|
||||
|
||||
curPath=`pwd`
|
||||
rootPath=$(dirname "$curPath")
|
||||
rootPath=$(dirname "$rootPath")
|
||||
serverPath=$(dirname "$rootPath")
|
||||
|
||||
install_tmp=${rootPath}/tmp/mw_install.pl
|
||||
|
||||
action=$1
|
||||
version=$2
|
||||
sys_os=`uname`
|
||||
|
||||
if [ -f ${rootPath}/bin/activate ];then
|
||||
source ${rootPath}/bin/activate
|
||||
fi
|
||||
|
||||
if [ "$sys_os" == "Darwin" ];then
|
||||
BAK='_bak'
|
||||
else
|
||||
BAK=''
|
||||
fi
|
||||
|
||||
Install_App(){
|
||||
echo '正在安装脚本文件...' > $install_tmp
|
||||
mkdir -p $serverPath/op_load_balance
|
||||
echo "${version}" > $serverPath/op_load_balance/version.pl
|
||||
cd ${rootPath} && python3 ${rootPath}/plugins/op_load_balance/index.py start
|
||||
echo 'install ok' > $install_tmp
|
||||
}
|
||||
|
||||
Uninstall_App(){
|
||||
cd ${rootPath} && python3 ${rootPath}/plugins/op_load_balance/index.py stop
|
||||
rm -rf $serverPath/op_load_balance
|
||||
}
|
||||
|
||||
|
||||
action=$1
|
||||
if [ "${1}" == 'install' ];then
|
||||
Install_App
|
||||
else
|
||||
Uninstall_App
|
||||
fi
|
||||
@@ -0,0 +1,606 @@
|
||||
function ooPost(method,args,callback){
|
||||
var _args = null;
|
||||
if (typeof(args) == 'string'){
|
||||
_args = JSON.stringify(toArrayObject(args));
|
||||
} else {
|
||||
_args = JSON.stringify(args);
|
||||
}
|
||||
|
||||
var loadT = layer.msg('正在获取...', { icon: 16, time: 0, shade: 0.3 });
|
||||
$.post('/plugins/run', {name:'op_load_balance', func:method, args:_args}, function(data) {
|
||||
layer.close(loadT);
|
||||
if (!data.status){
|
||||
layer.msg(data.msg,{icon:0,time:2000,shade: [0.3, '#000']});
|
||||
return;
|
||||
}
|
||||
|
||||
if(typeof(callback) == 'function'){
|
||||
callback(data);
|
||||
}
|
||||
},'json');
|
||||
}
|
||||
|
||||
function ooAsyncPost(method,args){
|
||||
var _args = null;
|
||||
if (typeof(args) == 'string'){
|
||||
_args = JSON.stringify(toArrayObject(args));
|
||||
} else {
|
||||
_args = JSON.stringify(args);
|
||||
}
|
||||
return syncPost('/plugins/run', {name:'op_load_balance', func:method, args:_args});
|
||||
}
|
||||
|
||||
function ooPostCallbak(method, args, callback){
|
||||
var loadT = layer.msg('正在获取...', { icon: 16, time: 0, shade: 0.3 });
|
||||
|
||||
var req_data = {};
|
||||
req_data['name'] = 'op_load_balance';
|
||||
req_data['func'] = method;
|
||||
args['version'] = '1.0';
|
||||
|
||||
if (typeof(args) == 'string'){
|
||||
req_data['args'] = JSON.stringify(toArrayObject(args));
|
||||
} else {
|
||||
req_data['args'] = JSON.stringify(args);
|
||||
}
|
||||
|
||||
$.post('/plugins/callback', req_data, function(data) {
|
||||
layer.close(loadT);
|
||||
if (!data.status){
|
||||
layer.msg(data.msg,{icon:0,time:2000,shade: [0.3, '#000']});
|
||||
return;
|
||||
}
|
||||
|
||||
if(typeof(callback) == 'function'){
|
||||
callback(data);
|
||||
}
|
||||
},'json');
|
||||
}
|
||||
|
||||
function addNode(){
|
||||
layer.open({
|
||||
type: 1,
|
||||
area: ['450px','580px'],
|
||||
title: '添加节点',
|
||||
closeBtn: 1,
|
||||
shift: 5,
|
||||
shadeClose: true,
|
||||
btn:['提交','关闭'],
|
||||
content: "<form class='bt-form pd20'>\
|
||||
<div class='line'>\
|
||||
<span class='tname'>IP地址</span>\
|
||||
<div class='info-r'>\
|
||||
<input name='ip' class='bt-input-text mr5' placeholder='负载名称,可以是英文字母和下划线,不能使用中文' type='text' style='width:250px' value='127.0.0.1'>\
|
||||
</div>\
|
||||
</div>\
|
||||
<div class='line'>\
|
||||
<span class='tname'>端口</span>\
|
||||
<div class='info-r'>\
|
||||
<input name='port' class='bt-input-text mr5' type='text' style='width:250px' value='80'>\
|
||||
</div>\
|
||||
</div>\
|
||||
<div class='line'>\
|
||||
<span class='tname'>验证文件路径</span>\
|
||||
<div class='info-r'>\
|
||||
<input name='path' class='bt-input-text mr5' type='text' style='width:250px' value='/'>\
|
||||
</div>\
|
||||
</div>\
|
||||
<div class='line'>\
|
||||
<span class='tname'>节点状态</span>\
|
||||
<div class='info-r'>\
|
||||
<select name='state'>\
|
||||
<option value='1'>参与者</option>\
|
||||
<option value='2'>备份</option>\
|
||||
<option value='0'>停用</option>\
|
||||
</select>\
|
||||
</div>\
|
||||
</div>\
|
||||
<div class='line'>\
|
||||
<span class='tname'>权重</span>\
|
||||
<div class='info-r'>\
|
||||
<input name='weight' class='bt-input-text mr5' type='text' style='width:250px' value='1'>\
|
||||
</div>\
|
||||
</div>\
|
||||
<div class='line'>\
|
||||
<span class='tname'>阈值</span>\
|
||||
<div class='info-r'>\
|
||||
<input name='max_fails' class='bt-input-text mr5' type='text' style='width:250px' value='2'>次\
|
||||
</div>\
|
||||
</div>\
|
||||
<div class='line'>\
|
||||
<span class='tname'>恢复时间</span>\
|
||||
<div class='info-r'>\
|
||||
<input name='fail_timeout' class='bt-input-text mr5' type='text' style='width:250px' value='10'>秒\
|
||||
</div>\
|
||||
</div>\
|
||||
<ul style='margin-left:10px' class='help-info-text c7'>\
|
||||
<li>备份状态: 指当其它节点都无法使用时才会使用此节点</li>\
|
||||
<li>参与状态: 正常参与负载均衡,请至少添加1个普通节点</li>\
|
||||
<li>验证文件路径: 用于检查文件路径地址是否可用</li>\
|
||||
<li>IP地址: 仅支持IP地址,否则无法正常参与负载均衡</li>\
|
||||
<li>阈值: 在恢复时间的时间段内,如果OpenResty与节点通信尝试失败的次数达到此值,OpenResty就认为服务器不可用</li>\
|
||||
</ul>\
|
||||
</form>",
|
||||
success:function(){
|
||||
},
|
||||
yes:function(index) {
|
||||
|
||||
var ip = $('input[name="ip"]').val();
|
||||
var port = $('input[name="port"]').val();
|
||||
var path = $('input[name="path"]').val();
|
||||
var state = $('select[name="state"]').val();
|
||||
var weight = $('input[name="weight"]').val();
|
||||
var max_fails = $('input[name="max_fails"]').val();
|
||||
var fail_timeout = $('input[name="fail_timeout"]').val();
|
||||
|
||||
ooPost('check_url', {ip:ip,port:port,path:path},function(rdata){
|
||||
var rdata = $.parseJSON(rdata.data);
|
||||
showMsg(rdata.msg, function(){
|
||||
if (rdata.status){
|
||||
layer.close(index);
|
||||
$('#nodecon .nulltr').hide();
|
||||
|
||||
var tbody = '<tr>';
|
||||
tbody +='<td>'+ip+'</td>';
|
||||
tbody +='<td>'+port+'</td>';
|
||||
tbody +='<td>'+path+'</td>';
|
||||
|
||||
tbody +="<td><select name='state'>";
|
||||
var state_option_list = {
|
||||
'1':'参与者',
|
||||
'2':'备份',
|
||||
'0':'停用',
|
||||
}
|
||||
for (i in state_option_list) {
|
||||
if (i == state){
|
||||
tbody +="<option value='"+i+"' selected>"+state_option_list[i]+"</option>";
|
||||
} else{
|
||||
tbody +="<option value='"+i+"'>"+state_option_list[i]+"</option>";
|
||||
}
|
||||
}
|
||||
tbody +="</select></td>";
|
||||
|
||||
tbody +='<td><input type="number" name="weight" value="'+weight+'" style="width:50px"></td>';
|
||||
tbody +='<td><input type="number" name="max_fails" value="'+max_fails+'" style="width:50px"></td>';
|
||||
tbody +='<td><input type="number" name="fail_timeout" value="'+fail_timeout+'" style="width:50px"></td>';
|
||||
tbody +='<td class="text-right" width="50"><a class="btlink minus delete">删除</a></td>';
|
||||
tbody += '</tr>';
|
||||
$('#nodecon').append(tbody);
|
||||
|
||||
$('#nodecon .delete').click(function(){
|
||||
$(this).parent().parent().remove();
|
||||
if ($('#nodecon tr').length == 1 ){
|
||||
$('#nodecon .nulltr').show();
|
||||
}
|
||||
});
|
||||
}
|
||||
},{ icon: rdata.status ? 1 : 2 }, 2000);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function addBalance(){
|
||||
layer.open({
|
||||
type: 1,
|
||||
area: ['750px','460px'],
|
||||
title: '创建负载',
|
||||
closeBtn: 1,
|
||||
shift: 5,
|
||||
shadeClose: true,
|
||||
btn:['提交','关闭'],
|
||||
content: "<form class='bt-form pd20'>\
|
||||
<div class='line'>\
|
||||
<span class='tname'>域名</span>\
|
||||
<div class='info-r'><textarea name='load_domain' class='bt-input-text mr5' placeholder='' style='width:95%;resize: none;height:90px;line-height:20px;'></textarea></div>\
|
||||
</div>\
|
||||
<div class='line'>\
|
||||
<span class='tname'>负载名称</span>\
|
||||
<div class='info-r'>\
|
||||
<input name='upstream_name' class='bt-input-text mr5' placeholder='负载名称,可以是英文字母和下划线,不能使用中文' type='text' style='width:95%' value=''>\
|
||||
</div>\
|
||||
</div>\
|
||||
<div class='line'>\
|
||||
<span class='tname'>节点调度</span>\
|
||||
<div class='info-r'>\
|
||||
<select name='node_algo'>\
|
||||
<option value='polling'>轮询[默认]</option>\
|
||||
<option value='ip_hash'>ip_hash</option>\
|
||||
<option value='fair'>fair</option>\
|
||||
<option value='url_hash'>url_hash</option>\
|
||||
<option value='least_conn'>least_conn</option>\
|
||||
</select>\
|
||||
</div>\
|
||||
</div>\
|
||||
<div class='line'>\
|
||||
<span class='tname'>节点健康检查</span>\
|
||||
<div class='info-r'>\
|
||||
<input type='checkbox' name='node_health_check' checked>\
|
||||
</div>\
|
||||
</div>\
|
||||
<div class='line'>\
|
||||
<span class='tname'>节点</span>\
|
||||
<div class='info-r'>\
|
||||
<div class='table-con divtable' style='max-height:120px;overflow:auto; margin-bottom:8px;width:95%'>\
|
||||
<table class='table table-hover' id='fixTable3'>\
|
||||
<thead>\
|
||||
<tr>\
|
||||
<th width='120'>IP地址</th>\
|
||||
<th width='60'>端口</th>\
|
||||
<th width='120'>验证路径</th>\
|
||||
<th width='60'>状态</th>\
|
||||
<th width='60'>权重</th>\
|
||||
<th width='60'>阀值</th>\
|
||||
<th width='120'>恢复时间</th>\
|
||||
<th width='80' class='text-right'>操作</th>\
|
||||
</tr>\
|
||||
</thead>\
|
||||
<tbody id='nodecon'>\
|
||||
<tr class='nulltr'>\
|
||||
<td colspan='8' align='center'>当前节点为空,请至少添加一个普通节点</td>\
|
||||
</tr>\
|
||||
</tbody>\
|
||||
</table>\
|
||||
</div>\
|
||||
<span class='btn btn-success btn-sm add_node' style='vertical-align:0'>添加节点</span>\
|
||||
</div>\
|
||||
</div>\
|
||||
</form>",
|
||||
success:function(){
|
||||
$('textarea[name="load_domain"]').attr('placeholder','每行填写一个域名,默认为80端口。\n泛解析添加方法 *.domain.com\n如另加端口格式为 www.domain.com:88');
|
||||
var rval = getRandomString(6);
|
||||
$('input[name="upstream_name"]').val('load_balance_'+rval);
|
||||
|
||||
$('.add_node').click(function(){
|
||||
addNode();
|
||||
});
|
||||
},
|
||||
yes:function(index) {
|
||||
var data = {};
|
||||
|
||||
var upstream_name = $('input[name="upstream_name"]').val();
|
||||
if (upstream_name == ''){
|
||||
layer.msg('负载名称不能为空!',{icon:0,time:2000,shade: [0.3, '#000']});
|
||||
return;
|
||||
}
|
||||
|
||||
var domain = $('textarea[name="load_domain"]').val().replace('http://','').replace('https://','').split("\n");
|
||||
if (domain[0] == ''){
|
||||
layer.msg('域名不能为空!',{icon:0,time:2000,shade: [0.3, '#000']});
|
||||
return;
|
||||
}
|
||||
|
||||
var domainlist = '';
|
||||
for(var i=1; i<domain.length; i++){
|
||||
domainlist += '"'+domain[i]+'",';
|
||||
}
|
||||
domain ='{"domain":"'+domain[0]+'","domainlist":['+domainlist+'],"count":'+domain.length+'}';//拼接json
|
||||
data['domain'] = domain;
|
||||
data['upstream_name'] = upstream_name;
|
||||
|
||||
data['node_algo'] = $('select[name="node_algo"]').val();
|
||||
|
||||
data['node_health_check'] = 'fail';
|
||||
if ($('input[name="node_health_check"]').prop('checked')){
|
||||
data['node_health_check'] = 'ok';
|
||||
}
|
||||
|
||||
var node_list = [];
|
||||
$('#nodecon tr').each(function(){
|
||||
|
||||
var ip = $(this).find('td').eq(0).text();
|
||||
var port = $(this).find('td').eq(1).text();
|
||||
|
||||
if (port == ''){return;}
|
||||
|
||||
var path = $(this).find('td').eq(2).text();
|
||||
var state = $(this).find('select[name="state"]').val();
|
||||
var weight = $(this).find('input[name="weight"]').val();
|
||||
var max_fails = $(this).find('input[name="max_fails"]').val();
|
||||
var fail_timeout = $(this).find('input[name="fail_timeout"]').val();
|
||||
|
||||
var tmp = {
|
||||
ip:ip,
|
||||
port:port,
|
||||
path:path,
|
||||
state:state,
|
||||
weight:weight,
|
||||
max_fails:max_fails,
|
||||
fail_timeout:fail_timeout,
|
||||
}
|
||||
node_list.push(tmp);
|
||||
});
|
||||
data['node_list'] = node_list;
|
||||
ooPostCallbak('add_load_balance', data, function(rdata){
|
||||
var rdata = $.parseJSON(rdata.data);
|
||||
showMsg(rdata.msg, function(){
|
||||
layer.close(index);
|
||||
loadBalanceListRender();
|
||||
},{ icon: rdata.status ? 1 : 2 }, 2000);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function editBalance(data, row){
|
||||
layer.open({
|
||||
type: 1,
|
||||
area: ['750px','400px'],
|
||||
title: '编辑负载',
|
||||
closeBtn: 1,
|
||||
shift: 5,
|
||||
shadeClose: true,
|
||||
btn:['提交','关闭'],
|
||||
content: "<form class='bt-form pd20'>\
|
||||
<div class='line'>\
|
||||
<span class='tname'>负载名称</span>\
|
||||
<div class='info-r'><input name='upstream_name' class='bt-input-text mr5' type='text' style='width:95%;background: rgb(238, 238, 238);' value='' readonly></div>\
|
||||
</div>\
|
||||
<div class='line'>\
|
||||
<span class='tname'>节点调度</span>\
|
||||
<div class='info-r'>\
|
||||
<select name='node_algo'>\
|
||||
<option value='polling'>轮询[默认]</option>\
|
||||
<option value='ip_hash'>ip_hash</option>\
|
||||
<option value='fair'>fair</option>\
|
||||
<option value='url_hash'>url_hash</option>\
|
||||
<option value='least_conn'>least_conn</option>\
|
||||
</select>\
|
||||
</div>\
|
||||
</div>\
|
||||
<div class='line'>\
|
||||
<span class='tname'>节点健康检查</span>\
|
||||
<div class='info-r'>\
|
||||
<input type='checkbox' name='node_health_check'>\
|
||||
</div>\
|
||||
</div>\
|
||||
<div class='line'>\
|
||||
<span class='tname'>节点</span>\
|
||||
<div class='info-r'>\
|
||||
<div class='table-con divtable' style='max-height:120px;overflow:auto; margin-bottom:8px;width:95%'>\
|
||||
<table class='table table-hover' id='fixTable3'>\
|
||||
<thead>\
|
||||
<tr>\
|
||||
<th width='120'>IP地址</th>\
|
||||
<th width='60'>端口</th>\
|
||||
<th width='120'>验证路径</th>\
|
||||
<th width='60'>状态</th>\
|
||||
<th width='60'>权重</th>\
|
||||
<th width='60'>阀值</th>\
|
||||
<th width='120'>恢复时间</th>\
|
||||
<th width='80' class='text-right'>操作</th>\
|
||||
</tr>\
|
||||
</thead>\
|
||||
<tbody id='nodecon'>\
|
||||
<tr class='nulltr'>\
|
||||
<td colspan='8' align='center'>当前节点为空,请至少添加一个普通节点</td>\
|
||||
</tr>\
|
||||
</tbody>\
|
||||
</table>\
|
||||
</div>\
|
||||
<span class='btn btn-success btn-sm add_node' style='vertical-align:0'>添加节点</span>\
|
||||
</div>\
|
||||
</div>\
|
||||
</form>",
|
||||
success:function(){
|
||||
$('input[name="upstream_name"]').val(data['upstream_name']);
|
||||
$('select[name="node_algo"]').val(data['node_algo']);
|
||||
|
||||
$('input[name="node_health_check"]').prop('checked',false);
|
||||
if (data['node_health_check'] == 'ok'){
|
||||
$('input[name="node_health_check"]').prop('checked',true);
|
||||
}
|
||||
|
||||
var node_list = data['node_list'];
|
||||
if (node_list.length>0){
|
||||
$('#nodecon .nulltr').hide();
|
||||
}
|
||||
|
||||
var state_option_list = {
|
||||
'1':'参与者',
|
||||
'2':'备份',
|
||||
'0':'停用',
|
||||
}
|
||||
|
||||
for (var n in node_list) {
|
||||
|
||||
var tbody = '<tr>';
|
||||
tbody +='<td>'+node_list[n]['ip']+'</td>';
|
||||
tbody +='<td>'+node_list[n]['port']+'</td>';
|
||||
tbody +='<td>'+node_list[n]['path']+'</td>';
|
||||
|
||||
tbody +="<td><select name='state'>";
|
||||
|
||||
for (i in state_option_list) {
|
||||
if (i == node_list[n]['state']){
|
||||
tbody +="<option value='"+i+"' selected>"+state_option_list[i]+"</option>";
|
||||
} else{
|
||||
tbody +="<option value='"+i+"'>"+state_option_list[i]+"</option>";
|
||||
}
|
||||
}
|
||||
tbody +="</select></td>";
|
||||
|
||||
tbody +='<td><input type="number" name="weight" value="'+node_list[n]['weight']+'" style="width:50px"></td>';
|
||||
tbody +='<td><input type="number" name="max_fails" value="'+node_list[n]['max_fails']+'" style="width:50px"></td>';
|
||||
tbody +='<td><input type="number" name="fail_timeout" value="'+node_list[n]['fail_timeout']+'" style="width:50px"></td>';
|
||||
tbody +='<td class="text-right" width="50"><a class="btlink minus delete">删除</a></td>';
|
||||
tbody += '</tr>';
|
||||
$('#nodecon').append(tbody);
|
||||
}
|
||||
|
||||
$('#nodecon .delete').click(function(){
|
||||
$(this).parent().parent().remove();
|
||||
if ($('#nodecon tr').length == 1 ){
|
||||
$('#nodecon .nulltr').show();
|
||||
}
|
||||
});
|
||||
|
||||
$('.add_node').click(function(){
|
||||
addNode();
|
||||
});
|
||||
},
|
||||
yes:function(index) {
|
||||
var data = {};
|
||||
|
||||
data['node_algo'] = $('select[name="node_algo"]').val();
|
||||
data['node_health_check'] = 'fail';
|
||||
if ($('input[name="node_health_check"]').prop('checked')){
|
||||
data['node_health_check'] = 'ok';
|
||||
}
|
||||
|
||||
var node_list = [];
|
||||
$('#nodecon tr').each(function(){
|
||||
|
||||
var ip = $(this).find('td').eq(0).text();
|
||||
var port = $(this).find('td').eq(1).text();
|
||||
|
||||
if (port == ''){return;}
|
||||
|
||||
var path = $(this).find('td').eq(2).text();
|
||||
var state = $(this).find('select[name="state"]').val();
|
||||
var weight = $(this).find('input[name="weight"]').val();
|
||||
var max_fails = $(this).find('input[name="max_fails"]').val();
|
||||
var fail_timeout = $(this).find('input[name="fail_timeout"]').val();
|
||||
|
||||
var tmp = {
|
||||
ip:ip,
|
||||
port:port,
|
||||
path:path,
|
||||
state:state,
|
||||
weight:weight,
|
||||
max_fails:max_fails,
|
||||
fail_timeout:fail_timeout,
|
||||
}
|
||||
node_list.push(tmp);
|
||||
});
|
||||
data['node_list'] = node_list;
|
||||
data['row'] = row;
|
||||
ooPostCallbak('edit_load_balance', data, function(rdata){
|
||||
var rdata = $.parseJSON(rdata.data);
|
||||
showMsg(rdata.msg, function(){
|
||||
layer.close(index);
|
||||
loadBalanceListRender();
|
||||
},{ icon: rdata.status ? 1 : 2 }, 2000);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function loadBalanceListRender(){
|
||||
ooPost('load_balance_list', {}, function(rdata){
|
||||
var rdata = $.parseJSON(rdata.data);
|
||||
var alist = rdata.data;
|
||||
|
||||
var tbody = '';
|
||||
for (var i = 0; i < alist.length; i++) {
|
||||
tbody += '<tr>';
|
||||
tbody += '<td>'+alist[i]['domain']+'</td>';
|
||||
tbody += '<td>'+alist[i]['upstream_name']+'</td>';
|
||||
tbody += '<td>'+alist[i]['node_list'].length+'</td>';
|
||||
tbody += '<td><a class="btlink log_look" data-row="'+i+'">查看</a></td>';
|
||||
tbody += '<td><a class="btlink health_status" data-row="'+i+'">查看</a></td>';
|
||||
tbody += '<td style="text-align: right;"><a class="btlink edit" data-row="'+i+'">修改</a> | <a class="btlink delete" data-row="'+i+'">删除</a></td>';
|
||||
tbody += '</tr>';
|
||||
}
|
||||
|
||||
$('#nodeTable').html(tbody);
|
||||
$('.nodeTablePage .Pcount').text('共'+alist.length+'条');
|
||||
$('#nodeTable .edit').click(function(){
|
||||
var row = $(this).data('row');
|
||||
editBalance(alist[row],row);
|
||||
});
|
||||
|
||||
$('#nodeTable .log_look').click(function(){
|
||||
var row = $(this).data('row');
|
||||
var args = {'domain':alist[row]['domain']};
|
||||
pluginRollingLogs('op_load_balance','','get_logs',JSON.stringify(args),20);
|
||||
});
|
||||
|
||||
$('#nodeTable .health_status').click(function(){
|
||||
var row = $(this).data('row');
|
||||
ooPost('get_health_status', {row:row}, function(rdata){
|
||||
var rdata = $.parseJSON(rdata.data);
|
||||
|
||||
var tval = '';
|
||||
for (var i = 0; i < rdata.data.length; i++) {
|
||||
tval += '<tr>';
|
||||
tval += '<td>'+rdata.data[i]['name']+'</td>';
|
||||
|
||||
if (typeof(rdata.data[i]['down']) != 'undefined' && rdata.data[i]['down']){
|
||||
tval += '<td><span style="color:red;">不正常</span></td>';
|
||||
} else{
|
||||
tval += '<td><span class="btlink">正常</span></td>';
|
||||
}
|
||||
tval += '</tr>';
|
||||
}
|
||||
|
||||
var tbody = "<div class='bt-form pd20'>\
|
||||
<div>\
|
||||
<div id='gitea_table' class='divtable' style='margin-top:5px;'>\
|
||||
<table class='table table-hover'>\
|
||||
<thead>\
|
||||
<tr>\
|
||||
<th width='120'>地址</th>\
|
||||
<th width='60'>状态</th>\
|
||||
</tr>\
|
||||
</thead>\
|
||||
<tbody>"+tval+"</tbody>\
|
||||
</table>\
|
||||
</div>\
|
||||
</div>\
|
||||
</div>";
|
||||
|
||||
layer.open({
|
||||
type: 1,
|
||||
area: ['500px','300px'],
|
||||
title: '节点状态',
|
||||
closeBtn: 1,
|
||||
shift: 5,
|
||||
shadeClose: true,
|
||||
btn:['提交','关闭'],
|
||||
content:tbody,
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
$('#nodeTable .delete').click(function(){
|
||||
var row = $(this).data('row');
|
||||
ooPost('load_balance_delete', {row:row}, function(rdata){
|
||||
var rdata = $.parseJSON(rdata.data);
|
||||
showMsg(rdata.msg, function(){
|
||||
loadBalanceListRender();
|
||||
},{ icon: rdata.status ? 1 : 2 }, 2000);
|
||||
});
|
||||
});
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
function loadBalanceList() {
|
||||
var body = '<div class="bt-box active" style="display: block;">\
|
||||
<div class="mb10">\
|
||||
<button class="btn btn-success btn-sm" data-index="0" onclick="addBalance()">添加负载</button>\
|
||||
<div class="divtable mt10">\
|
||||
<table class="table table-hover">\
|
||||
<thead>\
|
||||
<tr>\
|
||||
<th>网站</th>\
|
||||
<th>负载名称</th>\
|
||||
<th>节点</th>\
|
||||
<th>日志</th>\
|
||||
<th>状态</th>\
|
||||
<th width="100" style="text-align: right;">操作</th>\
|
||||
</tr>\
|
||||
</thead>\
|
||||
<tbody id="nodeTable"></tbody>\
|
||||
</table>\
|
||||
<div class="nodeTablePage page" data-type="nodePage">\
|
||||
<div><span class="Pcount">共0条</span></div>\
|
||||
</div>\
|
||||
</div>\
|
||||
</div>\
|
||||
</div>\
|
||||
</div>';
|
||||
$(".soft-man-con").html(body);
|
||||
loadBalanceListRender();
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
local hc = require "resty.upstream.healthcheck"
|
||||
local ok, err = hc.spawn_checker {
|
||||
shm = "healthcheck",
|
||||
type = "http",
|
||||
upstream = "{$UPSTREAM_NAME}",
|
||||
http_req = "GET / HTTP/1.0\r\nHost: {$UPSTREAM_NAME}\r\n\r\n",
|
||||
interval = 2000,
|
||||
timeout = 6000,
|
||||
fall = 3,
|
||||
rise = 2,
|
||||
valid_statuses = {200, 302},
|
||||
concurrency = 20,
|
||||
}
|
||||
|
||||
if not ok then
|
||||
ngx.log(ngx.ERR, "=======> load balance health checker error: ", err)
|
||||
return
|
||||
end
|
||||
+53
-53
@@ -225,6 +225,7 @@ def initTotalInfo():
|
||||
_name[name] = tmp
|
||||
total_contents['sites'] = _name
|
||||
|
||||
total_contents['start_time'] = str(time.time())
|
||||
cjson = mw.getJson(total_contents)
|
||||
mw.writeFile(path_total, cjson)
|
||||
|
||||
@@ -324,8 +325,41 @@ def restartWeb():
|
||||
mw.opWeb('start')
|
||||
|
||||
|
||||
def initDreplace():
|
||||
def makeDstLua():
|
||||
root_init_dir = mw.getServerDir() + '/web_conf/nginx/lua/init_by_lua_file'
|
||||
root_worker_dir = mw.getServerDir() + '/web_conf/nginx/lua/init_worker_by_lua_file'
|
||||
root_access_dir = mw.getServerDir() + '/web_conf/nginx/lua/access_by_lua_file'
|
||||
path = getServerDir()
|
||||
path_tpl = getPluginDir()
|
||||
|
||||
waf_common_tpl = path_tpl + "/waf/lua/waf_common.lua"
|
||||
waf_common_dst = path + "/waf/lua/waf_common.lua"
|
||||
content = mw.readFile(waf_common_tpl)
|
||||
content = contentReplace(content)
|
||||
mw.writeFile(waf_common_dst, content)
|
||||
|
||||
waf_init_tpl = path_tpl + "/waf/lua/init_preload.lua"
|
||||
waf_init_dst = root_init_dir + "/waf_init_preload.lua"
|
||||
content = mw.readFile(waf_init_tpl)
|
||||
content = contentReplace(content)
|
||||
mw.writeFile(waf_init_dst, content)
|
||||
|
||||
init_worker_tpl = path_tpl + "/waf/lua/init_worker.lua"
|
||||
init_worker_dst = root_worker_dir + '/opwaf_init_worker.lua'
|
||||
content = mw.readFile(init_worker_tpl)
|
||||
content = contentReplace(content)
|
||||
mw.writeFile(init_worker_dst, content)
|
||||
|
||||
access_file_tpl = path_tpl + "/waf/lua/init.lua"
|
||||
access_file_dst = root_access_dir + '/opwaf_init.lua'
|
||||
content = mw.readFile(access_file_tpl)
|
||||
content = contentReplace(content)
|
||||
mw.writeFile(access_file_dst, content)
|
||||
|
||||
mw.opLuaMakeAll()
|
||||
|
||||
|
||||
def initDreplace():
|
||||
path = getServerDir()
|
||||
if not os.path.exists(path + '/waf/lua'):
|
||||
sdir = getPluginDir() + '/waf'
|
||||
@@ -348,20 +382,7 @@ def initDreplace():
|
||||
content['reqfile_path'] = wfDir
|
||||
mw.writeFile(config, mw.getJson(content))
|
||||
|
||||
config = path + "/waf/lua/init.lua"
|
||||
content = mw.readFile(config)
|
||||
content = contentReplace(content)
|
||||
mw.writeFile(config, content)
|
||||
|
||||
config_common = path + "/waf/lua/common.lua"
|
||||
content = mw.readFile(config_common)
|
||||
content = contentReplace(content)
|
||||
mw.writeFile(config_common, content)
|
||||
|
||||
init_worker = path + "/waf/lua/init_worker.lua"
|
||||
content = mw.readFile(init_worker)
|
||||
content = contentReplace(content)
|
||||
mw.writeFile(init_worker, content)
|
||||
makeDstLua()
|
||||
|
||||
waf_conf = dstWafConf()
|
||||
if not os.path.exists(waf_conf):
|
||||
@@ -396,16 +417,6 @@ def status():
|
||||
def start():
|
||||
initDreplace()
|
||||
|
||||
path = mw.getServerDir() + '/web_conf/nginx/lua/lua.conf'
|
||||
init_worker_lua = getServerDir() + '/waf/lua/init_worker.lua'
|
||||
init_lua = getServerDir() + '/waf/lua/init.lua'
|
||||
conf = mw.readFile(path)
|
||||
conf = re.sub('init_worker_by_lua_file (.*);',
|
||||
"init_worker_by_lua_file " + init_worker_lua + ";", conf)
|
||||
conf = re.sub('access_by_lua_file (.*);',
|
||||
"access_by_lua_file " + init_lua + ";", conf)
|
||||
mw.writeFile(path, conf)
|
||||
|
||||
import tool_task
|
||||
tool_task.createBgTask()
|
||||
|
||||
@@ -414,15 +425,21 @@ def start():
|
||||
|
||||
|
||||
def stop():
|
||||
root_init_dir = mw.getServerDir() + '/web_conf/nginx/lua/init_by_lua_file'
|
||||
root_worker_dir = mw.getServerDir() + '/web_conf/nginx/lua/init_worker_by_lua_file'
|
||||
root_access_dir = mw.getServerDir() + '/web_conf/nginx/lua/access_by_lua_file'
|
||||
|
||||
path = mw.getServerDir() + '/web_conf/nginx/lua/lua.conf'
|
||||
empty_lua = mw.getServerDir() + '/web_conf/nginx/lua/empty.lua'
|
||||
conf = mw.readFile(path)
|
||||
conf = re.sub('init_worker_by_lua_file (.*);',
|
||||
"init_worker_by_lua_file " + empty_lua + ";", conf)
|
||||
conf = re.sub('access_by_lua_file (.*);',
|
||||
"access_by_lua_file " + empty_lua + ";", conf)
|
||||
mw.writeFile(path, conf)
|
||||
waf_init_dst = root_init_dir + "/waf_init_preload.lua"
|
||||
if os.path.exists(waf_init_dst):
|
||||
os.remove(waf_init_dst)
|
||||
|
||||
init_worker_dst = root_worker_dir + '/opwaf_init_worker.lua'
|
||||
if os.path.exists(init_worker_dst):
|
||||
os.remove(init_worker_dst)
|
||||
|
||||
access_file_dst = root_access_dir + '/opwaf_init.lua'
|
||||
if os.path.exists(access_file_dst):
|
||||
os.remove(access_file_dst)
|
||||
|
||||
wafconf = dstWafConf()
|
||||
if os.path.exists(wafconf):
|
||||
@@ -431,6 +448,8 @@ def stop():
|
||||
import tool_task
|
||||
tool_task.removeBgTask()
|
||||
|
||||
mw.opLuaMakeAll()
|
||||
|
||||
restartWeb()
|
||||
return 'ok'
|
||||
|
||||
@@ -443,26 +462,7 @@ def restart():
|
||||
def reload():
|
||||
stop()
|
||||
|
||||
path = getServerDir()
|
||||
path_tpl = getPluginDir()
|
||||
|
||||
config = path + "/waf/lua/common.lua"
|
||||
config_tpl = path_tpl + "/waf/lua/common.lua"
|
||||
content = mw.readFile(config_tpl)
|
||||
content = contentReplace(content)
|
||||
mw.writeFile(config, content)
|
||||
|
||||
config = path + "/waf/lua/init_worker.lua"
|
||||
config_tpl = path_tpl + "/waf/lua/init_worker.lua"
|
||||
content = mw.readFile(config_tpl)
|
||||
content = contentReplace(content)
|
||||
mw.writeFile(config, content)
|
||||
|
||||
config = path + "/waf/lua/init.lua"
|
||||
config_tpl = path_tpl + "/waf/lua/init.lua"
|
||||
content = mw.readFile(config_tpl)
|
||||
content = contentReplace(content)
|
||||
mw.writeFile(config, content)
|
||||
makeDstLua()
|
||||
|
||||
errlog = mw.getServerDir() + "/openresty/nginx/logs/error.log"
|
||||
mw.execShell('rm -rf ' + errlog)
|
||||
|
||||
@@ -1,4 +1,16 @@
|
||||
{
|
||||
"hook":[
|
||||
{
|
||||
"tag":"site_cb",
|
||||
"site_cb": {
|
||||
"title":"网站统计",
|
||||
"name":"op_waf",
|
||||
"add":{"func":"reload"},
|
||||
"update":{"func":"reload"},
|
||||
"delete":{"func":"reload"}
|
||||
}
|
||||
}
|
||||
],
|
||||
"title":"OP防火墙",
|
||||
"tip":"soft",
|
||||
"name":"op_waf",
|
||||
@@ -12,5 +24,5 @@
|
||||
"home":"https://github.com/loveshell/ngx_lua_waf",
|
||||
"date":"2019-04-21",
|
||||
"pid": "1",
|
||||
"versions": ["0.2.4"]
|
||||
"versions": ["0.2.6"]
|
||||
}
|
||||
@@ -24,7 +24,7 @@ else
|
||||
fi
|
||||
|
||||
|
||||
Install_of(){
|
||||
Install_App(){
|
||||
|
||||
echo '正在安装脚本文件...' > $install_tmp
|
||||
mkdir -p $serverPath/source/op_waf
|
||||
@@ -41,7 +41,9 @@ Install_of(){
|
||||
cd $serverPath/source/op_waf && tar xvf luarocks-3.5.0.tar.gz
|
||||
# cd luarocks-3.9.1 && ./configure && make bootstrap
|
||||
|
||||
cd luarocks-3.5.0 && ./configure --prefix=$serverPath/op_waf/luarocks --with-lua-include=$serverPath/openresty/luajit/include/luajit-2.1 --with-lua-bin=$serverPath/openresty/luajit/bin
|
||||
cd luarocks-3.5.0 && ./configure --prefix=$serverPath/op_waf/luarocks \
|
||||
--with-lua-include=$serverPath/openresty/luajit/include/luajit-2.1 \
|
||||
--with-lua-bin=$serverPath/openresty/luajit/bin
|
||||
make -I${serverPath}/openresty/luajit/bin
|
||||
make install
|
||||
fi
|
||||
@@ -85,13 +87,10 @@ Install_of(){
|
||||
echo 'install ok' > $install_tmp
|
||||
|
||||
cd ${rootPath} && python3 ${rootPath}/plugins/op_waf/index.py start
|
||||
|
||||
|
||||
|
||||
# cd ${rootPath} && python3 ${rootPath}/plugins/op_waf/index.py restart
|
||||
}
|
||||
|
||||
Uninstall_of(){
|
||||
Uninstall_App(){
|
||||
|
||||
cd ${rootPath} && python3 ${rootPath}/plugins/op_waf/index.py stop
|
||||
if [ "$?" == "0" ];then
|
||||
@@ -102,7 +101,7 @@ Uninstall_of(){
|
||||
|
||||
action=$1
|
||||
if [ "${1}" == 'install' ];then
|
||||
Install_of
|
||||
Install_App
|
||||
else
|
||||
Uninstall_of
|
||||
Uninstall_App
|
||||
fi
|
||||
|
||||
@@ -961,8 +961,12 @@ function wafScreen(){
|
||||
owPost('waf_srceen', {}, function(data){
|
||||
var rdata = $.parseJSON(data.data);
|
||||
|
||||
var end_time = Date.now();
|
||||
var cos_time = (end_time/1000) - parseInt(rdata['start_time']);
|
||||
var cos_day = parseInt(parseInt(cos_time)/86400);
|
||||
|
||||
var con = '<div class="wavbox alert alert-success" style="margin-right:16px">总拦截<span>'+rdata.total+'</span>次</div>';
|
||||
con += '<div class="wavbox alert alert-info" style="margin-right:16px">安全防护<span>0</span>天</div>';
|
||||
con += '<div class="wavbox alert alert-info" style="margin-right:16px">安全防护<span>'+cos_day+'</span>天</div>';
|
||||
|
||||
con += '<div class="screen">\
|
||||
<div class="line"><span class="name">POST渗透</span><span class="val">'+rdata.rules.post+'</span></div>\
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
#!/bin/bash
|
||||
|
||||
DST_DIR=/www/server/op_waf
|
||||
DIS_FILE=${DST_DIR}/cpu.info
|
||||
|
||||
CPU_USAGE=`top -bn 1 | fgrep 'Cpu(s)' | awk '{print 100 -$8}' | awk -F . '{print $1}'`
|
||||
echo $CPU_USAGE
|
||||
echo $CPU_USAGE > $DIS_FILE
|
||||
echo "done success!"
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user