Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
894c98702e | ||
|
|
b39d95d29e | ||
|
|
1c66f691a0 | ||
|
|
aaaea5cb44 | ||
|
|
349a7b9496 | ||
|
|
2c6d70f1d9 | ||
|
|
12b8afb7e9 | ||
|
|
01664afc0c | ||
|
|
5c059ffca5 | ||
|
|
d122f7812a | ||
|
|
fc5174ba64 | ||
|
|
9ad00f3ce7 | ||
|
|
609a4ed351 | ||
|
|
5ca74fd312 | ||
|
|
3b1bb06c68 | ||
|
|
b6b7de42d6 | ||
|
|
3aa3b532ac | ||
|
|
290c740ab1 | ||
|
|
23fd4ca1de | ||
|
|
c128a799b2 | ||
|
|
401c103b76 | ||
|
|
6fb88eb428 | ||
|
|
956f20d295 | ||
|
|
03f8578a37 | ||
|
|
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 | ||
|
|
ae0a6b5079 | ||
|
|
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 | ||
|
|
e129051a06 | ||
|
|
c696742866 | ||
|
|
046389df99 | ||
|
|
3e244ed227 | ||
|
|
5016aedb0f | ||
|
|
c1cd45a5f3 | ||
|
|
79c9865a5d | ||
|
|
4082f29424 | ||
|
|
d777a29d8c | ||
|
|
dffe9a77e7 | ||
|
|
1b1462586e | ||
|
|
f11d2db761 | ||
|
|
842c16540a | ||
|
|
614cc1032f | ||
|
|
ad46b2050e | ||
|
|
8109989927 | ||
|
|
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 | ||
|
|
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 | ||
|
|
042fd7ac6d | ||
|
|
1a48f52dad | ||
|
|
e758698ee2 | ||
|
|
088346fd5e | ||
|
|
cbcb686363 | ||
|
|
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 | ||
|
|
696797f125 | ||
|
|
fcfad78506 | ||
|
|
4787d6e7bb | ||
|
|
80ef6dbe4c | ||
|
|
78cdad5fb8 | ||
|
|
274f0638ac | ||
|
|
1e97b146db | ||
|
|
fbbc506de9 | ||
|
|
58ec4c2c30 | ||
|
|
a2298ade04 | ||
|
|
d5fc464603 | ||
|
|
c952be4e45 | ||
|
|
17a92518b4 | ||
|
|
c7154eea7b | ||
|
|
7cab1492c0 | ||
|
|
77b6391d9e | ||
|
|
08a91ed0f2 | ||
|
|
b4d090021c | ||
|
|
bf064d89f4 | ||
|
|
7314071634 | ||
|
|
49a606baa2 | ||
|
|
aaabe5628e | ||
|
|
ea03373363 | ||
|
|
515d25648f | ||
|
|
a30792178a | ||
|
|
1c74ce3c58 | ||
|
|
832f3b45eb | ||
|
|
2c3ad7ea99 | ||
|
|
6b3dae6169 | ||
|
|
d387546986 | ||
|
|
6929c5a173 | ||
|
|
d66e9d7fc6 | ||
|
|
17470600e9 | ||
|
|
d1017ddb5c | ||
|
|
67161e62ce | ||
|
|
87ad51f6ad | ||
|
|
2239d178f4 | ||
|
|
4ff95f057c | ||
|
|
f32ba65596 | ||
|
|
d3db79a4ed | ||
|
|
ddc8cde44c | ||
|
|
719ad042c7 | ||
|
|
9134dfb1a9 | ||
|
|
9611834a9d | ||
|
|
8d5cc2e926 | ||
|
|
01b9c51247 | ||
|
|
21ca9d8874 | ||
|
|
d2d5a0e12f | ||
|
|
ad2e9958f8 | ||
|
|
7cf64673f9 | ||
|
|
c49bc6f823 | ||
|
|
24e6c57eec | ||
|
|
a6842f2679 | ||
|
|
42ba0ea285 | ||
|
|
19e6055b7c | ||
|
|
1388c697e0 | ||
|
|
2a97c0f8b3 | ||
|
|
8aa4084373 | ||
|
|
4e16773ed8 | ||
|
|
b4b9b6eb1f | ||
|
|
b35ae736a3 | ||
|
|
ec78197317 | ||
|
|
1558e93f33 | ||
|
|
99a15ffc82 | ||
|
|
2b0b697a6a | ||
|
|
a93953a61d | ||
|
|
1ce68581af | ||
|
|
df5979a4cf | ||
|
|
9197dcbe03 | ||
|
|
4100824c43 | ||
|
|
c22b2b3774 | ||
|
|
0fbd3163e2 | ||
|
|
500171ed66 | ||
|
|
1c6b6f1090 | ||
|
|
fc8a54c4b3 | ||
|
|
4ade42380d | ||
|
|
6b2c21ab47 | ||
|
|
6f5ea82a58 | ||
|
|
73c849931c | ||
|
|
39d76f5621 | ||
|
|
bc435ea848 | ||
|
|
ed82dd3c12 | ||
|
|
0a5a86625f | ||
|
|
53f56c949b | ||
|
|
7e61e37d80 | ||
|
|
c05d0244db | ||
|
|
226d0addc5 | ||
|
|
35cce35ff3 | ||
|
|
9450103c4b | ||
|
|
4478f005e7 | ||
|
|
71be0e3196 | ||
|
|
e6a79216c4 | ||
|
|
3f12d4b8d6 | ||
|
|
683f041b1d | ||
|
|
e08cebd48e | ||
|
|
d02dea3150 | ||
|
|
18bb92c9e2 | ||
|
|
f9b45ee4d9 | ||
|
|
dcbc86ea5c | ||
|
|
300728cb17 | ||
|
|
51aa028f97 | ||
|
|
e30ac33cea | ||
|
|
1c2f3c4378 | ||
|
|
17eadfccff | ||
|
|
b55a6d3bd9 | ||
|
|
48936e7918 | ||
|
|
1a99c53cb6 | ||
|
|
a8b5df25d0 | ||
|
|
73b4be90f5 | ||
|
|
1ecf12ab5c | ||
|
|
758da44141 | ||
|
|
8aa44408cc | ||
|
|
ccf4ab7d81 | ||
|
|
61766a8942 | ||
|
|
ce9ac280c7 | ||
|
|
94bd8cbe8e | ||
|
|
705f2445ee | ||
|
|
bc8d16f2c2 | ||
|
|
fe9eb7dc38 | ||
|
|
94195b5246 | ||
|
|
24009267be | ||
|
|
c28412aea5 | ||
|
|
847b9d569b | ||
|
|
b3c3a4457d | ||
|
|
89a84f5ca0 | ||
|
|
4432fa15cc | ||
|
|
c6a600299d | ||
|
|
082b0ebcc3 | ||
|
|
828436b772 | ||
|
|
658a727fe3 | ||
|
|
bedd6e5409 | ||
|
|
ef8bc8bf57 | ||
|
|
a22f89797a | ||
|
|
eaf341cda6 | ||
|
|
58d69d734d | ||
|
|
e19f7702db | ||
|
|
b546a39f29 | ||
|
|
81b570214a | ||
|
|
44401f5944 | ||
|
|
4ad190586a | ||
|
|
25bca124b4 | ||
|
|
98165bb449 | ||
|
|
6693edb924 | ||
|
|
bae1beec3e | ||
|
|
0682ca4d4d | ||
|
|
5bc10bebd0 | ||
|
|
25df8b430b | ||
|
|
4f8fc31ca3 | ||
|
|
a44f54e719 | ||
|
|
12068046ec | ||
|
|
d6f57e5ea7 | ||
|
|
869d7795a6 | ||
|
|
0a3d976c6a | ||
|
|
c4d39ef9fb | ||
|
|
8e5b499601 | ||
|
|
e6dff6094c | ||
|
|
47dec45a27 | ||
|
|
4706af7423 | ||
|
|
1bc458eb8f | ||
|
|
dc3f872592 | ||
|
|
3d48289329 | ||
|
|
6126539929 | ||
|
|
cff5df6d13 | ||
|
|
23a0b74f9d | ||
|
|
5927a3abab | ||
|
|
4b176ad702 | ||
|
|
577103506d | ||
|
|
18f8d2b905 | ||
|
|
3a6b5339d2 | ||
|
|
c25d4305f7 | ||
|
|
b6ef87e97b | ||
|
|
4335668cd4 | ||
|
|
0fc2ab011d | ||
|
|
683035e6b7 | ||
|
|
f2dfa7ad48 | ||
|
|
2ae2c188e7 | ||
|
|
b5cd0564cb | ||
|
|
2f274ce202 | ||
|
|
0a67932473 | ||
|
|
7b7cb215c4 | ||
|
|
f6e98ae286 | ||
|
|
5958f6e067 | ||
|
|
6dadfb1019 | ||
|
|
f5aeb5664b | ||
|
|
ca1080937d | ||
|
|
1dd934ea3f | ||
|
|
a34512d057 | ||
|
|
575c1e48ae | ||
|
|
9f3ee4997b | ||
|
|
cfaa2d9f25 | ||
|
|
aded1fc731 | ||
|
|
4a6e847743 | ||
|
|
24fac51538 | ||
|
|
148b32fed3 | ||
|
|
fff19d2c4a | ||
|
|
a5e0332179 | ||
|
|
1349655570 | ||
|
|
b9e617bf0f | ||
|
|
f65d10e1e4 | ||
|
|
6d9ec087ce | ||
|
|
bfb9da0471 | ||
|
|
8a95a68c81 | ||
|
|
d9d81ded3d | ||
|
|
403660c980 | ||
|
|
ac06cfc83c | ||
|
|
c50012b9b5 | ||
|
|
baeb0ddc8f | ||
|
|
83f8c99275 | ||
|
|
2d2b454a31 | ||
|
|
aad41ce14b | ||
|
|
ebf8c82d33 | ||
|
|
d25100fec3 | ||
|
|
fe658d9009 | ||
|
|
f37c2c9bc6 | ||
|
|
a1550765aa | ||
|
|
b020f23161 | ||
|
|
4afe2061e2 | ||
|
|
ab64c01981 | ||
|
|
8a97734700 | ||
|
|
16b4c64aa5 | ||
|
|
df2a5919af | ||
|
|
40a81b794c | ||
|
|
8fc7ab3abf | ||
|
|
ca4205c89a | ||
|
|
c89b15a645 | ||
|
|
7a863c4544 | ||
|
|
5d81406fdf | ||
|
|
cce59c0521 | ||
|
|
0a5c5aae12 | ||
|
|
7478426206 | ||
|
|
c0ed736edb | ||
|
|
313eb25ccd | ||
|
|
f58595755b | ||
|
|
18a892a576 | ||
|
|
03d558092f | ||
|
|
3cffa9223a | ||
|
|
c0d5aff2f5 | ||
|
|
3fa4c7804b | ||
|
|
22acce8bc0 | ||
|
|
e53e5cd672 | ||
|
|
a7d861de22 | ||
|
|
ddeef3c17e | ||
|
|
af733ac28a | ||
|
|
148e2021dc | ||
|
|
50c3fe58f4 | ||
|
|
5ed3728d4f | ||
|
|
9bf06c9d21 | ||
|
|
08b3738cfd | ||
|
|
a04f59a3aa | ||
|
|
155b8d2b4c | ||
|
|
039d2794ab | ||
|
|
0d2721523c | ||
|
|
f042665c05 | ||
|
|
0118548c24 | ||
|
|
1ba6625525 | ||
|
|
293f8ab0af | ||
|
|
dc68868883 | ||
|
|
30f835b6b8 | ||
|
|
4d90fdfe4f | ||
|
|
bb4067aa6b | ||
|
|
5dbf89b872 | ||
|
|
cc244a8ea5 | ||
|
|
975cb988f7 | ||
|
|
5a11d1bff9 | ||
|
|
bab3501722 | ||
|
|
872d68e0f1 | ||
|
|
9a5878a90c | ||
|
|
e5e226b57a | ||
|
|
5d12218a3e | ||
|
|
7b3bbc8bdd | ||
|
|
67030e91f6 | ||
|
|
c03eb0aa40 | ||
|
|
2b5aaba0b7 | ||
|
|
c7a01b8c52 | ||
|
|
9bfb72ada5 | ||
|
|
5ab8936f6b | ||
|
|
43c5161085 | ||
|
|
7d90a6e99f | ||
|
|
9ba8028944 | ||
|
|
36fa3d7630 | ||
|
|
b6a2d7e3e8 | ||
|
|
99e73d1bda | ||
|
|
1083b46078 | ||
|
|
8bd081804d | ||
|
|
2536a79481 | ||
|
|
eab11680e5 | ||
|
|
651b7d6d1b | ||
|
|
75e181116e | ||
|
|
c0a981b5a1 | ||
|
|
61dc601d48 | ||
|
|
415a2a1f8f | ||
|
|
7c06a85ac7 | ||
|
|
7c8329bb78 | ||
|
|
5c8a50429f | ||
|
|
300bdc1eb1 | ||
|
|
a8a9e9033e | ||
|
|
77425dfc48 | ||
|
|
52eae4625f | ||
|
|
dafcf60633 | ||
|
|
79c1e81d2c | ||
|
|
b779a18de6 | ||
|
|
45f22d239a | ||
|
|
f7b688d6bf | ||
|
|
6f069c715a | ||
|
|
98ed286b50 | ||
|
|
e931fa526f | ||
|
|
ff17610732 | ||
|
|
13bbe07ac5 | ||
|
|
5380cd805c | ||
|
|
c0b056c18d | ||
|
|
e287086b36 | ||
|
|
8647300e16 | ||
|
|
636f7af8ef | ||
|
|
87cf208dfc | ||
|
|
579a84c6f1 | ||
|
|
54b90f4484 | ||
|
|
ebdaf5d395 | ||
|
|
1e5ecabbd1 | ||
|
|
0596dc243e | ||
|
|
7fa6ec7cd3 | ||
|
|
c21d8eed8b | ||
|
|
326f9f0e6b | ||
|
|
60c94f120e | ||
|
|
55c08a8916 | ||
|
|
cf944fb976 | ||
|
|
cda1ea93a8 | ||
|
|
46f02cae0d | ||
|
|
034d00a262 | ||
|
|
fe1a68208b | ||
|
|
f364cc4e2d | ||
|
|
5b35b5f38c | ||
|
|
2151b2c597 | ||
|
|
4f8e95210f | ||
|
|
b31297b47f | ||
|
|
e492a87160 | ||
|
|
47d6eb7993 | ||
|
|
3ddccea897 | ||
|
|
069d594e49 | ||
|
|
7c49609a2b | ||
|
|
70d260dd5a | ||
|
|
58503722c0 | ||
|
|
0a98b4dc09 | ||
|
|
5ab0f493e4 | ||
|
|
30580874fd | ||
|
|
26327d958c | ||
|
|
522eddd393 | ||
|
|
935df8d4d1 | ||
|
|
15b4ba9cbb | ||
|
|
c005e1f56d | ||
|
|
edbca430ee | ||
|
|
f8fedb8285 | ||
|
|
d9472709fd | ||
|
|
79bf1eae07 | ||
|
|
fab6df7097 | ||
|
|
b9416d58f9 | ||
|
|
96d86ce49e | ||
|
|
46f2d9f029 | ||
|
|
9d5fb77123 | ||
|
|
d0c903bf11 | ||
|
|
3c1b0e6708 | ||
|
|
340b72591a | ||
|
|
8f6a194a02 | ||
|
|
7e65993978 | ||
|
|
ca27a2d49d | ||
|
|
b941c9c71b | ||
|
|
e68c7b0346 | ||
|
|
c7c3d8fdb9 | ||
|
|
de383e0584 | ||
|
|
048463cf9d | ||
|
|
0c118613d5 | ||
|
|
aa8e9f173b | ||
|
|
c21c54198c | ||
|
|
e8ad9995d7 | ||
|
|
7a0b6be401 | ||
|
|
8b2a127ceb | ||
|
|
8a23fb18ff | ||
|
|
234eca3729 | ||
|
|
9c74eb6a77 | ||
|
|
0093af2661 | ||
|
|
482d9bdd86 | ||
|
|
d8f9b179ad | ||
|
|
d179328976 | ||
|
|
9aaa5e2a65 | ||
|
|
821a9ce2d1 | ||
|
|
7b39b28dcd | ||
|
|
c881ad7d18 | ||
|
|
82da5c2649 | ||
|
|
315ba990ec | ||
|
|
992fcb7e6f | ||
|
|
d7337239f5 | ||
|
|
224b91fe0b | ||
|
|
589dfe6da2 | ||
|
|
b1b47c3c38 | ||
|
|
106f5d897d | ||
|
|
c856d09b3b | ||
|
|
6bf8792bb6 | ||
|
|
75edb074b2 | ||
|
|
4a5df99f06 | ||
|
|
2c7536a138 | ||
|
|
a758fc2264 | ||
|
|
0d94fe2d26 | ||
|
|
630bdd5f96 | ||
|
|
411fc670fc | ||
|
|
cab35c7c28 | ||
|
|
b3c8a6365f | ||
|
|
b64f02ff2f | ||
|
|
7e9df9640e | ||
|
|
6c478be4df | ||
|
|
e919fac1d5 | ||
|
|
4854142f9a | ||
|
|
f37de275ba | ||
|
|
0c8f777452 | ||
|
|
9235affadf | ||
|
|
2001a3b5dc | ||
|
|
6ba1e4c19c | ||
|
|
231048a4a3 | ||
|
|
6b9aab3759 | ||
|
|
e8a152b1a3 | ||
|
|
1cf8e4ecb7 | ||
|
|
aa04235446 | ||
|
|
3acbc36297 | ||
|
|
85aba4f1ce | ||
|
|
f33f80cf34 | ||
|
|
1868b3197d | ||
|
|
b8e1fb2ca2 | ||
|
|
9aa9c4f799 | ||
|
|
344603f1b2 | ||
|
|
71b43e9f2f | ||
|
|
adba50bfc1 | ||
|
|
d917fa76f6 | ||
|
|
32971ccead | ||
|
|
c7a3481b95 | ||
|
|
8915b672e5 | ||
|
|
e472d104d1 | ||
|
|
48ee5574c7 | ||
|
|
706afbd681 | ||
|
|
70ca6d0459 | ||
|
|
f344a32fa7 | ||
|
|
e6123000ac | ||
|
|
b94361c59f | ||
|
|
4a7efb67d4 | ||
|
|
536b7ac997 | ||
|
|
0f5b055aeb | ||
|
|
7d81a59c8f | ||
|
|
2540cd1308 | ||
|
|
5358cd2d38 | ||
|
|
c4bb20ed54 | ||
|
|
7f2f307131 | ||
|
|
7a8ef9fe00 | ||
|
|
e1abc66179 | ||
|
|
8bda20c3d0 | ||
|
|
3c01838df1 | ||
|
|
3b459e0349 | ||
|
|
d3e7684f04 | ||
|
|
b2e6e7f256 | ||
|
|
ca6812561a | ||
|
|
44e8079db6 | ||
|
|
9bd063cc1d | ||
|
|
1b0044e28a | ||
|
|
9b752cf24f | ||
|
|
cf5f98958c | ||
|
|
c46872dab5 | ||
|
|
b57edb7b87 | ||
|
|
595cc30c6f | ||
|
|
4b6f5a5f4e | ||
|
|
741da10906 | ||
|
|
9965786976 | ||
|
|
f083696131 | ||
|
|
dd2105d9ab | ||
|
|
c1574f3430 | ||
|
|
6394ead5b8 |
@@ -11,8 +11,7 @@ Debian x, Centos x, Centos x stream, Ubuntu x?
|
||||
|
||||
Ex:
|
||||
```bash
|
||||
................start mw success
|
||||
bash: line 149: ./scripts/init.d/mw: No such file or directory
|
||||
一个issue,一个问题。多余问题,视情况而答。
|
||||
```
|
||||
|
||||
## 错误截图 ?
|
||||
|
||||
@@ -106,6 +106,7 @@ venv.bak/
|
||||
.idea/*.iml
|
||||
tmp/*
|
||||
*.swp
|
||||
*.pem
|
||||
*.zip
|
||||
logs/*
|
||||
data/*.db
|
||||
@@ -116,13 +117,15 @@ data/json/config.json
|
||||
data/site.pl
|
||||
data/admin_path.pl
|
||||
data/close.pl
|
||||
ssl/input.pl
|
||||
data/datadir.pl
|
||||
data/502Task.pl
|
||||
data/default.pl
|
||||
data/backup.pl
|
||||
data/debug.pl
|
||||
data/default_site.pl
|
||||
|
||||
ssl/input.pl
|
||||
|
||||
lib/python*
|
||||
|
||||
bin
|
||||
@@ -148,11 +151,21 @@ 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/system_safe
|
||||
plugins/tamper_proof
|
||||
plugins/tamper_proof_*
|
||||
plugins/cryptocurrency_trade
|
||||
plugins/op_load_balance
|
||||
plugins/gdrive
|
||||
plugins/mtproxy
|
||||
plugins/zimg
|
||||
@@ -161,6 +174,9 @@ plugins/mail
|
||||
plugins/fastdfs
|
||||
plugins/v2ray
|
||||
plugins/frp
|
||||
plugins/file_search
|
||||
debug.out
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,201 +0,0 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [midoks] [midoks of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
@@ -1,194 +0,0 @@
|
||||
<p align="center">
|
||||
<img alt="logo" src="https://cdn.jsdelivr.net/gh/midoks/mdserver-web@latest/route/static/logo.png" height="140" />
|
||||
<h3 align="center">mdserver-web</h3>
|
||||
<p align="center">一款简单Linux面板服务</p>
|
||||
</p>
|
||||
|
||||
### 简介
|
||||
|
||||
简单的Linux面板,感谢BT.CN写出如此好的web管理软件。我一看到,就知道这是我一直想要的页面化管理方式。
|
||||
复制了后台管理界面,按照自己想要的方式写了一版。
|
||||
|
||||
|
||||

|
||||

|
||||

|
||||

|
||||
|
||||
[](https://github.com/midoks/mdserver-web/wiki)
|
||||
[](https://www.jsdelivr.com/package/gh/midoks/mdserver-web)
|
||||
|
||||
* SSH终端工具
|
||||
* 面板收藏功能
|
||||
* 网站子目录绑定
|
||||
* 网站备份功能
|
||||
* 插件方式管理
|
||||
|
||||
基本上可以使用,后续会继续优化!欢迎提供意见!
|
||||
|
||||
- 吹水组 - https://t.me/mdserver_web
|
||||
- [兼容性测试报告](/compatibility.md)
|
||||
- [常用命令说明](/cmd.md)
|
||||
|
||||
### 主要插件介绍
|
||||
|
||||
* OpenResty - 轻量级,占有内存少,并发能力强。
|
||||
* PHP[53-82] - PHP是世界上最好的编程语言。
|
||||
* MySQL - 一种关系数据库管理系统。
|
||||
* MariaDB - 是MySQL的一个重要分支。
|
||||
* MongoDB - 一种非关系NOSQL数据库管理系统。
|
||||
* phpMyAdmin - 著名Web端MySQL管理工具。
|
||||
* Memcached - 一个高性能的分布式内存对象缓存系统。
|
||||
* Redis - 一个高性能的KV数据库。
|
||||
* PureFtpd - 一款专注于程序健壮和软件安全的免费FTP服务器软件。
|
||||
* Gogs - 一款极易搭建的自助Git服务。
|
||||
* Rsyncd - 通用同步服务。
|
||||
|
||||
|
||||
### 插件开发相关
|
||||
|
||||
```
|
||||
插件文档还不完善,如果有不明白的地方提Issue! 我会尽力完善。
|
||||
如果你自己写了插件,想分享出来,也可以提Issue。
|
||||
```
|
||||
|
||||
- 简单例子 - https://github.com/mw-plugin/simple-plugin
|
||||
- 插件地址 - https://github.com/mw-plugin
|
||||
- 开发文档 - https://github.com/midoks/mdserver-web/wiki/插件开发
|
||||
|
||||
|
||||
# Note
|
||||
|
||||
```
|
||||
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]
|
||||
```
|
||||
|
||||
# 特别赞助
|
||||
|
||||
- [找资源 - 阿里云盘资源搜索引擎](https://zhaoziyuan.la/)
|
||||
- [奈飞丝 - 奈飞中国 Netflix MOD首发](https://naifeis.com/index.php#/register?code=k7P7V6Ur)
|
||||
|
||||
# AD - VPS推荐 - 🙏
|
||||
|
||||
- [ZZZ评测](https://www.zzzvps.com/)
|
||||
|
||||
| 服务商 | LOGO | 推广地址 | 优惠码 |
|
||||
| ------------- |----------|-----------|-------|
|
||||
| digitalvirt |[](https://digitalvirt.com/aff.php?aff=154) | https://digitalvirt.com/aff.php?aff=154 | mdserver-web |
|
||||
|
||||
# Docker
|
||||
|
||||
- 由[DDSRem](https://github.com/DDSRem)开发维护。
|
||||
- https://hub.docker.com/r/ddsderek/mw-server
|
||||
|
||||
```
|
||||
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.11.2
|
||||
|
||||
* redis安全强化。
|
||||
* 添加php命令行设置。
|
||||
* 域名绑定。
|
||||
* Gitea优化。
|
||||
* 网站统计优化。
|
||||
* centos9下php-gd安装优化。
|
||||
* php82rc7测试版。
|
||||
|
||||
|
||||
### JSDelivr安装地址
|
||||
|
||||
- 初始安装
|
||||
|
||||
```
|
||||
curl -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
|
||||
```
|
||||
|
||||
- 卸载脚本
|
||||
|
||||
```
|
||||
wget -O uninstall.sh https://cdn.jsdelivr.net/gh/midoks/mdserver-web@latest/scripts/uninstall.sh && bash uninstall.sh
|
||||
```
|
||||
|
||||
### 备用地址
|
||||
|
||||
- 初始安装
|
||||
|
||||
```
|
||||
curl -fsSL https://raw.githubusercontent.com/midoks/mdserver-web/master/scripts/install.sh | bash
|
||||
|
||||
```
|
||||
|
||||
- 直接更新
|
||||
|
||||
```
|
||||
curl -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
|
||||
```
|
||||
|
||||
|
||||
### 通用软件安装[命令行安装]
|
||||
|
||||
- 需已经安装mdserver-web
|
||||
|
||||
```
|
||||
curl -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
|
||||
|
||||
wget -O uninstall.sh https://raw.githubusercontent.com/midoks/mdserver-web/dev/scripts/uninstall.sh && bash uninstall.sh
|
||||
|
||||
curl -fsSL https://gitee.com/midoks/mdserver-web/raw/master/scripts/install_dev.sh | bash
|
||||
curl -fsSL https://gitee.com/midoks/mdserver-web/raw/dev/scripts/update_dev.sh | bash
|
||||
```
|
||||
|
||||
### 捐赠地址 USDT(TRC20)
|
||||
|
||||
TVbNgrpeGBGZVm5gTLa21ADP7RpnPFhjya
|
||||
|
||||
|
||||
### 微信赞助
|
||||
|
||||
[](https://cdn.jsdelivr.net/gh/midoks/mdserver-web@latest/route/static/img/weixin_zz.jpg)
|
||||
|
||||
|
||||
### 无图不真相
|
||||
|
||||
[](https://cdn.jsdelivr.net/gh/midoks/mdserver-web@latest/route/static/mdw.jpg)
|
||||
|
||||
|
||||
### Stargazers over time
|
||||
|
||||
[](https://starchart.cc/midoks/mdserver-web)
|
||||
|
||||
|
||||
### 感谢开发赞助
|
||||
|
||||
[](https://digitalvirt.com/aff.php?aff=154)
|
||||
|
||||
### 授权许可
|
||||
|
||||
本项目采用 Apache 开源授权许可证,完整的授权说明已放置在 [LICENSE](https://github.com/midoks/mdserver-web/blob/master/LICENSE) 文件中。
|
||||
|
||||
@@ -1,49 +0,0 @@
|
||||
# coding:utf-8
|
||||
|
||||
# ---------------------------------------------------------------------------------
|
||||
# MW-Linux面板
|
||||
# ---------------------------------------------------------------------------------
|
||||
# copyright (c) 2018-∞(https://github.com/midoks/mdserver-web) All rights reserved.
|
||||
# ---------------------------------------------------------------------------------
|
||||
# Author: midoks <midoks@163.com>
|
||||
# ---------------------------------------------------------------------------------
|
||||
|
||||
# ---------------------------------------------------------------------------------
|
||||
# 入口文件
|
||||
# ---------------------------------------------------------------------------------
|
||||
|
||||
|
||||
# pip install profiler_online
|
||||
# 性能测试
|
||||
# from profiler_online import run_profiler
|
||||
# run_profiler()
|
||||
import sys
|
||||
import io
|
||||
import os
|
||||
from route import app, socketio
|
||||
|
||||
|
||||
from gevent.pywsgi import WSGIServer
|
||||
from geventwebsocket.handler import WebSocketHandler
|
||||
|
||||
from gevent import monkey
|
||||
monkey.patch_all()
|
||||
|
||||
try:
|
||||
if __name__ == "__main__":
|
||||
|
||||
PORT = 7200
|
||||
if os.path.exists('data/port.pl'):
|
||||
f = open('data/port.pl')
|
||||
PORT = int(f.read())
|
||||
f.close()
|
||||
|
||||
HOST = '0.0.0.0'
|
||||
http_server = WSGIServer(
|
||||
(HOST, PORT), app, handler_class=WebSocketHandler)
|
||||
|
||||
http_server.serve_forever()
|
||||
|
||||
socketio.run(app, host=HOST, port=PORT)
|
||||
except Exception as ex:
|
||||
print(ex)
|
||||
@@ -1,144 +0,0 @@
|
||||
# coding: utf-8
|
||||
|
||||
# ---------------------------------------------------------------------------------
|
||||
# MW-Linux面板
|
||||
# ---------------------------------------------------------------------------------
|
||||
# copyright (c) 2018-∞(https://github.com/midoks/mdserver-web) All rights reserved.
|
||||
# ---------------------------------------------------------------------------------
|
||||
# Author: midoks <midoks@163.com>
|
||||
# ---------------------------------------------------------------------------------
|
||||
|
||||
# ---------------------------------------------------------------------------------
|
||||
# 公共操作
|
||||
# ---------------------------------------------------------------------------------
|
||||
|
||||
import os
|
||||
import sys
|
||||
import time
|
||||
import string
|
||||
import json
|
||||
import hashlib
|
||||
import shlex
|
||||
import datetime
|
||||
import subprocess
|
||||
import re
|
||||
import hashlib
|
||||
from random import Random
|
||||
|
||||
import mw
|
||||
import db
|
||||
|
||||
from flask import redirect
|
||||
|
||||
|
||||
def init():
|
||||
initDB()
|
||||
initUserInfo()
|
||||
initInitD()
|
||||
initInitTask()
|
||||
|
||||
|
||||
def local():
|
||||
result = checkClose()
|
||||
if result:
|
||||
return result
|
||||
|
||||
|
||||
# 检查面板是否关闭
|
||||
def checkClose():
|
||||
if os.path.exists('data/close.pl'):
|
||||
return redirect('/close')
|
||||
|
||||
|
||||
def initDB():
|
||||
try:
|
||||
sql = db.Sql().dbfile('default')
|
||||
csql = mw.readFile('data/sql/default.sql')
|
||||
csql_list = csql.split(';')
|
||||
for index in range(len(csql_list)):
|
||||
sql.execute(csql_list[index], ())
|
||||
|
||||
except Exception as ex:
|
||||
print(str(ex))
|
||||
|
||||
|
||||
def doContentReplace(src, dst):
|
||||
content = mw.readFile(src)
|
||||
content = content.replace("{$SERVER_PATH}", mw.getRunDir())
|
||||
mw.writeFile(dst, content)
|
||||
|
||||
|
||||
def initInitD():
|
||||
|
||||
# systemctl
|
||||
# sysCfgDir = mw.systemdCfgDir()
|
||||
# if os.path.exists(sysCfgDir) and mw.getOsName() == 'centos' and mw.getOsID() == '9':
|
||||
# systemd_mw = sysCfgDir + '/mw.service'
|
||||
# systemd_mw_task = sysCfgDir + '/mw-task.service'
|
||||
|
||||
# systemd_mw_tpl = mw.getRunDir() + '/scripts/init.d/mw.service.tpl'
|
||||
# systemd_mw_task_tpl = mw.getRunDir() + '/scripts/init.d/mw-task.service.tpl'
|
||||
|
||||
# if os.path.exists(systemd_mw):
|
||||
# os.remove(systemd_mw)
|
||||
# if os.path.exists(systemd_mw_task):
|
||||
# os.remove(systemd_mw_task)
|
||||
|
||||
# doContentReplace(systemd_mw_tpl, systemd_mw)
|
||||
# doContentReplace(systemd_mw_task_tpl, systemd_mw_task)
|
||||
|
||||
# mw.execShell('systemctl enable mw')
|
||||
# mw.execShell('systemctl enable mw-task')
|
||||
# mw.execShell('systemctl daemon-reload')
|
||||
|
||||
script = mw.getRunDir() + '/scripts/init.d/mw.tpl'
|
||||
script_bin = mw.getRunDir() + '/scripts/init.d/mw'
|
||||
doContentReplace(script, script_bin)
|
||||
mw.execShell('chmod +x ' + script_bin)
|
||||
|
||||
# 在linux系统中,确保/etc/init.d存在
|
||||
if not mw.isAppleSystem() and not os.path.exists("/etc/rc.d/init.d"):
|
||||
mw.execShell('mkdir -p /etc/rc.d/init.d')
|
||||
|
||||
if not mw.isAppleSystem() and not os.path.exists("/etc/init.d"):
|
||||
mw.execShell('mkdir -p /etc/init.d')
|
||||
|
||||
# initd
|
||||
if os.path.exists('/etc/rc.d/init.d'):
|
||||
initd_bin = '/etc/rc.d/init.d/mw'
|
||||
if not os.path.exists(initd_bin):
|
||||
import shutil
|
||||
shutil.copyfile(script_bin, initd_bin)
|
||||
mw.execShell('chmod +x ' + initd_bin)
|
||||
# 加入自启动
|
||||
mw.execShell('which chkconfig && chkconfig --add mw')
|
||||
|
||||
if os.path.exists('/etc/init.d'):
|
||||
initd_bin = '/etc/init.d/mw'
|
||||
if not os.path.exists(initd_bin):
|
||||
import shutil
|
||||
shutil.copyfile(script_bin, initd_bin)
|
||||
mw.execShell('chmod +x ' + initd_bin)
|
||||
# 加入自启动
|
||||
mw.execShell('which update-rc.d && update-rc.d -f mw defaults')
|
||||
|
||||
# 获取系统IPV4
|
||||
mw.setHostAddr(mw.getLocalIp())
|
||||
|
||||
|
||||
def initInitTask():
|
||||
# 创建证书同步命令
|
||||
import cert_api
|
||||
api = cert_api.cert_api()
|
||||
api.createCertCron()
|
||||
|
||||
|
||||
def initUserInfo():
|
||||
|
||||
data = mw.M('users').where('id=?', (1,)).getField('password')
|
||||
if data == '21232f297a57a5a743894a0e4a801fc3':
|
||||
pwd = mw.getRandomString(8).lower()
|
||||
file_pw = mw.getRunDir() + '/data/default.pl'
|
||||
mw.writeFile(file_pw, pwd)
|
||||
mw.M('users').where('id=?', (1,)).setField(
|
||||
'password', mw.md5(pwd))
|
||||
@@ -1,653 +0,0 @@
|
||||
# coding: utf-8
|
||||
|
||||
# ---------------------------------------------------------------------------------
|
||||
# MW-Linux面板
|
||||
# ---------------------------------------------------------------------------------
|
||||
# copyright (c) 2018-∞(https://github.com/midoks/mdserver-web) All rights reserved.
|
||||
# ---------------------------------------------------------------------------------
|
||||
# Author: midoks <midoks@163.com>
|
||||
# ---------------------------------------------------------------------------------
|
||||
|
||||
# ---------------------------------------------------------------------------------
|
||||
# 配置操作
|
||||
# ---------------------------------------------------------------------------------
|
||||
|
||||
import psutil
|
||||
import time
|
||||
import os
|
||||
import sys
|
||||
import mw
|
||||
import re
|
||||
import json
|
||||
import pwd
|
||||
|
||||
from flask import session
|
||||
from flask import request
|
||||
|
||||
|
||||
class config_api:
|
||||
|
||||
__version = '0.11.2'
|
||||
__api_addr = 'data/api.json'
|
||||
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
def getVersion(self):
|
||||
return self.__version
|
||||
|
||||
##### ----- start ----- ###
|
||||
|
||||
# 取面板列表
|
||||
def getPanelListApi(self):
|
||||
data = mw.M('panel').field(
|
||||
'id,title,url,username,password,click,addtime').order('click desc').select()
|
||||
return mw.getJson(data)
|
||||
|
||||
def addPanelInfoApi(self):
|
||||
title = request.form.get('title', '')
|
||||
url = request.form.get('url', '')
|
||||
username = request.form.get('username', '')
|
||||
password = request.form.get('password', '')
|
||||
# 校验是还是重复
|
||||
isAdd = mw.M('panel').where(
|
||||
'title=? OR url=?', (title, url)).count()
|
||||
if isAdd:
|
||||
return mw.returnJson(False, '备注或面板地址重复!')
|
||||
isRe = mw.M('panel').add('title,url,username,password,click,addtime',
|
||||
(title, url, username, password, 0, int(time.time())))
|
||||
if isRe:
|
||||
return mw.returnJson(True, '添加成功!')
|
||||
return mw.returnJson(False, '添加失败!')
|
||||
|
||||
# 删除面板资料
|
||||
def delPanelInfoApi(self):
|
||||
mid = request.form.get('id', '')
|
||||
isExists = mw.M('panel').where('id=?', (mid,)).count()
|
||||
if not isExists:
|
||||
return mw.returnJson(False, '指定面板资料不存在!')
|
||||
mw.M('panel').where('id=?', (mid,)).delete()
|
||||
return mw.returnJson(True, '删除成功!')
|
||||
|
||||
# 修改面板资料
|
||||
def setPanelInfoApi(self):
|
||||
title = request.form.get('title', '')
|
||||
url = request.form.get('url', '')
|
||||
username = request.form.get('username', '')
|
||||
password = request.form.get('password', '')
|
||||
mid = request.form.get('id', '')
|
||||
# 校验是还是重复
|
||||
isSave = mw.M('panel').where(
|
||||
'(title=? OR url=?) AND id!=?', (title, url, mid)).count()
|
||||
if isSave:
|
||||
return mw.returnJson(False, '备注或面板地址重复!')
|
||||
|
||||
# 更新到数据库
|
||||
isRe = mw.M('panel').where('id=?', (mid,)).save(
|
||||
'title,url,username,password', (title, url, username, password))
|
||||
if isRe:
|
||||
return mw.returnJson(True, '修改成功!')
|
||||
return mw.returnJson(False, '修改失败!')
|
||||
|
||||
def setPanelDomainApi(self):
|
||||
domain = request.form.get('domain', '')
|
||||
|
||||
cfg_domain = 'data/bind_domain.pl'
|
||||
if domain == '':
|
||||
os.remove(cfg_domain)
|
||||
return mw.returnJson(True, '清空域名成功!')
|
||||
|
||||
reg = r"^([\w\-\*]{1,100}\.){1,4}(\w{1,10}|\w{1,10}\.\w{1,10})$"
|
||||
if not re.match(reg, domain):
|
||||
return mw.returnJson(False, '主域名格式不正确')
|
||||
|
||||
mw.writeFile(cfg_domain, domain)
|
||||
return mw.returnJson(True, '设置域名成功!')
|
||||
|
||||
def syncDateApi(self):
|
||||
if mw.isAppleSystem():
|
||||
return mw.returnJson(True, '开发系统不必同步时间!')
|
||||
|
||||
data = mw.execShell('ntpdate -s time.nist.gov')
|
||||
if data[0] == '':
|
||||
return mw.returnJson(True, '同步成功!')
|
||||
return mw.returnJson(False, '同步失败:' + data[0])
|
||||
|
||||
def setPasswordApi(self):
|
||||
password1 = request.form.get('password1', '')
|
||||
password2 = request.form.get('password2', '')
|
||||
if password1 != password2:
|
||||
return mw.returnJson(False, '两次输入的密码不一致,请重新输入!')
|
||||
if len(password1) < 5:
|
||||
return mw.returnJson(False, '用户密码不能小于5位!')
|
||||
mw.M('users').where("username=?", (session['username'],)).setField(
|
||||
'password', mw.md5(password1.strip()))
|
||||
return mw.returnJson(True, '密码修改成功!')
|
||||
|
||||
def setNameApi(self):
|
||||
name1 = request.form.get('name1', '')
|
||||
name2 = request.form.get('name2', '')
|
||||
if name1 != name2:
|
||||
return mw.returnJson(False, '两次输入的用户名不一致,请重新输入!')
|
||||
if len(name1) < 3:
|
||||
return mw.returnJson(False, '用户名长度不能少于3位')
|
||||
|
||||
mw.M('users').where("username=?", (session['username'],)).setField(
|
||||
'username', name1.strip())
|
||||
|
||||
session['username'] = name1
|
||||
return mw.returnJson(True, '用户修改成功!')
|
||||
|
||||
def setWebnameApi(self):
|
||||
webname = request.form.get('webname', '')
|
||||
if webname != mw.getConfig('title'):
|
||||
mw.setConfig('title', webname)
|
||||
return mw.returnJson(True, '面板别名保存成功!')
|
||||
|
||||
def setPortApi(self):
|
||||
port = request.form.get('port', '')
|
||||
if port != mw.getHostPort():
|
||||
import system_api
|
||||
import firewall_api
|
||||
|
||||
if os.path.exists("/lib/systemd/system/firewalld.service"):
|
||||
if not firewall_api.firewall_api().getFwStatus():
|
||||
return mw.returnJson(False, 'firewalld必须先启动!')
|
||||
|
||||
mw.setHostPort(port)
|
||||
|
||||
msg = mw.getInfo('放行端口[{1}]成功', (port,))
|
||||
mw.writeLog("防火墙管理", msg)
|
||||
addtime = time.strftime('%Y-%m-%d %X', time.localtime())
|
||||
mw.M('firewall').add('port,ps,addtime', (port, "配置修改", addtime))
|
||||
|
||||
firewall_api.firewall_api().addAcceptPort(port)
|
||||
firewall_api.firewall_api().firewallReload()
|
||||
|
||||
system_api.system_api().restartMw()
|
||||
|
||||
return mw.returnJson(True, '端口保存成功!')
|
||||
|
||||
def setIpApi(self):
|
||||
host_ip = request.form.get('host_ip', '')
|
||||
if host_ip != mw.getHostAddr():
|
||||
mw.setHostAddr(host_ip)
|
||||
return mw.returnJson(True, 'IP保存成功!')
|
||||
|
||||
def setWwwDirApi(self):
|
||||
sites_path = request.form.get('sites_path', '')
|
||||
if sites_path != mw.getWwwDir():
|
||||
mw.setWwwDir(sites_path)
|
||||
return mw.returnJson(True, '修改默认建站目录成功!')
|
||||
|
||||
def setBackupDirApi(self):
|
||||
backup_path = request.form.get('backup_path', '')
|
||||
if backup_path != mw.getBackupDir():
|
||||
mw.setBackupDir(backup_path)
|
||||
return mw.returnJson(True, '修改默认备份目录成功!')
|
||||
|
||||
def setBasicAuthApi(self):
|
||||
basic_user = request.form.get('basic_user', '').strip()
|
||||
basic_pwd = request.form.get('basic_pwd', '').strip()
|
||||
basic_open = request.form.get('is_open', '').strip()
|
||||
|
||||
salt = '_md_salt'
|
||||
path = 'data/basic_auth.json'
|
||||
is_open = True
|
||||
|
||||
if basic_open == 'false':
|
||||
if os.path.exists(path):
|
||||
os.remove(path)
|
||||
return mw.returnJson(True, '删除BasicAuth成功!')
|
||||
|
||||
if basic_user == '' or basic_pwd == '':
|
||||
return mw.returnJson(True, '用户和密码不能为空!')
|
||||
|
||||
ba_conf = None
|
||||
if os.path.exists(path):
|
||||
try:
|
||||
ba_conf = json.loads(public.readFile(path))
|
||||
except:
|
||||
os.remove(path)
|
||||
|
||||
if not ba_conf:
|
||||
ba_conf = {
|
||||
"basic_user": mw.md5(basic_user + salt),
|
||||
"basic_pwd": mw.md5(basic_pwd + salt),
|
||||
"open": is_open
|
||||
}
|
||||
else:
|
||||
ba_conf['basic_user'] = mw.md5(basic_user + salt)
|
||||
ba_conf['basic_pwd'] = mw.md5(basic_pwd + salt)
|
||||
ba_conf['open'] = is_open
|
||||
|
||||
mw.writeFile(path, json.dumps(ba_conf))
|
||||
os.chmod(path, 384)
|
||||
mw.writeLog('面板设置', '设置BasicAuth状态为: %s' % is_open)
|
||||
|
||||
mw.restartMw()
|
||||
return mw.returnJson(True, '设置成功!')
|
||||
|
||||
def setApi(self):
|
||||
webname = request.form.get('webname', '')
|
||||
port = request.form.get('port', '')
|
||||
host_ip = request.form.get('host_ip', '')
|
||||
domain = request.form.get('domain', '')
|
||||
sites_path = request.form.get('sites_path', '')
|
||||
backup_path = request.form.get('backup_path', '')
|
||||
|
||||
if domain != '':
|
||||
reg = "^([\w\-\*]{1,100}\.){1,4}(\w{1,10}|\w{1,10}\.\w{1,10})$"
|
||||
if not re.match(reg, domain):
|
||||
return mw.returnJson(False, '主域名格式不正确')
|
||||
|
||||
if int(port) >= 65535 or int(port) < 100:
|
||||
return mw.returnJson(False, '端口范围不正确!')
|
||||
|
||||
if webname != mw.getConfig('title'):
|
||||
mw.setConfig('title', webname)
|
||||
|
||||
if sites_path != mw.getWwwDir():
|
||||
mw.setWwwDir(sites_path)
|
||||
|
||||
if backup_path != mw.getWwwDir():
|
||||
mw.setBackupDir(backup_path)
|
||||
|
||||
if port != mw.getHostPort():
|
||||
import system_api
|
||||
import firewall_api
|
||||
|
||||
if os.path.exists("/lib/systemd/system/firewalld.service"):
|
||||
if not firewall_api.firewall_api().getFwStatus():
|
||||
return mw.returnJson(False, 'firewalld必须先启动!')
|
||||
|
||||
mw.setHostPort(port)
|
||||
|
||||
msg = mw.getInfo('放行端口[{1}]成功', (port,))
|
||||
mw.writeLog("防火墙管理", msg)
|
||||
addtime = time.strftime('%Y-%m-%d %X', time.localtime())
|
||||
mw.M('firewall').add('port,ps,addtime', (port, "配置修改", addtime))
|
||||
|
||||
firewall_api.firewall_api().addAcceptPort(port)
|
||||
firewall_api.firewall_api().firewallReload()
|
||||
|
||||
system_api.system_api().restartMw()
|
||||
|
||||
if host_ip != mw.getHostAddr():
|
||||
mw.setHostAddr(host_ip)
|
||||
|
||||
mhost = mw.getHostAddr()
|
||||
info = {
|
||||
'uri': '/config',
|
||||
'host': mhost + ':' + port
|
||||
}
|
||||
return mw.returnJson(True, '保存成功!', info)
|
||||
|
||||
def setAdminPathApi(self):
|
||||
admin_path = request.form.get('admin_path', '').strip()
|
||||
admin_path_checks = ['/', '/close', '/login',
|
||||
'/do_login', '/site', '/sites',
|
||||
'/download_file', '/control', '/crontab',
|
||||
'/firewall', '/files', 'config',
|
||||
'/soft', '/system', '/code',
|
||||
'/ssl', '/plugins', '/hook']
|
||||
if admin_path == '':
|
||||
admin_path = '/'
|
||||
if admin_path != '/':
|
||||
if len(admin_path) < 6:
|
||||
return mw.returnJson(False, '安全入口地址长度不能小于6位!')
|
||||
if admin_path in admin_path_checks:
|
||||
return mw.returnJson(False, '该入口已被面板占用,请使用其它入口!')
|
||||
if not re.match("^/[\w\./-_]+$", admin_path):
|
||||
return mw.returnJson(False, '入口地址格式不正确,示例: /mw_rand')
|
||||
# else:
|
||||
# domain = mw.readFile('data/bind_domain.pl')
|
||||
# if not domain:
|
||||
# domain = ''
|
||||
# limitip = mw.readFile('data/bind_limitip.pl')
|
||||
# if not limitip:
|
||||
# limitip = ''
|
||||
# if not domain.strip() and not limitip.strip():
|
||||
# return mw.returnJson(False,
|
||||
# '警告,关闭安全入口等于直接暴露你的后台地址在外网,十分危险,至少开启以下一种安全方式才能关闭:<a
|
||||
# style="color:red;"><br>1、绑定访问域名<br>2、绑定授权IP</a>')
|
||||
|
||||
admin_path_file = 'data/admin_path.pl'
|
||||
admin_path_old = '/'
|
||||
if os.path.exists(admin_path_file):
|
||||
admin_path_old = mw.readFile(admin_path_file).strip()
|
||||
|
||||
if admin_path_old != admin_path:
|
||||
mw.writeFile(admin_path_file, admin_path)
|
||||
mw.restartMw()
|
||||
return mw.returnJson(True, '修改成功!')
|
||||
|
||||
def closePanelApi(self):
|
||||
filename = 'data/close.pl'
|
||||
if os.path.exists(filename):
|
||||
os.remove(filename)
|
||||
return mw.returnJson(True, '开启成功')
|
||||
mw.writeFile(filename, 'True')
|
||||
mw.execShell("chmod 600 " + filename)
|
||||
mw.execShell("chown root.root " + filename)
|
||||
return mw.returnJson(True, '面板已关闭!')
|
||||
|
||||
def openDebugApi(self):
|
||||
filename = 'data/debug.pl'
|
||||
if os.path.exists(filename):
|
||||
os.remove(filename)
|
||||
return mw.returnJson(True, '开发模式关闭!')
|
||||
mw.writeFile(filename, 'True')
|
||||
return mw.returnJson(True, '开发模式开启!')
|
||||
|
||||
def setIpv6StatusApi(self):
|
||||
ipv6_file = 'data/ipv6.pl'
|
||||
if os.path.exists('data/ipv6.pl'):
|
||||
os.remove(ipv6_file)
|
||||
mw.writeLog('面板设置', '关闭面板IPv6兼容!')
|
||||
else:
|
||||
mw.writeFile(ipv6_file, 'True')
|
||||
mw.writeLog('面板设置', '开启面板IPv6兼容!')
|
||||
mw.restartMw()
|
||||
return mw.returnJson(True, '设置成功!')
|
||||
|
||||
# 获取面板证书
|
||||
def getPanelSslApi(self):
|
||||
cert = {}
|
||||
cert['privateKey'] = mw.readFile('ssl/privateKey.pem')
|
||||
cert['certPem'] = mw.readFile('ssl/certificate.pem')
|
||||
cert['rep'] = os.path.exists('ssl/input.pl')
|
||||
return mw.getJson(cert)
|
||||
|
||||
# 保存面板证书
|
||||
def savePanelSslApi(self):
|
||||
keyPath = 'ssl/privateKey.pem'
|
||||
certPath = 'ssl/certificate.pem'
|
||||
checkCert = '/tmp/cert.pl'
|
||||
|
||||
certPem = request.form.get('certPem', '').strip()
|
||||
privateKey = request.form.get('privateKey', '').strip()
|
||||
|
||||
mw.writeFile(checkCert, certPem)
|
||||
if privateKey:
|
||||
mw.writeFile(keyPath, privateKey)
|
||||
if certPem:
|
||||
mw.writeFile(certPath, certPem)
|
||||
if not mw.checkCert(checkCert):
|
||||
return mw.returnJson(False, '证书错误,请检查!')
|
||||
mw.writeFile('ssl/input.pl', 'True')
|
||||
return mw.returnJson(True, '证书已保存!')
|
||||
|
||||
# 设置面板SSL
|
||||
def setPanelSslApi(self):
|
||||
sslConf = mw.getRunDir() + '/data/ssl.pl'
|
||||
if os.path.exists(sslConf):
|
||||
os.system('rm -f ' + sslConf)
|
||||
return mw.returnJson(True, 'SSL已关闭,请使用http协议访问面板!')
|
||||
else:
|
||||
os.system('pip install cffi==1.10')
|
||||
os.system('pip install cryptography==2.1')
|
||||
os.system('pip install pyOpenSSL==16.2')
|
||||
try:
|
||||
if not self.createSSL():
|
||||
return mw.returnJson(False, '开启失败,无法自动安装pyOpenSSL组件!<p>请尝试手动安装: pip install pyOpenSSL</p>')
|
||||
mw.writeFile(sslConf, 'True')
|
||||
except Exception as ex:
|
||||
return mw.returnJson(False, '开启失败,无法自动安装pyOpenSSL组件!<p>请尝试手动安装: pip install pyOpenSSL</p>')
|
||||
return mw.returnJson(True, '开启成功,请使用https协议访问面板!')
|
||||
|
||||
def getApi(self):
|
||||
data = {}
|
||||
return mw.getJson(data)
|
||||
##### ----- end ----- ###
|
||||
|
||||
# 自签证书
|
||||
def createSSL(self):
|
||||
if os.path.exists('ssl/input.pl'):
|
||||
return True
|
||||
import OpenSSL
|
||||
key = OpenSSL.crypto.PKey()
|
||||
key.generate_key(OpenSSL.crypto.TYPE_RSA, 2048)
|
||||
cert = OpenSSL.crypto.X509()
|
||||
cert.set_serial_number(0)
|
||||
cert.get_subject().CN = mw.getLocalIp()
|
||||
cert.set_issuer(cert.get_subject())
|
||||
cert.gmtime_adj_notBefore(0)
|
||||
cert.gmtime_adj_notAfter(86400 * 3650)
|
||||
cert.set_pubkey(key)
|
||||
cert.sign(key, 'md5')
|
||||
cert_ca = OpenSSL.crypto.dump_certificate(
|
||||
OpenSSL.crypto.FILETYPE_PEM, cert)
|
||||
private_key = OpenSSL.crypto.dump_privatekey(
|
||||
OpenSSL.crypto.FILETYPE_PEM, key)
|
||||
if len(cert_ca) > 100 and len(private_key) > 100:
|
||||
mw.writeFile('ssl/certificate.pem', cert_ca)
|
||||
mw.writeFile('ssl/privateKey.pem', private_key)
|
||||
print(cert_ca, private_key)
|
||||
return True
|
||||
return False
|
||||
|
||||
# 获取临时登录列表
|
||||
def getTempLoginApi(self):
|
||||
if 'tmp_login_expire' in session:
|
||||
return mw.returnJson(False, '没有权限')
|
||||
limit = request.form.get('limit', '10').strip()
|
||||
p = request.form.get('p', '1').strip()
|
||||
tojs = request.form.get('tojs', '').strip()
|
||||
|
||||
tempLoginM = mw.M('temp_login')
|
||||
tempLoginM.where('state=? and expire<?',
|
||||
(0, int(time.time()))).setField('state', -1)
|
||||
|
||||
start = (int(p) - 1) * (int(limit))
|
||||
vlist = tempLoginM.limit(str(start) + ',' + str(limit)).order('id desc').field(
|
||||
'id,addtime,expire,login_time,login_addr,state').select()
|
||||
|
||||
data = {}
|
||||
data['data'] = vlist
|
||||
|
||||
count = tempLoginM.count()
|
||||
page_args = {}
|
||||
page_args['count'] = count
|
||||
page_args['tojs'] = 'get_temp_login'
|
||||
page_args['p'] = p
|
||||
page_args['row'] = limit
|
||||
|
||||
data['page'] = mw.getPage(page_args)
|
||||
return mw.getJson(data)
|
||||
|
||||
# 删除临时登录
|
||||
def removeTempLoginApi(self):
|
||||
if 'tmp_login_expire' in session:
|
||||
return mw.returnJson(False, '没有权限')
|
||||
sid = request.form.get('id', '10').strip()
|
||||
if mw.M('temp_login').where('id=?', (sid,)).delete():
|
||||
mw.writeLog('面板设置', '删除临时登录连接')
|
||||
return mw.returnJson(True, '删除成功')
|
||||
return mw.returnJson(False, '删除失败')
|
||||
|
||||
def setTempLoginApi(self):
|
||||
if 'tmp_login_expire' in session:
|
||||
return mw.returnJson(False, '没有权限')
|
||||
s_time = int(time.time())
|
||||
mw.M('temp_login').where(
|
||||
'state=? and expire>?', (0, s_time)).delete()
|
||||
token = mw.getRandomString(48)
|
||||
salt = mw.getRandomString(12)
|
||||
|
||||
pdata = {
|
||||
'token': mw.md5(token + salt),
|
||||
'salt': salt,
|
||||
'state': 0,
|
||||
'login_time': 0,
|
||||
'login_addr': '',
|
||||
'expire': s_time + 3600,
|
||||
'addtime': s_time
|
||||
}
|
||||
|
||||
if not mw.M('temp_login').count():
|
||||
pdata['id'] = 101
|
||||
|
||||
if mw.M('temp_login').insert(pdata):
|
||||
mw.writeLog('面板设置', '生成临时连接,过期时间:{}'.format(
|
||||
mw.formatDate(times=pdata['expire'])))
|
||||
return mw.getJson({'status': True, 'msg': "临时连接已生成", 'token': token, 'expire': pdata['expire']})
|
||||
return mw.returnJson(False, '连接生成失败')
|
||||
|
||||
def getTempLoginLogsApi(self):
|
||||
if 'tmp_login_expire' in session:
|
||||
return mw.returnJson(False, '没有权限')
|
||||
|
||||
logs_id = request.form.get('id', '').strip()
|
||||
logs_id = int(logs_id)
|
||||
data = mw.M('logs').where(
|
||||
'uid=?', (logs_id,)).order('id desc').field(
|
||||
'id,type,uid,log,addtime').select()
|
||||
return mw.returnJson(False, 'ok', data)
|
||||
|
||||
def checkPanelToken(self):
|
||||
api_file = self.__api_addr
|
||||
if not os.path.exists(api_file):
|
||||
return False, ''
|
||||
|
||||
tmp = mw.readFile(api_file)
|
||||
data = json.loads(tmp)
|
||||
if data['open']:
|
||||
return True, data
|
||||
else:
|
||||
return False, ''
|
||||
|
||||
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": []}
|
||||
mw.writeFile(api_file, json.dumps(ready_data))
|
||||
mw.execShell("chmod 600 " + api_file)
|
||||
tmp = mw.readFile(api_file)
|
||||
data = json.loads(tmp)
|
||||
|
||||
if not 'key' in data:
|
||||
data['key'] = mw.getRandomString(16)
|
||||
mw.writeFile(api_file, json.dumps(data))
|
||||
|
||||
if 'token_crypt' in data:
|
||||
data['token'] = mw.deCrypt(data['token'], data['token_crypt'])
|
||||
else:
|
||||
token = mw.getRandomString(32)
|
||||
data['token'] = mw.md5(token)
|
||||
data['token_crypt'] = mw.enCrypt(
|
||||
data['token'], token).decode('utf-8')
|
||||
mw.writeFile(api_file, json.dumps(data))
|
||||
data['token'] = "***********************************"
|
||||
|
||||
data['limit_addr'] = '\n'.join(data['limit_addr'])
|
||||
|
||||
del(data['key'])
|
||||
return mw.returnJson(True, 'ok', data)
|
||||
|
||||
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)
|
||||
data['token_crypt'] = mw.enCrypt(
|
||||
data['token'], token).decode('utf-8')
|
||||
mw.writeLog('API配置', '重新生成API-Token')
|
||||
mw.writeFile(api_file, json.dumps(data))
|
||||
return mw.returnJson(True, 'ok', token)
|
||||
|
||||
elif op_type == '2':
|
||||
data['open'] = not data['open']
|
||||
stats = {True: '开启', False: '关闭'}
|
||||
if not 'token_crypt' in data:
|
||||
token = mw.getRandomString(32)
|
||||
data['token'] = mw.md5(token)
|
||||
data['token_crypt'] = mw.enCrypt(
|
||||
data['token'], token).decode('utf-8')
|
||||
|
||||
token = stats[data['open']] + '成功!'
|
||||
mw.writeLog('API配置', '%sAPI接口' % stats[data['open']])
|
||||
mw.writeFile(api_file, json.dumps(data))
|
||||
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 get(self):
|
||||
|
||||
data = {}
|
||||
data['title'] = mw.getConfig('title')
|
||||
data['site_path'] = mw.getWwwDir()
|
||||
data['backup_path'] = mw.getBackupDir()
|
||||
sformat = 'date +"%Y-%m-%d %H:%M:%S %Z %z"'
|
||||
data['systemdate'] = mw.execShell(sformat)[0].strip()
|
||||
|
||||
data['port'] = mw.getHostPort()
|
||||
data['ip'] = mw.getHostAddr()
|
||||
|
||||
admin_path_file = 'data/admin_path.pl'
|
||||
if not os.path.exists(admin_path_file):
|
||||
data['admin_path'] = '/'
|
||||
else:
|
||||
data['admin_path'] = mw.readFile(admin_path_file)
|
||||
|
||||
ipv6_file = 'data/ipv6.pl'
|
||||
if os.path.exists(ipv6_file):
|
||||
data['ipv6'] = 'checked'
|
||||
else:
|
||||
data['ipv6'] = ''
|
||||
|
||||
debug_file = 'data/debug.pl'
|
||||
if os.path.exists(debug_file):
|
||||
data['debug'] = 'checked'
|
||||
else:
|
||||
data['debug'] = ''
|
||||
|
||||
ssl_file = 'data/ssl.pl'
|
||||
if os.path.exists('data/ssl.pl'):
|
||||
data['ssl'] = 'checked'
|
||||
else:
|
||||
data['ssl'] = ''
|
||||
|
||||
basic_auth = 'data/basic_auth.json'
|
||||
if os.path.exists(basic_auth):
|
||||
bac = mw.readFile(basic_auth)
|
||||
bac = json.loads(bac)
|
||||
if bac['open']:
|
||||
data['basic_auth'] = 'checked'
|
||||
else:
|
||||
data['basic_auth'] = ''
|
||||
|
||||
cfg_domain = 'data/bind_domain.pl'
|
||||
if os.path.exists(cfg_domain):
|
||||
domain = mw.readFile(cfg_domain)
|
||||
data['bind_domain'] = domain.strip()
|
||||
else:
|
||||
data['bind_domain'] = ''
|
||||
|
||||
api_token = self.__api_addr
|
||||
if os.path.exists(api_token):
|
||||
bac = mw.readFile(api_token)
|
||||
bac = json.loads(bac)
|
||||
if bac['open']:
|
||||
data['api_token'] = 'checked'
|
||||
else:
|
||||
data['api_token'] = ''
|
||||
|
||||
data['site_count'] = mw.M('sites').count()
|
||||
|
||||
data['username'] = mw.M('users').where(
|
||||
"id=?", (1,)).getField('username')
|
||||
|
||||
return data
|
||||
@@ -1,639 +0,0 @@
|
||||
# coding: utf-8
|
||||
|
||||
# ---------------------------------------------------------------------------------
|
||||
# MW-Linux面板
|
||||
# ---------------------------------------------------------------------------------
|
||||
# copyright (c) 2018-∞(https://github.com/midoks/mdserver-web) All rights reserved.
|
||||
# ---------------------------------------------------------------------------------
|
||||
# Author: midoks <midoks@163.com>
|
||||
# ---------------------------------------------------------------------------------
|
||||
|
||||
# ---------------------------------------------------------------------------------
|
||||
# 计划任务操作
|
||||
# ---------------------------------------------------------------------------------
|
||||
|
||||
|
||||
import psutil
|
||||
import time
|
||||
import os
|
||||
import sys
|
||||
import mw
|
||||
import re
|
||||
import json
|
||||
import pwd
|
||||
|
||||
from flask import request
|
||||
|
||||
|
||||
class crontab_api:
|
||||
|
||||
field = 'id,name,type,where1,where_hour,where_minute,echo,addtime,status,save,backup_to,stype,sname,sbody,urladdress'
|
||||
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
##### ----- start ----- ###
|
||||
def listApi(self):
|
||||
p = request.args.get('p', 1)
|
||||
psize = 10
|
||||
|
||||
startPage = (int(p) - 1) * psize
|
||||
pageInfo = str(startPage) + ',' + str(psize)
|
||||
|
||||
_list = mw.M('crontab').where('', ()).field(
|
||||
self.field).limit(pageInfo).order('id desc').select()
|
||||
|
||||
data = []
|
||||
for i in range(len(_list)):
|
||||
tmp = _list[i]
|
||||
if _list[i]['type'] == "day":
|
||||
tmp['type'] = '每天'
|
||||
tmp['cycle'] = mw.getInfo('每天, {1}点{2}分 执行', (str(
|
||||
_list[i]['where_hour']), str(_list[i]['where_minute'])))
|
||||
elif _list[i]['type'] == "day-n":
|
||||
tmp['type'] = mw.getInfo(
|
||||
'每{1}天', (str(_list[i]['where1']),))
|
||||
tmp['cycle'] = mw.getInfo('每隔{1}天, {2}点{3}分 执行', (str(
|
||||
_list[i]['where1']), str(_list[i]['where_hour']), str(_list[i]['where_minute'])))
|
||||
elif _list[i]['type'] == "hour":
|
||||
tmp['type'] = '每小时'
|
||||
tmp['cycle'] = mw.getInfo(
|
||||
'每小时, 第{1}分钟 执行', (str(_list[i]['where_minute']),))
|
||||
elif _list[i]['type'] == "hour-n":
|
||||
tmp['type'] = mw.getInfo(
|
||||
'每{1}小时', (str(_list[i]['where1']),))
|
||||
tmp['cycle'] = mw.getInfo('每{1}小时, 第{2}分钟 执行', (str(
|
||||
_list[i]['where1']), str(_list[i]['where_minute'])))
|
||||
elif _list[i]['type'] == "minute-n":
|
||||
tmp['type'] = mw.getInfo(
|
||||
'每{1}分钟', (str(_list[i]['where1']),))
|
||||
tmp['cycle'] = mw.getInfo(
|
||||
'每隔{1}分钟执行', (str(_list[i]['where1']),))
|
||||
elif _list[i]['type'] == "week":
|
||||
tmp['type'] = '每周'
|
||||
if not _list[i]['where1']:
|
||||
_list[i]['where1'] = '0'
|
||||
tmp['cycle'] = mw.getInfo('每周{1}, {2}点{3}分执行', (self.toWeek(int(
|
||||
_list[i]['where1'])), str(_list[i]['where_hour']), str(_list[i]['where_minute'])))
|
||||
elif _list[i]['type'] == "month":
|
||||
tmp['type'] = '每月'
|
||||
tmp['cycle'] = mw.getInfo('每月, {1}日 {2}点{3}分执行', (str(_list[i]['where1']), str(
|
||||
_list[i]['where_hour']), str(_list[i]['where_minute'])))
|
||||
data.append(tmp)
|
||||
|
||||
rdata = {}
|
||||
rdata['data'] = data
|
||||
|
||||
count = mw.M('crontab').where('', ()).count()
|
||||
_page = {}
|
||||
_page['count'] = count
|
||||
_page['p'] = p
|
||||
_page['row'] = psize
|
||||
_page['tojs'] = "getCronData"
|
||||
|
||||
rdata['list'] = mw.getPage(_page)
|
||||
rdata['p'] = p
|
||||
|
||||
# backup hock
|
||||
bh_file = mw.getPanelDataDir() + "/hook_backup.json"
|
||||
if os.path.exists(bh_file):
|
||||
hb_data = mw.readFile(bh_file)
|
||||
hb_data = json.loads(hb_data)
|
||||
rdata['backup_hook'] = hb_data
|
||||
|
||||
return mw.getJson(rdata)
|
||||
|
||||
# 设置计划任务状态
|
||||
def setCronStatusApi(self):
|
||||
mid = request.form.get('id', '')
|
||||
cronInfo = mw.M('crontab').where(
|
||||
'id=?', (mid,)).field(self.field).find()
|
||||
status = 1
|
||||
if cronInfo['status'] == status:
|
||||
status = 0
|
||||
self.removeForCrond(cronInfo['echo'])
|
||||
else:
|
||||
cronInfo['status'] = 1
|
||||
self.syncToCrond(cronInfo)
|
||||
|
||||
mw.M('crontab').where('id=?', (mid,)).setField('status', status)
|
||||
mw.writeLog(
|
||||
'计划任务', '修改计划任务[' + cronInfo['name'] + ']状态为[' + str(status) + ']')
|
||||
return mw.returnJson(True, '设置成功')
|
||||
|
||||
# 获取指定任务数据
|
||||
def getCrondFindApi(self):
|
||||
sid = request.form.get('id', '')
|
||||
data = mw.M('crontab').where(
|
||||
'id=?', (sid,)).field(self.field).find()
|
||||
return mw.getJson(data)
|
||||
|
||||
# 参数校验
|
||||
def cronCheck(self, params):
|
||||
|
||||
if params['stype'] == 'site' or params['stype'] == 'database' or params['stype'] == 'logs':
|
||||
if params['save'] == '':
|
||||
return False, '保留份数不能为空!'
|
||||
|
||||
if params['type'] == 'day':
|
||||
if params['hour'] == '':
|
||||
return False, '小时不能为空!'
|
||||
if params['minute'] == '':
|
||||
return False, '分钟不能为空!'
|
||||
|
||||
if params['type'] == 'day-n':
|
||||
if params['where1'] == '':
|
||||
return False, '天不能为空!'
|
||||
if params['hour'] == '':
|
||||
return False, '小时不能为空!'
|
||||
if params['minute'] == '':
|
||||
return False, '分钟不能为空!'
|
||||
if params['type'] == 'hour':
|
||||
if params['minute'] == '':
|
||||
return False, '分钟不能为空!'
|
||||
|
||||
if params['type'] == 'hour-n':
|
||||
if params['where1'] == '':
|
||||
return False, '小时不能为空!'
|
||||
if params['minute'] == '':
|
||||
return False, '分钟不能为空!'
|
||||
|
||||
if params['type'] == 'minute-n':
|
||||
if params['where1'] == '':
|
||||
return False, '分钟不能为空!'
|
||||
|
||||
if params['type'] == 'week':
|
||||
if params['hour'] == '':
|
||||
return False, '小时不能为空!'
|
||||
if params['minute'] == '':
|
||||
return False, '分钟不能为空!'
|
||||
|
||||
if params['type'] == 'month':
|
||||
if params['where1'] == '':
|
||||
return False, '日不能为空!'
|
||||
if params['hour'] == '':
|
||||
return False, '小时不能为空!'
|
||||
if params['minute'] == '':
|
||||
return False, '分钟不能为空!'
|
||||
|
||||
return True, 'OK'
|
||||
|
||||
def modifyCrondApi(self):
|
||||
sid = request.form.get('id', '')
|
||||
iname = request.form.get('name', '')
|
||||
field_type = request.form.get('type', '')
|
||||
week = request.form.get('week', '')
|
||||
where1 = request.form.get('where1', '')
|
||||
hour = request.form.get('hour', '')
|
||||
minute = request.form.get('minute', '')
|
||||
save = request.form.get('save', '')
|
||||
backup_to = request.form.get('backup_to', '')
|
||||
stype = request.form.get('stype', '')
|
||||
sname = request.form.get('sname', '')
|
||||
sbody = request.form.get('sbody', '')
|
||||
urladdress = request.form.get('urladdress', '')
|
||||
|
||||
if len(iname) < 1:
|
||||
return mw.returnJson(False, '任务名称不能为空!')
|
||||
|
||||
params = {
|
||||
'name': iname,
|
||||
'type': field_type,
|
||||
'week': week,
|
||||
'where1': where1,
|
||||
'hour': hour,
|
||||
'minute': minute,
|
||||
'save': save,
|
||||
'backup_to': backup_to,
|
||||
'stype': stype,
|
||||
'sname': sname,
|
||||
'sbody': sbody,
|
||||
'urladdress': urladdress,
|
||||
}
|
||||
|
||||
is_check_pass, msg = self.cronCheck(params)
|
||||
if not is_check_pass:
|
||||
return mw.returnJson(is_check_pass, msg)
|
||||
|
||||
cuonConfig, get, name = self.getCrondCycle(params)
|
||||
cronInfo = mw.M('crontab').where(
|
||||
'id=?', (sid,)).field(self.field).find()
|
||||
del(cronInfo['id'])
|
||||
del(cronInfo['addtime'])
|
||||
cronInfo['name'] = get['name']
|
||||
cronInfo['type'] = get['type']
|
||||
cronInfo['where1'] = get['where1']
|
||||
cronInfo['where_hour'] = get['hour']
|
||||
cronInfo['where_minute'] = get['minute']
|
||||
cronInfo['save'] = get['save']
|
||||
cronInfo['backup_to'] = get['backup_to']
|
||||
cronInfo['sbody'] = get['sbody']
|
||||
cronInfo['urladdress'] = get['urladdress']
|
||||
|
||||
addData = mw.M('crontab').where('id=?', (sid,)).save('name,type,where1,where_hour,where_minute,save,backup_to,sbody,urladdress', (get[
|
||||
'name'], field_type, get['where1'], get['hour'], get['minute'], get['save'], get['backup_to'], get['sbody'], get['urladdress']))
|
||||
self.removeForCrond(cronInfo['echo'])
|
||||
self.syncToCrond(cronInfo)
|
||||
mw.writeLog('计划任务', '修改计划任务[' + cronInfo['name'] + ']成功')
|
||||
return mw.returnJson(True, '修改成功')
|
||||
|
||||
def logsApi(self):
|
||||
sid = request.form.get('id', '')
|
||||
echo = mw.M('crontab').where("id=?", (sid,)).field('echo').find()
|
||||
logFile = mw.getServerDir() + '/cron/' + echo['echo'] + '.log'
|
||||
if not os.path.exists(logFile):
|
||||
return mw.returnJson(False, '当前日志为空!')
|
||||
log = mw.getLastLine(logFile, 500)
|
||||
return mw.returnJson(True, log)
|
||||
|
||||
def addApi(self):
|
||||
iname = request.form.get('name', '')
|
||||
field_type = request.form.get('type', '')
|
||||
week = request.form.get('week', '')
|
||||
where1 = request.form.get('where1', '')
|
||||
hour = request.form.get('hour', '')
|
||||
minute = request.form.get('minute', '')
|
||||
save = request.form.get('save', '')
|
||||
backup_to = request.form.get('backupTo', '')
|
||||
stype = request.form.get('sType', '')
|
||||
sname = request.form.get('sName', '')
|
||||
sbody = request.form.get('sBody', '')
|
||||
urladdress = request.form.get('urladdress', '')
|
||||
|
||||
if len(iname) < 1:
|
||||
return mw.returnJson(False, '任务名称不能为空!')
|
||||
|
||||
params = {
|
||||
'name': iname,
|
||||
'type': field_type,
|
||||
'week': week,
|
||||
'where1': where1,
|
||||
'hour': hour,
|
||||
'minute': minute,
|
||||
'save': save,
|
||||
'backup_to': backup_to,
|
||||
'stype': stype,
|
||||
'sname': sname,
|
||||
'sbody': sbody,
|
||||
'urladdress': urladdress,
|
||||
}
|
||||
|
||||
is_check_pass, msg = self.cronCheck(params)
|
||||
if not is_check_pass:
|
||||
return mw.returnJson(is_check_pass, msg)
|
||||
|
||||
addData = self.add(params)
|
||||
if addData > 0:
|
||||
return mw.returnJson(True, '添加成功')
|
||||
return mw.returnJson(False, '添加失败')
|
||||
|
||||
def add(self, params):
|
||||
|
||||
iname = params["name"]
|
||||
field_type = params["type"]
|
||||
week = params["week"]
|
||||
where1 = params["where1"]
|
||||
hour = params["hour"]
|
||||
minute = params["minute"]
|
||||
save = params["save"]
|
||||
backup_to = params["backup_to"]
|
||||
stype = params["stype"]
|
||||
sname = params["sname"]
|
||||
sbody = params["sbody"]
|
||||
urladdress = params["urladdress"]
|
||||
|
||||
# print params
|
||||
cronConfig, get, name = self.getCrondCycle(params)
|
||||
cronPath = mw.getServerDir() + '/cron'
|
||||
cronName = self.getShell(params)
|
||||
|
||||
if type(cronName) == dict:
|
||||
return cronName
|
||||
|
||||
cronConfig += ' ' + cronPath + '/' + cronName + \
|
||||
' >> ' + cronPath + '/' + cronName + '.log 2>&1'
|
||||
|
||||
# print(cronConfig)
|
||||
if not mw.isAppleSystem():
|
||||
wRes = self.writeShell(cronConfig)
|
||||
if type(wRes) != bool:
|
||||
return wRes
|
||||
self.crondReload()
|
||||
|
||||
add_time = time.strftime('%Y-%m-%d %X', time.localtime())
|
||||
task_id = mw.M('crontab').add('name,type,where1,where_hour,where_minute,echo,addtime,status,save,backup_to,stype,sname,sbody,urladdress',
|
||||
(iname, field_type, where1, hour, minute, cronName, add_time, 1, save, backup_to, stype, sname, sbody, urladdress,))
|
||||
return task_id
|
||||
|
||||
def startTaskApi(self):
|
||||
sid = request.form.get('id', '')
|
||||
echo = mw.M('crontab').where('id=?', (sid,)).getField('echo')
|
||||
execstr = mw.getServerDir() + '/cron/' + echo
|
||||
os.system('chmod +x ' + execstr)
|
||||
os.system('nohup ' + execstr + ' >> ' + execstr + '.log 2>&1 &')
|
||||
return mw.returnJson(True, '任务已执行!')
|
||||
|
||||
def delApi(self):
|
||||
task_id = request.form.get('id', '')
|
||||
try:
|
||||
data = self.delete(task_id)
|
||||
if not data[0]:
|
||||
return mw.returnJson(False, data[1])
|
||||
return mw.returnJson(True, '删除成功')
|
||||
except Exception as e:
|
||||
return mw.returnJson(False, '删除失败:' + str(e))
|
||||
|
||||
def delete(self, tid):
|
||||
|
||||
find = mw.M('crontab').where("id=?", (tid,)).field('name,echo').find()
|
||||
if not self.removeForCrond(find['echo']):
|
||||
return (False, '无法写入文件,请检查是否开启了系统加固功能!')
|
||||
|
||||
cronPath = mw.getServerDir() + '/cron'
|
||||
sfile = cronPath + '/' + find['echo']
|
||||
|
||||
if os.path.exists(sfile):
|
||||
os.remove(sfile)
|
||||
sfile = cronPath + '/' + find['echo'] + '.log'
|
||||
if os.path.exists(sfile):
|
||||
os.remove(sfile)
|
||||
|
||||
mw.M('crontab').where("id=?", (tid,)).delete()
|
||||
mw.writeLog('计划任务', mw.getInfo('删除计划任务[{1}]成功!', (find['name'],)))
|
||||
return (True, "OK")
|
||||
|
||||
def delLogsApi(self):
|
||||
sid = request.form.get('id', '')
|
||||
try:
|
||||
echo = mw.M('crontab').where("id=?", (sid,)).getField('echo')
|
||||
logFile = mw.getServerDir() + '/cron/' + echo + '.log'
|
||||
os.remove(logFile)
|
||||
return mw.returnJson(True, '任务日志已清空!')
|
||||
except:
|
||||
return mw.returnJson(False, '任务日志清空失败!')
|
||||
|
||||
# 取数据列表
|
||||
def getDataListApi(self):
|
||||
stype = request.form.get('type', '')
|
||||
|
||||
bak_data = []
|
||||
|
||||
if stype == 'sites' or stype == 'databases':
|
||||
hookPath = mw.getPanelDataDir() + "/hook_backup.json"
|
||||
if os.path.exists(hookPath):
|
||||
t = mw.readFile(hookPath)
|
||||
bak_data = json.loads(t)
|
||||
|
||||
if stype == 'databases':
|
||||
db_list = {}
|
||||
db_list['orderOpt'] = bak_data
|
||||
path = mw.getServerDir() + '/mysql'
|
||||
if not os.path.exists(path + '/mysql.db'):
|
||||
db_list['data'] = []
|
||||
else:
|
||||
db_list['data'] = mw.M('databases').dbPos(
|
||||
path, 'mysql').field('name,ps').select()
|
||||
return mw.getJson(db_list)
|
||||
|
||||
data = {}
|
||||
data['orderOpt'] = bak_data
|
||||
|
||||
data['data'] = mw.M(stype).field('name,ps').select()
|
||||
return mw.getJson(data)
|
||||
##### ----- start ----- ###
|
||||
|
||||
# 转换大写星期
|
||||
def toWeek(self, num):
|
||||
wheres = {
|
||||
0: '日',
|
||||
1: '一',
|
||||
2: '二',
|
||||
3: '三',
|
||||
4: '四',
|
||||
5: '五',
|
||||
6: '六'
|
||||
}
|
||||
try:
|
||||
return wheres[num]
|
||||
except:
|
||||
return ''
|
||||
|
||||
def getCrondCycle(self, params):
|
||||
cuonConfig = ''
|
||||
name = ''
|
||||
if params['type'] == "day":
|
||||
cuonConfig = self.getDay(params)
|
||||
name = '每天'
|
||||
elif params['type'] == "day-n":
|
||||
cuonConfig = self.getDay_N(params)
|
||||
name = mw.getInfo('每{1}天', (params['where1'],))
|
||||
elif params['type'] == "hour":
|
||||
cuonConfig = self.getHour(params)
|
||||
name = '每小时'
|
||||
elif params['type'] == "hour-n":
|
||||
cuonConfig = self.getHour_N(params)
|
||||
name = '每小时'
|
||||
elif params['type'] == "minute-n":
|
||||
cuonConfig = self.minute_N(params)
|
||||
elif params['type'] == "week":
|
||||
params['where1'] = params['week']
|
||||
cuonConfig = self.week(params)
|
||||
elif params['type'] == "month":
|
||||
cuonConfig = self.month(params)
|
||||
return cuonConfig, params, name
|
||||
|
||||
# 取任务构造Day
|
||||
def getDay(self, param):
|
||||
cuonConfig = "{0} {1} * * * ".format(param['minute'], param['hour'])
|
||||
return cuonConfig
|
||||
# 取任务构造Day_n
|
||||
|
||||
def getDay_N(self, param):
|
||||
cuonConfig = "{0} {1} */{2} * * ".format(
|
||||
param['minute'], param['hour'], param['where1'])
|
||||
return cuonConfig
|
||||
|
||||
# 取任务构造Hour
|
||||
def getHour(self, param):
|
||||
cuonConfig = "{0} * * * * ".format(param['minute'])
|
||||
return cuonConfig
|
||||
|
||||
# 取任务构造Hour-N
|
||||
def getHour_N(self, param):
|
||||
cuonConfig = "{0} */{1} * * * ".format(
|
||||
param['minute'], param['where1'])
|
||||
return cuonConfig
|
||||
|
||||
# 取任务构造Minute-N
|
||||
def minute_N(self, param):
|
||||
cuonConfig = "*/{0} * * * * ".format(param['where1'])
|
||||
return cuonConfig
|
||||
|
||||
# 取任务构造week
|
||||
def week(self, param):
|
||||
cuonConfig = "{0} {1} * * {2}".format(
|
||||
param['minute'], param['hour'], param['week'])
|
||||
return cuonConfig
|
||||
|
||||
# 取任务构造Month
|
||||
def month(self, param):
|
||||
cuonConfig = "{0} {1} {2} * * ".format(
|
||||
param['minute'], param['hour'], param['where1'])
|
||||
return cuonConfig
|
||||
|
||||
# 取执行脚本
|
||||
def getShell(self, param):
|
||||
# try:
|
||||
stype = param['stype']
|
||||
if stype == 'toFile':
|
||||
shell = param.sFile
|
||||
else:
|
||||
head = "#!/bin/bash\nPATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin\nexport PATH\n"
|
||||
|
||||
source_bin_activate = '''
|
||||
export LANG=en_US.UTF-8
|
||||
MW_PATH=%s/bin/activate
|
||||
if [ -f $MW_PATH ];then
|
||||
source $MW_PATH
|
||||
fi
|
||||
''' % (mw.getRunDir(),)
|
||||
|
||||
head = head + source_bin_activate + "\n"
|
||||
log = '.log'
|
||||
|
||||
script_dir = mw.getServerDir() + "/mdserver-web/scripts"
|
||||
|
||||
wheres = {
|
||||
'path': head + "python3 " + script_dir + "/backup.py path " + param['sname'] + " " + str(param['save']),
|
||||
'site': head + "python3 " + script_dir + "/backup.py site " + param['sname'] + " " + str(param['save']),
|
||||
'database': head + "python3 " + script_dir + "/backup.py database " + param['sname'] + " " + str(param['save']),
|
||||
'logs': head + "python3 " + script_dir + "/logs_backup.py " + param['sname'] + log + " " + str(param['save']),
|
||||
'rememory': head + "/bin/bash " + script_dir + '/rememory.sh'
|
||||
}
|
||||
if param['backup_to'] != 'localhost':
|
||||
cfile = mw.getPluginDir() + "/" + \
|
||||
param['backup_to'] + "/index.py"
|
||||
wheres = {
|
||||
'path': head + "python3 " + cfile + " path " + param['sname'] + " " + str(param['save']),
|
||||
'site': head + "python3 " + cfile + " site " + param['sname'] + " " + str(param['save']),
|
||||
'database': head + "python3 " + cfile + " database " + param['sname'] + " " + str(param['save']),
|
||||
'logs': head + "python3 " + script_dir + "/logs_backup.py " + param['sname'] + log + " " + str(param['save']),
|
||||
'rememory': head + "/bin/bash " + script_dir + '/rememory.sh'
|
||||
}
|
||||
try:
|
||||
shell = wheres[stype]
|
||||
except:
|
||||
if stype == 'toUrl':
|
||||
shell = head + "curl -sS --connect-timeout 10 -m 60 '" + \
|
||||
param['urladdress'] + "'"
|
||||
else:
|
||||
shell = head + param['sbody'].replace("\r\n", "\n")
|
||||
|
||||
shell += '''
|
||||
echo "----------------------------------------------------------------------------"
|
||||
endDate=`date +"%Y-%m-%d %H:%M:%S"`
|
||||
echo "★[$endDate] Successful"
|
||||
echo "----------------------------------------------------------------------------"
|
||||
'''
|
||||
cronPath = mw.getServerDir() + '/cron'
|
||||
if not os.path.exists(cronPath):
|
||||
mw.execShell('mkdir -p ' + cronPath)
|
||||
|
||||
if not 'echo' in param:
|
||||
cronName = mw.md5(mw.md5(str(time.time()) + '_mw'))
|
||||
else:
|
||||
cronName = param['echo']
|
||||
file = cronPath + '/' + cronName
|
||||
mw.writeFile(file, self.checkScript(shell))
|
||||
mw.execShell('chmod 750 ' + file)
|
||||
return cronName
|
||||
|
||||
# 检查脚本
|
||||
def checkScript(self, shell):
|
||||
keys = ['shutdown', 'init 0', 'mkfs', 'passwd',
|
||||
'chpasswd', '--stdin', 'mkfs.ext', 'mke2fs']
|
||||
for key in keys:
|
||||
shell = shell.replace(key, '[***]')
|
||||
return shell
|
||||
|
||||
# 将Shell脚本写到文件
|
||||
def writeShell(self, config):
|
||||
u_file = '/var/spool/cron/crontabs/root'
|
||||
if not os.path.exists(u_file):
|
||||
file = '/var/spool/cron/root'
|
||||
if mw.isAppleSystem():
|
||||
file = '/etc/crontab'
|
||||
else:
|
||||
file = u_file
|
||||
|
||||
if not os.path.exists(file):
|
||||
mw.writeFile(file, '')
|
||||
conf = mw.readFile(file)
|
||||
conf += str(config) + "\n"
|
||||
if mw.writeFile(file, conf):
|
||||
if not os.path.exists(u_file):
|
||||
mw.execShell("chmod 600 '" + file +
|
||||
"' && chown root.root " + file)
|
||||
else:
|
||||
mw.execShell("chmod 600 '" + file +
|
||||
"' && chown root.crontab " + file)
|
||||
return True
|
||||
return mw.returnJson(False, '文件写入失败,请检查是否开启系统加固功能!')
|
||||
|
||||
# 重载配置
|
||||
def crondReload(self):
|
||||
if mw.isAppleSystem():
|
||||
if os.path.exists('/etc/crontab'):
|
||||
pass
|
||||
# mw.execShell('/usr/sbin/cron restart')
|
||||
else:
|
||||
if os.path.exists('/etc/init.d/crond'):
|
||||
mw.execShell('/etc/init.d/crond reload')
|
||||
elif os.path.exists('/etc/init.d/cron'):
|
||||
mw.execShell('service cron restart')
|
||||
else:
|
||||
mw.execShell("systemctl reload crond")
|
||||
|
||||
# 从crond删除
|
||||
def removeForCrond(self, echo):
|
||||
u_file = '/var/spool/cron/crontabs/root'
|
||||
if not os.path.exists(u_file):
|
||||
file = '/var/spool/cron/root'
|
||||
if mw.isAppleSystem():
|
||||
file = '/etc/crontab'
|
||||
|
||||
if not os.path.exists(file):
|
||||
return False
|
||||
else:
|
||||
file = u_file
|
||||
|
||||
if mw.isAppleSystem():
|
||||
return True
|
||||
|
||||
conf = mw.readFile(file)
|
||||
rep = ".+" + str(echo) + ".+\n"
|
||||
conf = re.sub(rep, "", conf)
|
||||
if not mw.writeFile(file, conf):
|
||||
return False
|
||||
self.crondReload()
|
||||
return True
|
||||
|
||||
def syncToCrond(self, cronInfo):
|
||||
if 'status' in cronInfo:
|
||||
if cronInfo['status'] == 0:
|
||||
return False
|
||||
if 'where_hour' in cronInfo:
|
||||
cronInfo['hour'] = cronInfo['where_hour']
|
||||
cronInfo['minute'] = cronInfo['where_minute']
|
||||
cronInfo['week'] = cronInfo['where1']
|
||||
cuonConfig, cronInfo, name = self.getCrondCycle(cronInfo)
|
||||
cronPath = mw.getServerDir() + '/cron'
|
||||
cronName = self.getShell(cronInfo)
|
||||
if type(cronName) == dict:
|
||||
return cronName
|
||||
cuonConfig += ' ' + cronPath + '/' + cronName + \
|
||||
' >> ' + cronPath + '/' + cronName + '.log 2>&1'
|
||||
wRes = self.writeShell(cuonConfig)
|
||||
if type(wRes) != bool:
|
||||
return False
|
||||
self.crondReload()
|
||||
@@ -1,393 +0,0 @@
|
||||
# coding: utf-8
|
||||
|
||||
# ---------------------------------------------------------------------------------
|
||||
# MW-Linux面板
|
||||
# ---------------------------------------------------------------------------------
|
||||
# copyright (c) 2018-∞(https://github.com/midoks/mdserver-web) All rights reserved.
|
||||
# ---------------------------------------------------------------------------------
|
||||
# Author: midoks <midoks@163.com>
|
||||
# ---------------------------------------------------------------------------------
|
||||
|
||||
# ---------------------------------------------------------------------------------
|
||||
# sqlite3操作
|
||||
# ---------------------------------------------------------------------------------
|
||||
|
||||
|
||||
import sqlite3
|
||||
import os
|
||||
import sys
|
||||
|
||||
|
||||
class Sql():
|
||||
#------------------------------
|
||||
# 数据库操作类 For sqlite3
|
||||
#------------------------------
|
||||
__DB_FILE = None # 数据库文件
|
||||
__DB_CONN = None # 数据库连接对象
|
||||
__DB_TABLE = "" # 被操作的表名称
|
||||
__OPT_WHERE = "" # where条件
|
||||
__OPT_LIMIT = "" # limit条件
|
||||
__OPT_GROUP = "" # group条件
|
||||
__OPT_ORDER = "" # order条件
|
||||
__OPT_FIELD = "*" # field条件
|
||||
__OPT_PARAM = () # where值
|
||||
|
||||
def __init__(self):
|
||||
self.__DB_FILE = 'data/default.db'
|
||||
|
||||
def __getConn(self):
|
||||
# 取数据库对象
|
||||
try:
|
||||
if self.__DB_CONN == None:
|
||||
self.__DB_CONN = sqlite3.connect(self.__DB_FILE)
|
||||
self.__DB_CONN.text_factory = str
|
||||
except Exception as ex:
|
||||
return "error: " + str(ex)
|
||||
|
||||
def autoTextFactory(self):
|
||||
if sys.version_info[0] == 3:
|
||||
self.__DB_CONN.text_factory = lambda x: str(
|
||||
x, encoding="utf-8", errors='ignore')
|
||||
else:
|
||||
self.__DB_CONN.text_factory = lambda x: unicode(
|
||||
x, "utf-8", "ignore")
|
||||
|
||||
def dbfile(self, name):
|
||||
self.__DB_FILE = 'data/' + name + '.db'
|
||||
return self
|
||||
|
||||
def dbPos(self, path, name):
|
||||
self.__DB_FILE = path + '/' + name + '.db'
|
||||
return self
|
||||
|
||||
def table(self, table):
|
||||
# 设置表名
|
||||
self.__DB_TABLE = table
|
||||
return self
|
||||
|
||||
def where(self, where, param):
|
||||
# WHERE条件
|
||||
if where:
|
||||
self.__OPT_WHERE = " WHERE " + where
|
||||
self.__OPT_PARAM = param
|
||||
return self
|
||||
|
||||
def andWhere(self, where, param):
|
||||
# WHERE条件
|
||||
if where:
|
||||
self.__OPT_WHERE = self.__OPT_WHERE + " and " + where
|
||||
# print(param)
|
||||
# print(self.__OPT_PARAM)
|
||||
self.__OPT_PARAM = self.__OPT_PARAM + param
|
||||
return self
|
||||
|
||||
def order(self, order):
|
||||
# ORDER条件
|
||||
if len(order):
|
||||
self.__OPT_ORDER = " ORDER BY " + order
|
||||
else:
|
||||
self.__OPT_ORDER = ""
|
||||
return self
|
||||
|
||||
def group(self, group):
|
||||
if len(group):
|
||||
self.__OPT_GROUP = " GROUP BY " + group
|
||||
else:
|
||||
self.__OPT_GROUP = ""
|
||||
return self
|
||||
|
||||
def limit(self, limit):
|
||||
# LIMIT条件
|
||||
if len(limit):
|
||||
self.__OPT_LIMIT = " LIMIT " + limit
|
||||
else:
|
||||
self.__OPT_LIMIT = ""
|
||||
return self
|
||||
|
||||
def field(self, field):
|
||||
# FIELD条件
|
||||
if len(field):
|
||||
self.__OPT_FIELD = field
|
||||
return self
|
||||
|
||||
def select(self):
|
||||
# 查询数据集
|
||||
self.__getConn()
|
||||
try:
|
||||
sql = "SELECT " + self.__OPT_FIELD + " FROM " + self.__DB_TABLE + \
|
||||
self.__OPT_WHERE + self.__OPT_GROUP + self.__OPT_ORDER + self.__OPT_LIMIT
|
||||
# print(sql)
|
||||
result = self.__DB_CONN.execute(sql, self.__OPT_PARAM)
|
||||
data = result.fetchall()
|
||||
# 构造字曲系列
|
||||
if self.__OPT_FIELD != "*":
|
||||
field = self.__OPT_FIELD.split(',')
|
||||
tmp = []
|
||||
for row in data:
|
||||
i = 0
|
||||
tmp1 = {}
|
||||
for key in field:
|
||||
tmp1[key] = row[i]
|
||||
i += 1
|
||||
tmp.append(tmp1)
|
||||
del(tmp1)
|
||||
data = tmp
|
||||
del(tmp)
|
||||
else:
|
||||
# 将元组转换成列表
|
||||
tmp = map(list, data)
|
||||
data = tmp
|
||||
del(tmp)
|
||||
self.__close()
|
||||
return data
|
||||
except Exception as ex:
|
||||
return "error: " + str(ex)
|
||||
|
||||
def inquiry(self, input_field=''):
|
||||
# 查询数据集
|
||||
# 不清空查询参数
|
||||
self.__getConn()
|
||||
try:
|
||||
sql = "SELECT " + self.__OPT_FIELD + " FROM " + self.__DB_TABLE + \
|
||||
self.__OPT_WHERE + self.__OPT_GROUP + self.__OPT_ORDER + self.__OPT_LIMIT
|
||||
# print(sql, self.__OPT_PARAM)
|
||||
result = self.__DB_CONN.execute(sql, self.__OPT_PARAM)
|
||||
data = result.fetchall()
|
||||
# 构造字曲系列
|
||||
if self.__OPT_FIELD != "*":
|
||||
|
||||
if input_field != "":
|
||||
field = input_field.split(',')
|
||||
else:
|
||||
field = self.__OPT_FIELD.split(',')
|
||||
|
||||
tmp = []
|
||||
for row in data:
|
||||
i = 0
|
||||
tmp1 = {}
|
||||
for key in field:
|
||||
tmp1[key] = row[i]
|
||||
i += 1
|
||||
tmp.append(tmp1)
|
||||
del(tmp1)
|
||||
data = tmp
|
||||
del(tmp)
|
||||
else:
|
||||
# 将元组转换成列表
|
||||
tmp = map(list, data)
|
||||
data = tmp
|
||||
del(tmp)
|
||||
return data
|
||||
except Exception as ex:
|
||||
return "error: " + str(ex)
|
||||
|
||||
def getField(self, keyName):
|
||||
# 取回指定字段
|
||||
result = self.field(keyName).select()
|
||||
# print(result)
|
||||
if len(result) == 1:
|
||||
return result[0][keyName]
|
||||
return result
|
||||
|
||||
def setField(self, keyName, keyValue):
|
||||
# 更新指定字段
|
||||
return self.save(keyName, (keyValue,))
|
||||
|
||||
def find(self):
|
||||
# 取一行数据
|
||||
result = self.limit("1").select()
|
||||
if len(result) == 1:
|
||||
return result[0]
|
||||
return result
|
||||
|
||||
def count(self):
|
||||
# 取行数
|
||||
key = "COUNT(*)"
|
||||
data = self.field(key).select()
|
||||
try:
|
||||
return int(data[0][key])
|
||||
except:
|
||||
return 0
|
||||
|
||||
def add(self, keys, param):
|
||||
# 插入数据
|
||||
self.__getConn()
|
||||
try:
|
||||
values = ""
|
||||
for key in keys.split(','):
|
||||
values += "?,"
|
||||
values = self.checkInput(values[0:len(values) - 1])
|
||||
sql = "INSERT INTO " + self.__DB_TABLE + \
|
||||
"(" + keys + ") " + "VALUES(" + values + ")"
|
||||
result = self.__DB_CONN.execute(sql, param)
|
||||
last_id = result.lastrowid
|
||||
self.__close()
|
||||
self.__DB_CONN.commit()
|
||||
return last_id
|
||||
except Exception as ex:
|
||||
return "error: " + str(ex)
|
||||
|
||||
# 插入数据
|
||||
def insert(self, pdata):
|
||||
if not pdata:
|
||||
return False
|
||||
keys, param = self.__format_pdata(pdata)
|
||||
return self.add(keys, param)
|
||||
|
||||
# 更新数据
|
||||
def update(self, pdata):
|
||||
if not pdata:
|
||||
return False
|
||||
keys, param = self.__format_pdata(pdata)
|
||||
return self.save(keys, param)
|
||||
|
||||
# 构造数据
|
||||
def __format_pdata(self, pdata):
|
||||
keys = pdata.keys()
|
||||
keys_str = ','.join(keys)
|
||||
param = []
|
||||
for k in keys:
|
||||
param.append(pdata[k])
|
||||
return keys_str, tuple(param)
|
||||
|
||||
def checkInput(self, data):
|
||||
if not data:
|
||||
return data
|
||||
if type(data) != str:
|
||||
return data
|
||||
checkList = [
|
||||
{'d': '<', 'r': '<'},
|
||||
{'d': '>', 'r': '>'},
|
||||
{'d': '\'', 'r': '‘'},
|
||||
{'d': '"', 'r': '“'},
|
||||
{'d': '&', 'r': '&'},
|
||||
{'d': '#', 'r': '#'},
|
||||
{'d': '<', 'r': '<'}
|
||||
]
|
||||
for v in checkList:
|
||||
data = data.replace(v['d'], v['r'])
|
||||
return data
|
||||
|
||||
def addAll(self, keys, param):
|
||||
# 插入数据
|
||||
self.__getConn()
|
||||
try:
|
||||
values = ""
|
||||
for key in keys.split(','):
|
||||
values += "?,"
|
||||
values = values[0:len(values) - 1]
|
||||
sql = "INSERT INTO " + self.__DB_TABLE + \
|
||||
"(" + keys + ") " + "VALUES(" + values + ")"
|
||||
result = self.__DB_CONN.execute(sql, param)
|
||||
return True
|
||||
except Exception as ex:
|
||||
return "error: " + str(ex)
|
||||
|
||||
def commit(self):
|
||||
self.__close()
|
||||
self.__DB_CONN.commit()
|
||||
|
||||
def save(self, keys, param):
|
||||
# 更新数据
|
||||
self.__getConn()
|
||||
try:
|
||||
opt = ""
|
||||
for key in keys.split(','):
|
||||
opt += key + "=?,"
|
||||
opt = opt[0:len(opt) - 1]
|
||||
sql = "UPDATE " + self.__DB_TABLE + " SET " + opt + self.__OPT_WHERE
|
||||
|
||||
# import mw
|
||||
# mw.writeFile('/tmp/test.pl', sql)
|
||||
|
||||
# 处理拼接WHERE与UPDATE参数
|
||||
tmp = list(param)
|
||||
for arg in self.__OPT_PARAM:
|
||||
tmp.append(arg)
|
||||
self.__OPT_PARAM = tuple(tmp)
|
||||
result = self.__DB_CONN.execute(sql, self.__OPT_PARAM)
|
||||
self.__close()
|
||||
self.__DB_CONN.commit()
|
||||
return result.rowcount
|
||||
except Exception as ex:
|
||||
return "error: " + str(ex)
|
||||
|
||||
def delete(self, id=None):
|
||||
# 删除数据
|
||||
self.__getConn()
|
||||
try:
|
||||
if id:
|
||||
self.__OPT_WHERE = " WHERE id=?"
|
||||
self.__OPT_PARAM = (id,)
|
||||
sql = "DELETE FROM " + self.__DB_TABLE + self.__OPT_WHERE
|
||||
result = self.__DB_CONN.execute(sql, self.__OPT_PARAM)
|
||||
self.__close()
|
||||
self.__DB_CONN.commit()
|
||||
return result.rowcount
|
||||
except Exception as ex:
|
||||
return "error: " + str(ex)
|
||||
|
||||
def originExecute(self, sql, param=()):
|
||||
self.__getConn()
|
||||
try:
|
||||
result = self.__DB_CONN.execute(sql, param)
|
||||
self.__DB_CONN.commit()
|
||||
return result
|
||||
except Exception as ex:
|
||||
return "error: " + str(ex)
|
||||
|
||||
def execute(self, sql, param=()):
|
||||
# 执行SQL语句返回受影响行
|
||||
self.__getConn()
|
||||
# print sql, param
|
||||
try:
|
||||
result = self.__DB_CONN.execute(sql, param)
|
||||
self.__DB_CONN.commit()
|
||||
return result.rowcount
|
||||
except Exception as ex:
|
||||
return "error: " + str(ex)
|
||||
|
||||
def query(self, sql, param):
|
||||
# 执行SQL语句返回数据集
|
||||
self.__getConn()
|
||||
try:
|
||||
result = self.__DB_CONN.execute(sql, param)
|
||||
# 将元组转换成列表
|
||||
# data = map(list, result)
|
||||
return result
|
||||
except Exception as ex:
|
||||
return "error: " + str(ex)
|
||||
|
||||
def create(self, name):
|
||||
# 创建数据表
|
||||
self.__getConn()
|
||||
import mw
|
||||
script = mw.readFile('data/' + name + '.sql')
|
||||
result = self.__DB_CONN.executescript(script)
|
||||
self.__DB_CONN.commit()
|
||||
return result.rowcount
|
||||
|
||||
def fofile(self, filename):
|
||||
# 执行脚本
|
||||
self.__getConn()
|
||||
import mw
|
||||
script = mw.readFile(filename)
|
||||
result = self.__DB_CONN.executescript(script)
|
||||
self.__DB_CONN.commit()
|
||||
return result.rowcount
|
||||
|
||||
def __close(self):
|
||||
# 清理条件属性
|
||||
self.__OPT_WHERE = ""
|
||||
self.__OPT_FIELD = "*"
|
||||
self.__OPT_ORDER = ""
|
||||
self.__OPT_LIMIT = ""
|
||||
self.__OPT_PARAM = ()
|
||||
|
||||
def close(self):
|
||||
# 释放资源
|
||||
try:
|
||||
self.__DB_CONN.close()
|
||||
self.__DB_CONN = None
|
||||
except:
|
||||
pass
|
||||
@@ -1,896 +0,0 @@
|
||||
# coding: utf-8
|
||||
|
||||
# ---------------------------------------------------------------------------------
|
||||
# MW-Linux面板
|
||||
# ---------------------------------------------------------------------------------
|
||||
# copyright (c) 2018-∞(https://github.com/midoks/mdserver-web) All rights reserved.
|
||||
# ---------------------------------------------------------------------------------
|
||||
# Author: midoks <midoks@163.com>
|
||||
# ---------------------------------------------------------------------------------
|
||||
|
||||
# ---------------------------------------------------------------------------------
|
||||
# 文件类操作
|
||||
# ---------------------------------------------------------------------------------
|
||||
|
||||
|
||||
import psutil
|
||||
import time
|
||||
import os
|
||||
import sys
|
||||
import mw
|
||||
import re
|
||||
import json
|
||||
import pwd
|
||||
import shutil
|
||||
|
||||
from flask import request
|
||||
from flask import send_file, send_from_directory
|
||||
from flask import make_response
|
||||
from flask import session
|
||||
|
||||
|
||||
class files_api:
|
||||
|
||||
rPath = None
|
||||
|
||||
def __init__(self):
|
||||
self.rPath = mw.getRootDir() + '/recycle_bin/'
|
||||
|
||||
##### ----- start ----- ###
|
||||
def getBodyApi(self):
|
||||
path = request.form.get('path', '')
|
||||
return self.getBody(path)
|
||||
|
||||
def getLastBodyApi(self):
|
||||
path = request.form.get('path', '')
|
||||
line = request.form.get('line', '100')
|
||||
|
||||
if not os.path.exists(path):
|
||||
return mw.returnJson(False, '文件不存在', (path,))
|
||||
|
||||
try:
|
||||
data = mw.getLastLine(path, int(line))
|
||||
return mw.returnJson(True, 'OK', data)
|
||||
except Exception as ex:
|
||||
return mw.returnJson(False, '无法正确读取文件!' + str(ex))
|
||||
|
||||
def saveBodyApi(self):
|
||||
path = request.form.get('path', '')
|
||||
data = request.form.get('data', '')
|
||||
encoding = request.form.get('encoding', '')
|
||||
return self.saveBody(path, data, encoding)
|
||||
|
||||
def downloadApi(self):
|
||||
filename = request.args.get('filename', '')
|
||||
if not os.path.exists(filename):
|
||||
return ''
|
||||
response = make_response(send_from_directory(
|
||||
os.path.dirname(filename), os.path.basename(filename), as_attachment=True))
|
||||
return response
|
||||
|
||||
def zipApi(self):
|
||||
sfile = request.form.get('sfile', '')
|
||||
dfile = request.form.get('dfile', '')
|
||||
stype = request.form.get('type', '')
|
||||
path = request.form.get('path', '')
|
||||
return self.zip(sfile, dfile, stype, path)
|
||||
|
||||
def unzipApi(self):
|
||||
sfile = request.form.get('sfile', '')
|
||||
dfile = request.form.get('dfile', '')
|
||||
stype = request.form.get('type', '')
|
||||
path = request.form.get('path', '')
|
||||
return self.unzip(sfile, dfile, stype, path)
|
||||
|
||||
# 移动文件或目录
|
||||
def mvFileApi(self):
|
||||
sfile = request.form.get('sfile', '')
|
||||
dfile = request.form.get('dfile', '')
|
||||
if not self.checkFileName(dfile):
|
||||
return mw.returnJson(False, '文件名中不能包含特殊字符!')
|
||||
if not os.path.exists(sfile):
|
||||
return mw.returnJson(False, '指定文件不存在!')
|
||||
|
||||
if not self.checkDir(sfile):
|
||||
return mw.returnJson(False, 'FILE_DANGER')
|
||||
|
||||
import shutil
|
||||
try:
|
||||
shutil.move(sfile, dfile)
|
||||
msg = mw.getInfo('移动文件或目录[{1}]到[{2}]成功!', (sfile, dfile,))
|
||||
mw.writeLog('文件管理', msg)
|
||||
return mw.returnJson(True, '移动文件或目录成功!')
|
||||
except:
|
||||
return mw.returnJson(False, '移动文件或目录失败!')
|
||||
|
||||
def deleteApi(self):
|
||||
path = request.form.get('path', '')
|
||||
return self.delete(path)
|
||||
|
||||
def fileAccessApi(self):
|
||||
filename = request.form.get('filename', '')
|
||||
data = self.getAccess(filename)
|
||||
return mw.getJson(data)
|
||||
|
||||
def setFileAccessApi(self):
|
||||
|
||||
if mw.isAppleSystem():
|
||||
return mw.returnJson(True, '开发机不设置!')
|
||||
|
||||
filename = request.form.get('filename', '')
|
||||
user = request.form.get('user', '')
|
||||
access = request.form.get('access', '755')
|
||||
sall = '-R'
|
||||
try:
|
||||
if not self.checkDir(filename):
|
||||
return mw.returnJson(False, '请不要花样作死')
|
||||
|
||||
if not os.path.exists(filename):
|
||||
return mw.returnJson(False, '指定文件不存在!')
|
||||
|
||||
os.system('chmod ' + sall + ' ' + access + " '" + filename + "'")
|
||||
os.system('chown ' + sall + ' ' + user +
|
||||
':' + user + " '" + filename + "'")
|
||||
msg = mw.getInfo(
|
||||
'设置[{1}]权限为[{2}]所有者为[{3}]', (filename, access, user,))
|
||||
mw.writeLog('文件管理', msg)
|
||||
return mw.returnJson(True, '设置成功!')
|
||||
except:
|
||||
return mw.returnJson(False, '设置失败!')
|
||||
|
||||
def getDirSizeApi(self):
|
||||
path = request.form.get('path', '')
|
||||
tmp = self.getDirSize(path)
|
||||
return mw.returnJson(True, tmp[0].split()[0])
|
||||
|
||||
def getDirApi(self):
|
||||
path = request.form.get('path', '')
|
||||
if not os.path.exists(path):
|
||||
path = mw.getRootDir() + "/wwwroot"
|
||||
search = request.args.get('search', '').strip().lower()
|
||||
page = request.args.get('p', '1').strip().lower()
|
||||
row = request.args.get('showRow', '10')
|
||||
disk = request.form.get('disk', '')
|
||||
if disk == 'True':
|
||||
row = 1000
|
||||
|
||||
return self.getDir(path, int(page), int(row), search)
|
||||
|
||||
def createFileApi(self):
|
||||
file = request.form.get('path', '')
|
||||
try:
|
||||
if not self.checkFileName(file):
|
||||
return mw.returnJson(False, '文件名中不能包含特殊字符!')
|
||||
if os.path.exists(file):
|
||||
return mw.returnJson(False, '指定文件已存在!')
|
||||
_path = os.path.dirname(file)
|
||||
if not os.path.exists(_path):
|
||||
os.makedirs(_path)
|
||||
open(file, 'w+').close()
|
||||
self.setFileAccept(file)
|
||||
msg = mw.getInfo('创建文件[{1}]成功!', (file,))
|
||||
mw.writeLog('文件管理', msg)
|
||||
return mw.returnJson(True, '文件创建成功!')
|
||||
except Exception as e:
|
||||
return mw.returnJson(True, '文件创建失败!')
|
||||
|
||||
def createDirApi(self):
|
||||
path = request.form.get('path', '')
|
||||
try:
|
||||
if not self.checkFileName(path):
|
||||
return mw.returnJson(False, '目录名中不能包含特殊字符!')
|
||||
if os.path.exists(path):
|
||||
return mw.returnJson(False, '指定目录已存在!')
|
||||
os.makedirs(path)
|
||||
self.setFileAccept(path)
|
||||
msg = mw.getInfo('创建目录[{1}]成功!', (path,))
|
||||
mw.writeLog('文件管理', msg)
|
||||
return mw.returnJson(True, '目录创建成功!')
|
||||
except Exception as e:
|
||||
return mw.returnJson(False, '目录创建失败!')
|
||||
|
||||
def downloadFileApi(self):
|
||||
import db
|
||||
import time
|
||||
url = request.form.get('url', '')
|
||||
path = request.form.get('path', '')
|
||||
filename = request.form.get('filename', '')
|
||||
execstr = url + '|mw|' + path + '/' + filename
|
||||
execstr = execstr.strip()
|
||||
mw.M('tasks').add('name,type,status,addtime,execstr',
|
||||
('下载文件[' + filename + ']', 'download', '-1', time.strftime('%Y-%m-%d %H:%M:%S'), execstr))
|
||||
|
||||
# self.setFileAccept(path + '/' + filename)
|
||||
mw.triggerTask()
|
||||
return mw.returnJson(True, '已将下载任务添加到队列!')
|
||||
|
||||
# 删除进程下的所有进程
|
||||
def removeTaskRecursion(self, pid):
|
||||
cmd = "ps -ef|grep " + pid + \
|
||||
" | grep -v grep |sed -n '2,1p' | awk '{print $2}'"
|
||||
sub_pid = mw.execShell(cmd)
|
||||
|
||||
if sub_pid[0].strip() == '':
|
||||
return 'ok'
|
||||
|
||||
self.removeTaskRecursion(sub_pid[0].strip())
|
||||
mw.execShell('kill -9 ' + sub_pid[0].strip())
|
||||
return sub_pid[0].strip()
|
||||
|
||||
def removeTaskApi(self):
|
||||
import system_api
|
||||
mid = request.form.get('id', '')
|
||||
try:
|
||||
name = mw.M('tasks').where('id=?', (mid,)).getField('name')
|
||||
status = mw.M('tasks').where('id=?', (mid,)).getField('status')
|
||||
mw.M('tasks').delete(mid)
|
||||
if status == '-1':
|
||||
task_pid = mw.execShell(
|
||||
"ps aux | grep 'task.py' | grep -v grep |awk '{print $2}'")
|
||||
|
||||
task_list = task_pid[0].strip().split("\n")
|
||||
for i in range(len(task_list)):
|
||||
self.removeTaskRecursion(task_list[i])
|
||||
|
||||
mw.triggerTask()
|
||||
system_api.system_api().restartTask()
|
||||
except:
|
||||
system_api.system_api().restartTask()
|
||||
return mw.returnJson(True, '任务已删除!')
|
||||
|
||||
# 上传文件
|
||||
def uploadFileApi(self):
|
||||
from werkzeug.utils import secure_filename
|
||||
from flask import request
|
||||
|
||||
path = request.args.get('path', '')
|
||||
|
||||
if not os.path.exists(path):
|
||||
os.makedirs(path)
|
||||
f = request.files['zunfile']
|
||||
filename = os.path.join(path, f.filename)
|
||||
|
||||
s_path = path
|
||||
if os.path.exists(filename):
|
||||
s_path = filename
|
||||
p_stat = os.stat(s_path)
|
||||
f.save(filename)
|
||||
os.chown(filename, p_stat.st_uid, p_stat.st_gid)
|
||||
os.chmod(filename, p_stat.st_mode)
|
||||
|
||||
msg = mw.getInfo('上传文件[{1}] 到 [{2}]成功!', (filename, path))
|
||||
mw.writeLog('文件管理', msg)
|
||||
return mw.returnMsg(True, '上传成功!')
|
||||
|
||||
def getRecycleBinApi(self):
|
||||
rPath = self.rPath
|
||||
if not os.path.exists(rPath):
|
||||
os.system('mkdir -p ' + rPath)
|
||||
data = {}
|
||||
data['dirs'] = []
|
||||
data['files'] = []
|
||||
data['status'] = os.path.exists('data/recycle_bin.pl')
|
||||
data['status_db'] = os.path.exists('data/recycle_bin_db.pl')
|
||||
for file in os.listdir(rPath):
|
||||
try:
|
||||
tmp = {}
|
||||
fname = rPath + file
|
||||
tmp1 = file.split('_mw_')
|
||||
tmp2 = tmp1[len(tmp1) - 1].split('_t_')
|
||||
tmp['rname'] = file
|
||||
tmp['dname'] = file.replace('_mw_', '/').split('_t_')[0]
|
||||
tmp['name'] = tmp2[0]
|
||||
tmp['time'] = int(float(tmp2[1]))
|
||||
if os.path.islink(fname):
|
||||
filePath = os.readlink(fname)
|
||||
link = ' -> ' + filePath
|
||||
if os.path.exists(filePath):
|
||||
tmp['size'] = os.path.getsize(filePath)
|
||||
else:
|
||||
tmp['size'] = 0
|
||||
else:
|
||||
tmp['size'] = os.path.getsize(fname)
|
||||
if os.path.isdir(fname):
|
||||
data['dirs'].append(tmp)
|
||||
else:
|
||||
data['files'].append(tmp)
|
||||
except Exception as e:
|
||||
continue
|
||||
return mw.returnJson(True, 'OK', data)
|
||||
|
||||
# 回收站开关
|
||||
def recycleBinApi(self):
|
||||
c = 'data/recycle_bin.pl'
|
||||
db = request.form.get('db', '')
|
||||
if db != '':
|
||||
c = 'data/recycle_bin_db.pl'
|
||||
if os.path.exists(c):
|
||||
os.remove(c)
|
||||
mw.writeLog('文件管理', '已关闭回收站功能!')
|
||||
return mw.returnJson(True, '已关闭回收站功能!')
|
||||
else:
|
||||
mw.writeFile(c, 'True')
|
||||
mw.writeLog('文件管理', '已开启回收站功能!')
|
||||
return mw.returnJson(True, '已开启回收站功能!')
|
||||
|
||||
def reRecycleBinApi(self):
|
||||
rPath = self.rPath
|
||||
path = request.form.get('path', '')
|
||||
dFile = path.replace('_mw_', '/').split('_t_')[0]
|
||||
try:
|
||||
import shutil
|
||||
shutil.move(rPath + path, dFile)
|
||||
msg = mw.getInfo('移动文件[{1}]到回收站成功!', (dFile,))
|
||||
mw.writeLog('文件管理', msg)
|
||||
return mw.returnJson(True, '恢复成功!')
|
||||
except Exception as e:
|
||||
msg = mw.getInfo('从回收站恢复[{1}]失败!', (dFile,))
|
||||
mw.writeLog('文件管理', msg)
|
||||
return mw.returnJson(False, '恢复失败!')
|
||||
|
||||
def delRecycleBinApi(self):
|
||||
rPath = self.rPath
|
||||
path = request.form.get('path', '')
|
||||
empty = request.form.get('empty', '')
|
||||
dFile = path.split('_t_')[0]
|
||||
|
||||
if not self.checkDir(path):
|
||||
return mw.returnJson(False, '敏感目录,请不要花样作死!')
|
||||
|
||||
mw.execShell('which chattr && chattr -R -i ' + rPath + path)
|
||||
if os.path.isdir(rPath + path):
|
||||
import shutil
|
||||
shutil.rmtree(rPath + path)
|
||||
else:
|
||||
os.remove(rPath + path)
|
||||
|
||||
tfile = path.replace('_mw_', '/').split('_t_')[0]
|
||||
msg = mw.getInfo('已彻底从回收站删除{1}!', (tfile,))
|
||||
mw.writeLog('文件管理', msg)
|
||||
return mw.returnJson(True, msg)
|
||||
|
||||
# 获取进度
|
||||
def getSpeedApi(self):
|
||||
data = mw.getSpeed()
|
||||
return mw.returnJson(True, '已清空回收站!', data)
|
||||
|
||||
def closeRecycleBinApi(self):
|
||||
rPath = self.rPath
|
||||
mw.execShell('which chattr && chattr -R -i ' + rPath)
|
||||
rlist = os.listdir(rPath)
|
||||
i = 0
|
||||
l = len(rlist)
|
||||
for name in rlist:
|
||||
i += 1
|
||||
path = rPath + name
|
||||
mw.writeSpeed(name, i, l)
|
||||
if os.path.isdir(path):
|
||||
shutil.rmtree(path)
|
||||
else:
|
||||
os.remove(path)
|
||||
mw.writeSpeed(None, 0, 0)
|
||||
mw.writeLog('文件管理', '已清空回收站!')
|
||||
return mw.returnJson(True, '已清空回收站!')
|
||||
|
||||
def deleteDirApi(self):
|
||||
path = request.form.get('path', '')
|
||||
if not os.path.exists(path):
|
||||
return mw.returnJson(False, '指定文件不存在!')
|
||||
|
||||
# 检查是否为.user.ini
|
||||
if path.find('.user.ini'):
|
||||
os.system("which chattr && chattr -i '" + path + "'")
|
||||
try:
|
||||
if os.path.exists('data/recycle_bin.pl'):
|
||||
if self.mvRecycleBin(path):
|
||||
return mw.returnJson(True, '已将文件移动到回收站!')
|
||||
mw.execShell('rm -rf ' + path)
|
||||
mw.writeLog('文件管理', '删除文件成功!', (path,))
|
||||
return mw.returnJson(True, '删除文件成功!')
|
||||
except:
|
||||
return mw.returnJson(False, '删除文件失败!')
|
||||
|
||||
def closeLogsApi(self):
|
||||
logPath = mw.getLogsDir()
|
||||
os.system('rm -f ' + logPath + '/*')
|
||||
os.system('kill -USR1 `cat ' + mw.getServerDir() +
|
||||
'openresty/nginx/logs/nginx.pid`')
|
||||
mw.writeLog('文件管理', '网站日志已被清空!')
|
||||
tmp = self.getDirSize(logPath)
|
||||
return mw.returnJson(True, tmp[0].split()[0])
|
||||
|
||||
def setBatchDataApi(self):
|
||||
path = request.form.get('path', '')
|
||||
stype = request.form.get('type', '')
|
||||
access = request.form.get('access', '')
|
||||
user = request.form.get('user', '')
|
||||
data = request.form.get('data')
|
||||
if stype == '1' or stype == '2':
|
||||
session['selected'] = {
|
||||
'path': path,
|
||||
'type': stype,
|
||||
'access': access,
|
||||
'user': user,
|
||||
'data': data
|
||||
}
|
||||
return mw.returnJson(True, '标记成功,请在目标目录点击粘贴所有按钮!')
|
||||
elif stype == '3':
|
||||
for key in json.loads(data):
|
||||
try:
|
||||
filename = path + '/' + key
|
||||
if not self.checkDir(filename):
|
||||
return mw.returnJson(False, 'FILE_DANGER')
|
||||
os.system('chmod -R ' + access + " '" + filename + "'")
|
||||
os.system('chown -R ' + user + ':' +
|
||||
user + " '" + filename + "'")
|
||||
except:
|
||||
continue
|
||||
mw.writeLog('文件管理', '批量设置权限成功!')
|
||||
return mw.returnJson(True, '批量设置权限成功!')
|
||||
else:
|
||||
import shutil
|
||||
isRecyle = os.path.exists('data/recycle_bin.pl')
|
||||
data = json.loads(data)
|
||||
l = len(data)
|
||||
i = 0
|
||||
for key in data:
|
||||
try:
|
||||
filename = path + '/' + key
|
||||
topath = filename
|
||||
if not os.path.exists(filename):
|
||||
continue
|
||||
|
||||
i += 1
|
||||
mw.writeSpeed(key, i, l)
|
||||
if os.path.isdir(filename):
|
||||
if not self.checkDir(filename):
|
||||
return mw.returnJson(False, '请不要花样作死!')
|
||||
if isRecyle:
|
||||
self.mvRecycleBin(topath)
|
||||
else:
|
||||
shutil.rmtree(filename)
|
||||
else:
|
||||
if key == '.user.ini':
|
||||
os.system('which chattr && chattr -i ' + filename)
|
||||
if isRecyle:
|
||||
self.mvRecycleBin(topath)
|
||||
else:
|
||||
os.remove(filename)
|
||||
except:
|
||||
continue
|
||||
mw.writeSpeed(None, 0, 0)
|
||||
mw.writeLog('文件管理', '批量删除成功!')
|
||||
return mw.returnJson(True, '批量删除成功!')
|
||||
|
||||
def checkExistsFilesApi(self):
|
||||
dfile = request.form.get('dfile', '')
|
||||
filename = request.form.get('filename', '')
|
||||
data = []
|
||||
filesx = []
|
||||
if filename == '':
|
||||
filesx = json.loads(session['selected']['data'])
|
||||
else:
|
||||
filesx.append(filename)
|
||||
|
||||
for fn in filesx:
|
||||
if fn == '.':
|
||||
continue
|
||||
filename = dfile + '/' + fn
|
||||
if os.path.exists(filename):
|
||||
tmp = {}
|
||||
stat = os.stat(filename)
|
||||
tmp['filename'] = fn
|
||||
tmp['size'] = os.path.getsize(filename)
|
||||
tmp['mtime'] = str(int(stat.st_mtime))
|
||||
data.append(tmp)
|
||||
return mw.returnJson(True, 'ok', data)
|
||||
|
||||
def batchPasteApi(self):
|
||||
path = request.form.get('path', '')
|
||||
stype = request.form.get('type', '')
|
||||
# filename = request.form.get('filename', '')
|
||||
import shutil
|
||||
if not self.checkDir(path):
|
||||
return mw.returnJson(False, '请不要花样作死!')
|
||||
i = 0
|
||||
myfiles = json.loads(session['selected']['data'])
|
||||
l = len(myfiles)
|
||||
if stype == '1':
|
||||
for key in myfiles:
|
||||
i += 1
|
||||
mw.writeSpeed(key, i, l)
|
||||
try:
|
||||
|
||||
sfile = session['selected'][
|
||||
'path'] + '/' + key
|
||||
dfile = path + '/' + key
|
||||
|
||||
if os.path.isdir(sfile):
|
||||
shutil.copytree(sfile, dfile)
|
||||
else:
|
||||
shutil.copyfile(sfile, dfile)
|
||||
stat = os.stat(sfile)
|
||||
os.chown(dfile, stat.st_uid, stat.st_gid)
|
||||
except:
|
||||
continue
|
||||
msg = mw.getInfo('从[{1}]批量复制到[{2}]成功',
|
||||
(session['selected']['path'], path,))
|
||||
mw.writeLog('文件管理', msg)
|
||||
else:
|
||||
for key in myfiles:
|
||||
try:
|
||||
i += 1
|
||||
mw.writeSpeed(key, i, l)
|
||||
|
||||
sfile = session['selected'][
|
||||
'path'] + '/' + key
|
||||
dfile = path + '/' + key
|
||||
|
||||
shutil.move(sfile, dfile)
|
||||
except:
|
||||
continue
|
||||
msg = mw.getInfo('从[{1}]批量移动到[{2}]成功',
|
||||
(session['selected']['path'], path,))
|
||||
mw.writeLog('文件管理', msg)
|
||||
mw.writeSpeed(None, 0, 0)
|
||||
errorCount = len(myfiles) - i
|
||||
del(session['selected'])
|
||||
msg = mw.getInfo('批量操作成功[{1}],失败[{2}]', (str(i), str(errorCount)))
|
||||
return mw.returnJson(True, msg)
|
||||
|
||||
def copyFileApi(self):
|
||||
sfile = request.form.get('sfile', '')
|
||||
dfile = request.form.get('dfile', '')
|
||||
|
||||
if sfile == dfile:
|
||||
return mw.returnJson(False, '源与目的一致!')
|
||||
|
||||
if not os.path.exists(sfile):
|
||||
return mw.returnJson(False, '指定文件不存在!')
|
||||
|
||||
if os.path.isdir(sfile):
|
||||
return self.copyDir(sfile, dfile)
|
||||
|
||||
try:
|
||||
import shutil
|
||||
shutil.copyfile(sfile, dfile)
|
||||
msg = mw.getInfo('复制文件[{1}]到[{2}]成功!', (sfile, dfile,))
|
||||
mw.writeLog('文件管理', msg)
|
||||
stat = os.stat(sfile)
|
||||
os.chown(dfile, stat.st_uid, stat.st_gid)
|
||||
return mw.returnJson(True, '文件复制成功!')
|
||||
except:
|
||||
return mw.returnJson(False, '文件复制失败!')
|
||||
|
||||
##### ----- end ----- ###
|
||||
|
||||
def copyDir(self, sfile, dfile):
|
||||
|
||||
if not os.path.exists(sfile):
|
||||
return mw.returnJson(False, '指定目录不存在!')
|
||||
|
||||
if os.path.exists(dfile):
|
||||
return mw.returnJson(False, '指定目录已存在!')
|
||||
import shutil
|
||||
try:
|
||||
shutil.copytree(sfile, dfile)
|
||||
stat = os.stat(sfile)
|
||||
os.chown(dfile, stat.st_uid, stat.st_gid)
|
||||
msg = mw.getInfo('复制目录[{1}]到[{2}]成功!', (sfile, dfile))
|
||||
mw.writeLog('文件管理', msg)
|
||||
return mw.returnJson(True, '目录复制成功!')
|
||||
except:
|
||||
return mw.returnJson(False, '目录复制失败!')
|
||||
|
||||
# 检查敏感目录
|
||||
def checkDir(self, path):
|
||||
# path = str(path, encoding='utf-8')
|
||||
path = path.replace('//', '/')
|
||||
if path[-1:] == '/':
|
||||
path = path[:-1]
|
||||
|
||||
nDirs = ('',
|
||||
'/',
|
||||
'/*',
|
||||
'/www',
|
||||
'/root',
|
||||
'/boot',
|
||||
'/bin',
|
||||
'/etc',
|
||||
'/home',
|
||||
'/dev',
|
||||
'/sbin',
|
||||
'/var',
|
||||
'/usr',
|
||||
'/tmp',
|
||||
'/sys',
|
||||
'/proc',
|
||||
'/media',
|
||||
'/mnt',
|
||||
'/opt',
|
||||
'/lib',
|
||||
'/srv',
|
||||
'/selinux',
|
||||
'/www/server',
|
||||
mw.getRootDir())
|
||||
|
||||
return not path in nDirs
|
||||
|
||||
def getDirSize(self, path):
|
||||
if mw.getOs() == 'darwin':
|
||||
tmp = mw.execShell('du -sh ' + path)
|
||||
else:
|
||||
tmp = mw.execShell('du -sbh ' + path)
|
||||
return tmp
|
||||
|
||||
def checkFileName(self, filename):
|
||||
# 检测文件名
|
||||
nots = ['\\', '&', '*', '|', ';']
|
||||
if filename.find('/') != -1:
|
||||
filename = filename.split('/')[-1]
|
||||
for n in nots:
|
||||
if n in filename:
|
||||
return False
|
||||
return True
|
||||
|
||||
def setFileAccept(self, filename):
|
||||
auth = 'www:www'
|
||||
if mw.getOs() == 'darwin':
|
||||
user = mw.execShell(
|
||||
"who | sed -n '2, 1p' |awk '{print $1}'")[0].strip()
|
||||
auth = user + ':staff'
|
||||
|
||||
os.system('chown -R ' + auth + ' ' + filename)
|
||||
os.system('chmod -R 755 ' + filename)
|
||||
|
||||
# 移动到回收站
|
||||
def mvRecycleBin(self, path):
|
||||
rPath = self.rPath
|
||||
if not os.path.exists(rPath):
|
||||
os.system('mkdir -p ' + rPath)
|
||||
|
||||
rFile = rPath + path.replace('/', '_mw_') + '_t_' + str(time.time())
|
||||
try:
|
||||
import shutil
|
||||
shutil.move(path, rFile)
|
||||
mw.writeLog('文件管理', mw.getInfo(
|
||||
'移动文件[{1}]到回收站成功!', (path)))
|
||||
return True
|
||||
except:
|
||||
mw.writeLog('文件管理', mw.getInfo(
|
||||
'移动文件[{1}]到回收站失败!', (path)))
|
||||
return False
|
||||
|
||||
def getBody(self, path):
|
||||
if not os.path.exists(path):
|
||||
return mw.returnJson(False, '文件不存在', (path,))
|
||||
|
||||
if os.path.getsize(path) > 2097152:
|
||||
return mw.returnJson(False, '不能在线编辑大于2MB的文件!')
|
||||
|
||||
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'
|
||||
|
||||
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'
|
||||
|
||||
return mw.returnJson(True, 'OK', data)
|
||||
except Exception as ex:
|
||||
return mw.returnJson(False, '文件编码不被兼容,无法正确读取文件!' + str(ex))
|
||||
|
||||
def saveBody(self, path, data, encoding='utf-8'):
|
||||
if not os.path.exists(path):
|
||||
return mw.returnJson(False, '文件不存在')
|
||||
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)
|
||||
fp.write(data)
|
||||
fp.close()
|
||||
|
||||
if path.find("web_conf") > 0:
|
||||
mw.restartWeb()
|
||||
|
||||
mw.writeLog('文件管理', '文件保存成功', (path,))
|
||||
return mw.returnJson(True, '文件保存成功')
|
||||
except Exception as ex:
|
||||
return mw.returnJson(False, '文件保存错误:' + str(ex))
|
||||
|
||||
def zip(self, sfile, dfile, stype, path):
|
||||
if sfile.find(',') == -1:
|
||||
if not os.path.exists(path + '/' + sfile):
|
||||
return mw.returnMsg(False, '指定文件不存在!')
|
||||
|
||||
try:
|
||||
tmps = mw.getRunDir() + '/tmp/panelExec.log'
|
||||
if stype == 'zip':
|
||||
mw.execShell("cd '" + path + "' && zip '" + dfile +
|
||||
"' -r '" + sfile + "' > " + tmps + " 2>&1")
|
||||
else:
|
||||
sfiles = ''
|
||||
for sfile in sfile.split(','):
|
||||
if not sfile:
|
||||
continue
|
||||
sfiles += " '" + sfile + "'"
|
||||
mw.execShell("cd '" + path + "' && tar -zcvf '" +
|
||||
dfile + "' " + sfiles + " > " + tmps + " 2>&1")
|
||||
self.setFileAccept(dfile)
|
||||
mw.writeLog("文件管理", '文件压缩成功!', (sfile, dfile))
|
||||
return mw.returnJson(True, '文件压缩成功!')
|
||||
except:
|
||||
return mw.returnJson(False, '文件压缩失败!')
|
||||
|
||||
def unzip(self, sfile, dfile, stype, path):
|
||||
|
||||
if not os.path.exists(sfile):
|
||||
return mw.returnMsg(False, '指定文件不存在!')
|
||||
|
||||
try:
|
||||
tmps = mw.getRunDir() + '/tmp/panelExec.log'
|
||||
if stype == 'zip':
|
||||
mw.execShell("cd " + path + " && unzip -o -d '" + dfile +
|
||||
"' '" + sfile + "' > " + tmps + " 2>&1 &")
|
||||
else:
|
||||
sfiles = ''
|
||||
for sfile in sfile.split(','):
|
||||
if not sfile:
|
||||
continue
|
||||
sfiles += " '" + sfile + "'"
|
||||
mw.execShell("cd " + path + " && tar -zxvf " + sfiles +
|
||||
" -C " + dfile + " > " + tmps + " 2>&1 &")
|
||||
|
||||
self.setFileAccept(dfile)
|
||||
mw.writeLog("文件管理", '文件解压成功!', (sfile, dfile))
|
||||
return mw.returnJson(True, '文件解压成功!')
|
||||
except:
|
||||
return mw.returnJson(False, '文件解压失败!')
|
||||
|
||||
def delete(self, path):
|
||||
|
||||
if not os.path.exists(path):
|
||||
return mw.returnJson(False, '指定文件不存在!')
|
||||
|
||||
# 检查是否为.user.ini
|
||||
if path.find('.user.ini') >= 0:
|
||||
mw.execShell("which chattr && chattr -i '" + path + "'")
|
||||
|
||||
try:
|
||||
if os.path.exists('data/recycle_bin.pl'):
|
||||
if self.mvRecycleBin(path):
|
||||
return mw.returnJson(True, '已将文件移动到回收站!')
|
||||
os.remove(path)
|
||||
mw.writeLog('文件管理', mw.getInfo(
|
||||
'删除文件[{1}]成功!', (path)))
|
||||
return mw.returnJson(True, '删除文件成功!')
|
||||
except:
|
||||
return mw.returnJson(False, '删除文件失败!')
|
||||
|
||||
def getAccess(self, filename):
|
||||
data = {}
|
||||
try:
|
||||
stat = os.stat(filename)
|
||||
data['chmod'] = str(oct(stat.st_mode)[-3:])
|
||||
data['chown'] = pwd.getpwuid(stat.st_uid).pw_name
|
||||
except:
|
||||
data['chmod'] = 755
|
||||
data['chown'] = 'www'
|
||||
return data
|
||||
|
||||
# 计算文件数量
|
||||
def getCount(self, path, search):
|
||||
i = 0
|
||||
for name in os.listdir(path):
|
||||
if search:
|
||||
if name.lower().find(search) == -1:
|
||||
continue
|
||||
# if name[0:1] == '.':
|
||||
# continue
|
||||
i += 1
|
||||
return i
|
||||
|
||||
def getDir(self, path, page=1, page_size=10, search=None):
|
||||
data = {}
|
||||
dirnames = []
|
||||
filenames = []
|
||||
|
||||
info = {}
|
||||
info['count'] = self.getCount(path, search)
|
||||
info['row'] = page_size
|
||||
info['p'] = page
|
||||
info['tojs'] = 'getFiles'
|
||||
pageObj = mw.getPageObject(info, '1,2,3,4,5,6,7,8')
|
||||
data['PAGE'] = pageObj[0]
|
||||
|
||||
i = 0
|
||||
n = 0
|
||||
for filename in os.listdir(path):
|
||||
if search:
|
||||
if filename.lower().find(search) == -1:
|
||||
continue
|
||||
i += 1
|
||||
if n >= pageObj[1].ROW:
|
||||
break
|
||||
if i < pageObj[1].SHIFT:
|
||||
continue
|
||||
try:
|
||||
filePath = path + '/' + filename
|
||||
link = ''
|
||||
if os.path.islink(filePath):
|
||||
filePath = os.readlink(filePath)
|
||||
link = ' -> ' + filePath
|
||||
if not os.path.exists(filePath):
|
||||
filePath = path + '/' + filePath
|
||||
if not os.path.exists(filePath):
|
||||
continue
|
||||
|
||||
stat = os.stat(filePath)
|
||||
accept = str(oct(stat.st_mode)[-3:])
|
||||
mtime = str(int(stat.st_mtime))
|
||||
user = ''
|
||||
try:
|
||||
user = pwd.getpwuid(stat.st_uid).pw_name
|
||||
except Exception as ee:
|
||||
user = str(stat.st_uid)
|
||||
|
||||
size = str(stat.st_size)
|
||||
if os.path.isdir(filePath):
|
||||
dirnames.append(filename + ';' + size + ';' +
|
||||
mtime + ';' + accept + ';' + user + ';' + link)
|
||||
else:
|
||||
filenames.append(filename + ';' + size + ';' +
|
||||
mtime + ';' + accept + ';' + user + ';' + link)
|
||||
n += 1
|
||||
except Exception as e:
|
||||
continue
|
||||
data['DIR'] = sorted(dirnames)
|
||||
data['FILES'] = sorted(filenames)
|
||||
data['PATH'] = path.replace('//', '/')
|
||||
|
||||
return mw.getJson(data)
|
||||
@@ -1,463 +0,0 @@
|
||||
# coding: utf-8
|
||||
|
||||
# ---------------------------------------------------------------------------------
|
||||
# MW-Linux面板
|
||||
# ---------------------------------------------------------------------------------
|
||||
# copyright (c) 2018-∞(https://github.com/midoks/mdserver-web) All rights reserved.
|
||||
# ---------------------------------------------------------------------------------
|
||||
# Author: midoks <midoks@163.com>
|
||||
# ---------------------------------------------------------------------------------
|
||||
|
||||
# ---------------------------------------------------------------------------------
|
||||
# 防火墙操作
|
||||
# ---------------------------------------------------------------------------------
|
||||
|
||||
|
||||
import psutil
|
||||
import time
|
||||
import os
|
||||
import sys
|
||||
import mw
|
||||
import re
|
||||
import json
|
||||
import pwd
|
||||
|
||||
from flask import request
|
||||
|
||||
|
||||
class firewall_api:
|
||||
|
||||
__isFirewalld = False
|
||||
__isUfw = False
|
||||
__isMac = False
|
||||
|
||||
def __init__(self):
|
||||
if os.path.exists('/usr/sbin/firewalld'):
|
||||
self.__isFirewalld = True
|
||||
if os.path.exists('/usr/sbin/ufw'):
|
||||
self.__isUfw = True
|
||||
if mw.isAppleSystem():
|
||||
self.__isMac = True
|
||||
|
||||
##### ----- start ----- ###
|
||||
# 添加屏蔽IP
|
||||
def addDropAddressApi(self):
|
||||
import re
|
||||
port = request.form.get('port', '').strip()
|
||||
ps = request.form.get('ps', '').strip()
|
||||
|
||||
rep = "^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}(\/\d{1,2})?$"
|
||||
if not re.search(rep, port):
|
||||
return mw.returnJson(False, '您输入的IP地址不合法!')
|
||||
address = port
|
||||
if mw.M('firewall').where("port=?", (address,)).count() > 0:
|
||||
return mw.returnJson(False, '您要放屏蔽的IP已存在屏蔽列表,无需重复处理!')
|
||||
if self.__isUfw:
|
||||
mw.execShell('ufw deny from ' + address + ' to any')
|
||||
else:
|
||||
if self.__isFirewalld:
|
||||
cmd = 'firewall-cmd --permanent --add-rich-rule=\'rule family=ipv4 source address="' + \
|
||||
address + '" drop\''
|
||||
mw.execShell(cmd)
|
||||
else:
|
||||
cmd = 'iptables -I INPUT -s ' + address + ' -j DROP'
|
||||
mw.execShell(cmd)
|
||||
|
||||
msg = mw.getInfo('屏蔽IP[{1}]成功!', (address,))
|
||||
mw.writeLog("防火墙管理", msg)
|
||||
addtime = time.strftime('%Y-%m-%d %X', time.localtime())
|
||||
mw.M('firewall').add('port,ps,addtime', (address, ps, addtime))
|
||||
self.firewallReload()
|
||||
return mw.returnJson(True, '添加成功!')
|
||||
|
||||
# 添加放行端口
|
||||
def addAcceptPortApi(self):
|
||||
if not self.getFwStatus():
|
||||
return mw.returnJson(False, '防火墙启动时,才能添加规则!')
|
||||
|
||||
port = request.form.get('port', '').strip()
|
||||
ps = request.form.get('ps', '').strip()
|
||||
stype = request.form.get('type', '').strip()
|
||||
|
||||
data = self.addAcceptPortArgs(port, ps, stype)
|
||||
return mw.getJson(data)
|
||||
|
||||
# 添加放行端口
|
||||
def addAcceptPortArgs(self, port, ps, stype):
|
||||
import re
|
||||
import time
|
||||
|
||||
if not self.getFwStatus():
|
||||
self.setFw(0)
|
||||
|
||||
rep = "^\d{1,5}(:\d{1,5})?$"
|
||||
if not re.search(rep, port):
|
||||
return mw.returnData(False, '端口范围不正确!')
|
||||
|
||||
if mw.M('firewall').where("port=?", (port,)).count() > 0:
|
||||
return mw.returnData(False, '您要放行的端口已存在,无需重复放行!')
|
||||
|
||||
msg = mw.getInfo('放行端口[{1}]成功', (port,))
|
||||
mw.writeLog("防火墙管理", msg)
|
||||
addtime = time.strftime('%Y-%m-%d %X', time.localtime())
|
||||
mw.M('firewall').add('port,ps,addtime', (port, ps, addtime))
|
||||
|
||||
self.addAcceptPort(port)
|
||||
self.firewallReload()
|
||||
return mw.returnData(True, '添加放行(' + port + ')端口成功!')
|
||||
|
||||
# 删除IP屏蔽
|
||||
def delDropAddressApi(self):
|
||||
if not self.getFwStatus():
|
||||
return mw.returnJson(False, '防火墙启动时,才能删除规则!')
|
||||
|
||||
port = request.form.get('port', '').strip()
|
||||
ps = request.form.get('ps', '').strip()
|
||||
sid = request.form.get('id', '').strip()
|
||||
address = port
|
||||
if self.__isUfw:
|
||||
mw.execShell('ufw delete deny from ' + address + ' to any')
|
||||
else:
|
||||
if self.__isFirewalld:
|
||||
mw.execShell(
|
||||
'firewall-cmd --permanent --remove-rich-rule=\'rule family=ipv4 source address="' + address + '" drop\'')
|
||||
elif self.__isMac:
|
||||
pass
|
||||
else:
|
||||
cmd = 'iptables -D INPUT -s ' + address + ' -j DROP'
|
||||
mw.execShell(cmd)
|
||||
|
||||
msg = mw.getInfo('解除IP[{1}]的屏蔽!', (address,))
|
||||
mw.writeLog("防火墙管理", msg)
|
||||
mw.M('firewall').where("id=?", (sid,)).delete()
|
||||
|
||||
self.firewallReload()
|
||||
return mw.returnJson(True, '删除成功!')
|
||||
|
||||
# 删除放行端口
|
||||
def delAcceptPortApi(self):
|
||||
port = request.form.get('port', '').strip()
|
||||
sid = request.form.get('id', '').strip()
|
||||
mw_port = mw.readFile('data/port.pl')
|
||||
try:
|
||||
if(port == mw_port):
|
||||
return mw.returnJson(False, '失败,不能删除当前面板端口!')
|
||||
if self.__isUfw:
|
||||
mw.execShell('ufw delete allow ' + port + '/tcp')
|
||||
else:
|
||||
if self.__isFirewalld:
|
||||
mw.execShell(
|
||||
'firewall-cmd --permanent --zone=public --remove-port=' + port + '/tcp')
|
||||
mw.execShell(
|
||||
'firewall-cmd --permanent --zone=public --remove-port=' + port + '/udp')
|
||||
else:
|
||||
mw.execShell(
|
||||
'iptables -D INPUT -p tcp -m state --state NEW -m tcp --dport ' + port + ' -j ACCEPT')
|
||||
msg = mw.getInfo('删除防火墙放行端口[{1}]成功!', (port,))
|
||||
mw.writeLog("防火墙管理", msg)
|
||||
mw.M('firewall').where("id=?", (sid,)).delete()
|
||||
|
||||
self.firewallReload()
|
||||
return mw.returnJson(True, '删除成功!')
|
||||
except Exception as e:
|
||||
return mw.returnJson(False, '删除失败!:' + str(e))
|
||||
|
||||
def getWwwPathApi(self):
|
||||
path = mw.getLogsDir()
|
||||
return mw.getJson({'path': path})
|
||||
|
||||
def getListApi(self):
|
||||
p = request.form.get('p', '1').strip()
|
||||
limit = request.form.get('limit', '10').strip()
|
||||
return self.getList(int(p), int(limit))
|
||||
|
||||
def getLogListApi(self):
|
||||
p = request.form.get('p', '1').strip()
|
||||
limit = request.form.get('limit', '10').strip()
|
||||
search = request.form.get('search', '').strip()
|
||||
return self.getLogList(int(p), int(limit), search)
|
||||
|
||||
def getSshInfoApi(self):
|
||||
data = {}
|
||||
|
||||
file = '/etc/ssh/sshd_config'
|
||||
conf = mw.readFile(file)
|
||||
rep = "#*Port\s+([0-9]+)\s*\n"
|
||||
port = re.search(rep, conf).groups(0)[0]
|
||||
|
||||
isPing = True
|
||||
try:
|
||||
if mw.isAppleSystem():
|
||||
isPing = True
|
||||
else:
|
||||
file = '/etc/sysctl.conf'
|
||||
sys_conf = mw.readFile(file)
|
||||
rep = "#*net\.ipv4\.icmp_echo_ignore_all\s*=\s*([0-9]+)"
|
||||
tmp = re.search(rep, sys_conf).groups(0)[0]
|
||||
if tmp == '1':
|
||||
isPing = False
|
||||
except:
|
||||
isPing = True
|
||||
|
||||
# sshd 检测
|
||||
status = True
|
||||
cmd = "service sshd status | grep -P '(dead|stop)'|grep -v grep"
|
||||
ssh_status = mw.execShell(cmd)
|
||||
if ssh_status[0] != '':
|
||||
status = False
|
||||
|
||||
cmd = "systemctl status sshd.service | grep 'dead'|grep -v grep"
|
||||
ssh_status = mw.execShell(cmd)
|
||||
if ssh_status[0] != '':
|
||||
status = False
|
||||
|
||||
data['pass_prohibit_status'] = False
|
||||
# 密码登陆配置检查
|
||||
pass_rep = "#PasswordAuthentication\s+(\w*)\s*\n"
|
||||
pass_status = re.search(pass_rep, conf)
|
||||
if pass_status:
|
||||
data['pass_prohibit_status'] = True
|
||||
|
||||
if not data['pass_prohibit_status']:
|
||||
pass_rep = "PasswordAuthentication\s+(\w*)\s*\n"
|
||||
pass_status = re.search(pass_rep, conf)
|
||||
if pass_status and pass_status.groups(0)[0].strip() == 'no':
|
||||
data['pass_prohibit_status'] = True
|
||||
|
||||
data['port'] = port
|
||||
data['status'] = status
|
||||
data['ping'] = isPing
|
||||
if mw.isAppleSystem():
|
||||
data['firewall_status'] = False
|
||||
else:
|
||||
data['firewall_status'] = self.getFwStatus()
|
||||
return mw.getJson(data)
|
||||
|
||||
def setSshPortApi(self):
|
||||
port = request.form.get('port', '1').strip()
|
||||
if int(port) < 22 or int(port) > 65535:
|
||||
return mw.returnJson(False, '端口范围必需在22-65535之间!')
|
||||
|
||||
ports = ['21', '25', '80', '443', '888']
|
||||
if port in ports:
|
||||
return mw.returnJson(False, '(' + port + ')' + '特殊端口不可设置!')
|
||||
|
||||
file = '/etc/ssh/sshd_config'
|
||||
conf = mw.readFile(file)
|
||||
|
||||
rep = "#*Port\s+([0-9]+)\s*\n"
|
||||
conf = re.sub(rep, "Port " + port + "\n", conf)
|
||||
mw.writeFile(file, conf)
|
||||
|
||||
if self.__isFirewalld:
|
||||
mw.execShell('setenforce 0')
|
||||
mw.execShell(
|
||||
'sed -i "s#SELINUX=enforcing#SELINUX=disabled#" /etc/selinux/config')
|
||||
mw.execShell("systemctl restart sshd.service")
|
||||
elif self.__isUfw:
|
||||
mw.execShell('ufw allow ' + port + '/tcp')
|
||||
mw.execShell("service ssh restart")
|
||||
else:
|
||||
mw.execShell(
|
||||
'iptables -I INPUT -p tcp -m state --state NEW -m tcp --dport ' + port + ' -j ACCEPT')
|
||||
mw.execShell("/etc/init.d/sshd restart")
|
||||
|
||||
self.firewallReload()
|
||||
# mw.M('firewall').where(
|
||||
# "ps=?", ('SSH远程管理服务',)).setField('port', port)
|
||||
msg = "改SSH端口为[{}]成功!".format(port)
|
||||
mw.writeLog("防火墙管理", msg)
|
||||
return mw.returnJson(True, '修改成功!')
|
||||
|
||||
def setSshStatusApi(self):
|
||||
if mw.isAppleSystem():
|
||||
return mw.returnJson(True, '开发机不能操作!')
|
||||
|
||||
status = request.form.get('status', '1').strip()
|
||||
msg = 'SSH服务已启用'
|
||||
act = 'start'
|
||||
if status == "1":
|
||||
msg = 'SSH服务已停用'
|
||||
act = 'stop'
|
||||
|
||||
ssh_service = mw.systemdCfgDir() + '/sshd.service'
|
||||
if os.path.exists(ssh_service):
|
||||
mw.execShell("systemctl " + act + " sshd.service")
|
||||
else:
|
||||
mw.execShell('service sshd ' + act)
|
||||
|
||||
if os.path.exists('/etc/init.d/sshd'):
|
||||
mw.execShell('/etc/init.d/sshd ' + act)
|
||||
|
||||
mw.writeLog("防火墙管理", msg)
|
||||
return mw.returnJson(True, '操作成功!')
|
||||
|
||||
def setSshPassStatusApi(self):
|
||||
if mw.isAppleSystem():
|
||||
return mw.returnJson(True, '开发机不能操作!')
|
||||
|
||||
status = request.form.get('status', '1').strip()
|
||||
msg = '禁止密码登陆成功'
|
||||
if status == "1":
|
||||
msg = '开启密码登陆成功'
|
||||
|
||||
file = '/etc/ssh/sshd_config'
|
||||
if not os.path.exists(file):
|
||||
return mw.returnJson(False, '无法设置!')
|
||||
|
||||
conf = mw.readFile(file)
|
||||
|
||||
if status == '1':
|
||||
rep = "(#)?PasswordAuthentication\s+(\w*)\s*\n"
|
||||
conf = re.sub(rep, "PasswordAuthentication yes\n", conf)
|
||||
else:
|
||||
rep = "(#)?PasswordAuthentication\s+(\w*)\s*\n"
|
||||
conf = re.sub(rep, "PasswordAuthentication no\n", conf)
|
||||
mw.writeFile(file, conf)
|
||||
mw.execShell("systemctl restart sshd.service")
|
||||
mw.writeLog("SSH管理", msg)
|
||||
return mw.returnJson(True, msg)
|
||||
|
||||
def setPingApi(self):
|
||||
if mw.isAppleSystem():
|
||||
return mw.returnJson(True, '开发机不能操作!')
|
||||
|
||||
status = request.form.get('status')
|
||||
filename = '/etc/sysctl.conf'
|
||||
conf = mw.readFile(filename)
|
||||
if conf.find('net.ipv4.icmp_echo') != -1:
|
||||
rep = u"net\.ipv4\.icmp_echo.*"
|
||||
conf = re.sub(rep, 'net.ipv4.icmp_echo_ignore_all=' + status, conf)
|
||||
else:
|
||||
conf += "\nnet.ipv4.icmp_echo_ignore_all=" + status
|
||||
|
||||
mw.writeFile(filename, conf)
|
||||
mw.execShell('sysctl -p')
|
||||
return mw.returnJson(True, '设置成功!')
|
||||
|
||||
def setFwApi(self):
|
||||
if mw.isAppleSystem():
|
||||
return mw.returnJson(True, '开发机不能设置!')
|
||||
|
||||
status = request.form.get('status', '1')
|
||||
return mw.getJson(self.setFw(status))
|
||||
|
||||
def setFw(self, status):
|
||||
if status == '1':
|
||||
if self.__isUfw:
|
||||
mw.execShell('/usr/sbin/ufw disable')
|
||||
if self.__isFirewalld:
|
||||
mw.execShell('systemctl stop firewalld.service')
|
||||
mw.execShell('systemctl disable firewalld.service')
|
||||
elif self.__isMac:
|
||||
pass
|
||||
else:
|
||||
mw.execShell('/etc/init.d/iptables save')
|
||||
mw.execShell('/etc/init.d/iptables stop')
|
||||
else:
|
||||
if self.__isUfw:
|
||||
mw.execShell("echo 'y'|sudo ufw enable")
|
||||
if self.__isFirewalld:
|
||||
mw.execShell('systemctl start firewalld.service')
|
||||
mw.execShell('systemctl enable firewalld.service')
|
||||
elif self.__isMac:
|
||||
pass
|
||||
else:
|
||||
mw.execShell('/etc/init.d/iptables save')
|
||||
mw.execShell('/etc/init.d/iptables restart')
|
||||
|
||||
return mw.returnData(True, '设置成功!')
|
||||
|
||||
def delPanelLogsApi(self):
|
||||
mw.M('logs').where('id>?', (0,)).delete()
|
||||
mw.writeLog('面板设置', '面板操作日志已清空!')
|
||||
return mw.returnJson(True, '面板操作日志已清空!')
|
||||
|
||||
##### ----- start ----- ###
|
||||
|
||||
def getList(self, page, limit):
|
||||
|
||||
start = (page - 1) * limit
|
||||
|
||||
_list = mw.M('firewall').field('id,port,ps,addtime').limit(
|
||||
str(start) + ',' + str(limit)).order('id desc').select()
|
||||
data = {}
|
||||
data['data'] = _list
|
||||
|
||||
count = mw.M('firewall').count()
|
||||
_page = {}
|
||||
_page['count'] = count
|
||||
_page['tojs'] = 'showAccept'
|
||||
_page['p'] = page
|
||||
|
||||
data['page'] = mw.getPage(_page)
|
||||
return mw.getJson(data)
|
||||
|
||||
def getLogList(self, page, limit, search=''):
|
||||
find_search = ''
|
||||
if search != '':
|
||||
find_search = "type like '%" + search + "%' or log like '%" + \
|
||||
search + "%' or addtime like '%" + search + "%'"
|
||||
|
||||
start = (page - 1) * limit
|
||||
|
||||
_list = mw.M('logs').where(find_search, ()).field(
|
||||
'id,type,log,addtime').limit(str(start) + ',' + str(limit)).order('id desc').select()
|
||||
data = {}
|
||||
data['data'] = _list
|
||||
|
||||
count = mw.M('logs').where(find_search, ()).count()
|
||||
_page = {}
|
||||
_page['count'] = count
|
||||
_page['tojs'] = 'getLogs'
|
||||
_page['p'] = page
|
||||
|
||||
data['page'] = mw.getPage(_page)
|
||||
return mw.getJson(data)
|
||||
|
||||
def addAcceptPort(self, port):
|
||||
if self.__isUfw:
|
||||
mw.execShell('ufw allow ' + port + '/tcp')
|
||||
elif self.__isFirewalld:
|
||||
port = port.replace(':', '-')
|
||||
cmd = 'firewall-cmd --permanent --zone=public --add-port=' + port + '/tcp'
|
||||
mw.execShell(cmd)
|
||||
elif self.__isMac:
|
||||
pass
|
||||
else:
|
||||
cmd = 'iptables -I INPUT -p tcp -m state --state NEW -m tcp --dport ' + port + ' -j ACCEPT'
|
||||
mw.execShell(cmd)
|
||||
|
||||
def firewallReload(self):
|
||||
if self.__isUfw:
|
||||
mw.execShell('/usr/sbin/ufw reload')
|
||||
return
|
||||
if self.__isFirewalld:
|
||||
mw.execShell('firewall-cmd --reload')
|
||||
elif self.__isMac:
|
||||
pass
|
||||
else:
|
||||
mw.execShell('/etc/init.d/iptables save')
|
||||
mw.execShell('/etc/init.d/iptables restart')
|
||||
|
||||
def getFwStatus(self):
|
||||
if self.__isUfw:
|
||||
cmd = "/usr/sbin/ufw status| grep Status | awk -F ':' '{print $2}'"
|
||||
data = mw.execShell(cmd)
|
||||
if data[0].strip() == 'inactive':
|
||||
return False
|
||||
return True
|
||||
if self.__isFirewalld:
|
||||
cmd = "ps -ef|grep firewalld |grep -v grep | awk '{print $2}'"
|
||||
data = mw.execShell(cmd)
|
||||
if data[0] == '':
|
||||
return False
|
||||
return True
|
||||
elif self.__isMac:
|
||||
return False
|
||||
else:
|
||||
cmd = "ps -ef|grep iptables |grep -v grep | awk '{print $2}'"
|
||||
data = mw.execShell(cmd)
|
||||
if data[0] == '':
|
||||
return False
|
||||
return True
|
||||
@@ -1,237 +0,0 @@
|
||||
# coding: utf-8
|
||||
|
||||
# ---------------------------------------------------------------------------------
|
||||
# MW-Linux面板
|
||||
# ---------------------------------------------------------------------------------
|
||||
# copyright (c) 2018-∞(https://github.com/midoks/mdserver-web) All rights reserved.
|
||||
# ---------------------------------------------------------------------------------
|
||||
# Author: midoks <midoks@163.com>
|
||||
# ---------------------------------------------------------------------------------
|
||||
|
||||
# ---------------------------------------------------------------------------------
|
||||
# 分页库操作
|
||||
# ---------------------------------------------------------------------------------
|
||||
|
||||
|
||||
import math
|
||||
import string
|
||||
|
||||
|
||||
class Page():
|
||||
#--------------------------
|
||||
# 分页类 - JS回调版
|
||||
#--------------------------
|
||||
__PREV = '上一页'
|
||||
__NEXT = '下一页'
|
||||
__START = '首页'
|
||||
__END = '尾页'
|
||||
__COUNT_START = '共'
|
||||
__COUNT_END = '条数据'
|
||||
__FO = '从'
|
||||
__LINE = '条'
|
||||
__LIST_NUM = 4
|
||||
SHIFT = None # 偏移量
|
||||
ROW = None # 每页行数
|
||||
__C_PAGE = None # 当前页
|
||||
__COUNT_PAGE = None # 总页数
|
||||
__COUNT_ROW = None # 总行数
|
||||
__URI = None # URI
|
||||
__RTURN_JS = False # 是否返回JS回调
|
||||
__START_NUM = None # 起始行
|
||||
__END_NUM = None # 结束行
|
||||
|
||||
def __init__(self):
|
||||
if False:
|
||||
self.__PREV = tmp['PREV']
|
||||
self.__NEXT = tmp['NEXT']
|
||||
self.__START = tmp['START']
|
||||
self.__END = tmp['END']
|
||||
self.__COUNT_START = tmp['COUNT_START']
|
||||
self.__COUNT_END = tmp['COUNT_END']
|
||||
self.__FO = tmp['FO']
|
||||
self.__LINE = tmp['LINE']
|
||||
|
||||
def GetPage(self, pageInfo, limit='1,2,3,4,5,6,7,8'):
|
||||
# 取分页信息
|
||||
# @param pageInfo 传入分页参数字典
|
||||
# @param limit 返回系列
|
||||
self.__RTURN_JS = pageInfo['return_js']
|
||||
self.__COUNT_ROW = pageInfo['count']
|
||||
self.ROW = pageInfo['row']
|
||||
self.__C_PAGE = self.__GetCpage(pageInfo['p'])
|
||||
self.__START_NUM = self.__StartRow()
|
||||
self.__END_NUM = self.__EndRow()
|
||||
self.__COUNT_PAGE = self.__GetCountPage()
|
||||
self.__URI = self.__SetUri(pageInfo['uri'])
|
||||
self.SHIFT = self.__START_NUM - 1
|
||||
|
||||
keys = limit.split(',')
|
||||
pages = {}
|
||||
# 起始页
|
||||
pages['1'] = self.__GetStart()
|
||||
# 上一页
|
||||
pages['2'] = self.__GetPrev()
|
||||
# 分页
|
||||
pages['3'] = self.__GetPages()
|
||||
# 下一页
|
||||
pages['4'] = self.__GetNext()
|
||||
# 尾页
|
||||
pages['5'] = self.__GetEnd()
|
||||
|
||||
# 当前显示页与总页数
|
||||
pages['6'] = "<span class='Pnumber'>" + \
|
||||
str(self.__C_PAGE) + "/" + \
|
||||
str(self.__COUNT_PAGE) + "</span>"
|
||||
|
||||
# 本页显示开始与结束行
|
||||
pages['7'] = "<span class='Pline'>" + str(self.__FO) + \
|
||||
str(self.__START_NUM) + "-" + \
|
||||
str(self.__END_NUM) + str(self.__LINE) + "</span>"
|
||||
# 行数
|
||||
pages['8'] = "<span class='Pcount'>" + str(self.__COUNT_START) + \
|
||||
str(self.__COUNT_ROW) + str(self.__COUNT_END) + "</span>"
|
||||
|
||||
# 构造返回数据
|
||||
retuls = '<div>'
|
||||
for value in keys:
|
||||
retuls += pages[value]
|
||||
retuls += '</div>'
|
||||
|
||||
# 返回分页数据
|
||||
return retuls
|
||||
|
||||
def __GetEnd(self):
|
||||
# 构造尾页
|
||||
endStr = ""
|
||||
if self.__C_PAGE >= self.__COUNT_PAGE:
|
||||
endStr = ''
|
||||
else:
|
||||
if self.__RTURN_JS == "":
|
||||
endStr = "<a class='Pend' href='" + self.__URI + "p=" + \
|
||||
str(self.__COUNT_PAGE) + "'>" + str(self.__END) + "</a>"
|
||||
else:
|
||||
endStr = "<a class='Pend' onclick='" + self.__RTURN_JS + \
|
||||
"(" + str(self.__COUNT_PAGE) + ")'>" + \
|
||||
str(self.__END) + "</a>"
|
||||
return endStr
|
||||
|
||||
def __GetNext(self):
|
||||
# 构造下一页
|
||||
nextStr = ""
|
||||
if self.__C_PAGE >= self.__COUNT_PAGE:
|
||||
nextStr = ''
|
||||
else:
|
||||
if self.__RTURN_JS == "":
|
||||
nextStr = "<a class='Pnext' href='" + self.__URI + "p=" + \
|
||||
str(self.__C_PAGE + 1) + "'>" + str(self.__NEXT) + "</a>"
|
||||
else:
|
||||
nextStr = "<a class='Pnext' onclick='" + self.__RTURN_JS + \
|
||||
"(" + str(self.__C_PAGE + 1) + ")'>" + \
|
||||
str(self.__NEXT) + "</a>"
|
||||
|
||||
return nextStr
|
||||
|
||||
def __GetPages(self):
|
||||
# 构造分页
|
||||
pages = ''
|
||||
num = 0
|
||||
# 当前页之前
|
||||
if (self.__COUNT_PAGE - self.__C_PAGE) < self.__LIST_NUM:
|
||||
num = self.__LIST_NUM + \
|
||||
(self.__LIST_NUM - (self.__COUNT_PAGE - self.__C_PAGE))
|
||||
else:
|
||||
num = self.__LIST_NUM
|
||||
n = 0
|
||||
for i in range(num):
|
||||
n = num - i
|
||||
page = self.__C_PAGE - n
|
||||
if page > 0:
|
||||
if self.__RTURN_JS == "":
|
||||
pages += "<a class='Pnum' href='" + self.__URI + \
|
||||
"p=" + str(page) + "'>" + str(page) + "</a>"
|
||||
else:
|
||||
pages += "<a class='Pnum' onclick='" + self.__RTURN_JS + \
|
||||
"(" + str(page) + ")'>" + str(page) + "</a>"
|
||||
|
||||
# 当前页
|
||||
if self.__C_PAGE > 0:
|
||||
pages += "<span class='Pcurrent'>" + \
|
||||
str(self.__C_PAGE) + "</span>"
|
||||
|
||||
# 当前页之后
|
||||
if self.__C_PAGE <= self.__LIST_NUM:
|
||||
num = self.__LIST_NUM + (self.__LIST_NUM - self.__C_PAGE) + 1
|
||||
else:
|
||||
num = self.__LIST_NUM
|
||||
for i in range(num):
|
||||
if i == 0:
|
||||
continue
|
||||
page = self.__C_PAGE + i
|
||||
if page > self.__COUNT_PAGE:
|
||||
break
|
||||
if self.__RTURN_JS == "":
|
||||
pages += "<a class='Pnum' href='" + self.__URI + \
|
||||
"p=" + str(page) + "'>" + str(page) + "</a>"
|
||||
else:
|
||||
pages += "<a class='Pnum' onclick='" + self.__RTURN_JS + \
|
||||
"(" + str(page) + ")'>" + str(page) + "</a>"
|
||||
|
||||
return pages
|
||||
|
||||
def __GetPrev(self):
|
||||
# 构造上一页
|
||||
startStr = ''
|
||||
if self.__C_PAGE == 1:
|
||||
startStr = ''
|
||||
else:
|
||||
if self.__RTURN_JS == "":
|
||||
startStr = "<a class='Ppren' href='" + self.__URI + "p=" + \
|
||||
str(self.__C_PAGE - 1) + "'>" + str(self.__PREV) + "</a>"
|
||||
else:
|
||||
startStr = "<a class='Ppren' onclick='" + self.__RTURN_JS + \
|
||||
"(" + str(self.__C_PAGE - 1) + ")'>" + \
|
||||
str(self.__PREV) + "</a>"
|
||||
return startStr
|
||||
|
||||
def __GetStart(self):
|
||||
# 构造起始分页
|
||||
startStr = ''
|
||||
if self.__C_PAGE == 1:
|
||||
startStr = ''
|
||||
else:
|
||||
if self.__RTURN_JS == "":
|
||||
startStr = "<a class='Pstart' href='" + \
|
||||
self.__URI + "p=1'>" + str(self.__START) + "</a>"
|
||||
else:
|
||||
startStr = "<a class='Pstart' onclick='" + \
|
||||
self.__RTURN_JS + "(1)'>" + str(self.__START) + "</a>"
|
||||
return startStr
|
||||
|
||||
def __GetCpage(self, p):
|
||||
# 取当前页
|
||||
if p:
|
||||
return p
|
||||
return 1
|
||||
|
||||
def __StartRow(self):
|
||||
# 从多少行开始
|
||||
return (self.__C_PAGE - 1) * self.ROW + 1
|
||||
|
||||
def __EndRow(self):
|
||||
# 从多少行结束
|
||||
if self.ROW > self.__COUNT_ROW:
|
||||
return self.__COUNT_ROW
|
||||
return self.__C_PAGE * self.ROW
|
||||
|
||||
def __GetCountPage(self):
|
||||
# 取总页数
|
||||
return int(math.ceil(self.__COUNT_ROW / float(self.ROW)))
|
||||
|
||||
def __SetUri(self, sinput):
|
||||
# 构造URI
|
||||
uri = '?'
|
||||
for key in sinput:
|
||||
if key == 'p':
|
||||
continue
|
||||
uri += key + '=' + sinput[key] + '&'
|
||||
return str(uri)
|
||||
@@ -1,985 +0,0 @@
|
||||
# coding: utf-8
|
||||
|
||||
# ---------------------------------------------------------------------------------
|
||||
# MW-Linux面板
|
||||
# ---------------------------------------------------------------------------------
|
||||
# copyright (c) 2018-∞(https://github.com/midoks/mdserver-web) All rights reserved.
|
||||
# ---------------------------------------------------------------------------------
|
||||
# Author: midoks <midoks@163.com>
|
||||
# ---------------------------------------------------------------------------------
|
||||
|
||||
# ---------------------------------------------------------------------------------
|
||||
# 插件操作
|
||||
# ---------------------------------------------------------------------------------
|
||||
|
||||
|
||||
import psutil
|
||||
import time
|
||||
import os
|
||||
import mw
|
||||
import re
|
||||
import json
|
||||
|
||||
import sys
|
||||
# reload(sys)
|
||||
# sys.setdefaultencoding('utf8')
|
||||
|
||||
import threading
|
||||
import multiprocessing
|
||||
|
||||
|
||||
from flask import request
|
||||
|
||||
|
||||
class pa_thread(threading.Thread):
|
||||
|
||||
def __init__(self, func, args, name=''):
|
||||
threading.Thread.__init__(self)
|
||||
self.name = name
|
||||
self.func = func
|
||||
self.args = args
|
||||
self.result = self.func(*self.args)
|
||||
|
||||
def getResult(self):
|
||||
try:
|
||||
return self.result
|
||||
except Exception:
|
||||
return None
|
||||
|
||||
|
||||
class plugins_api:
|
||||
__tasks = None
|
||||
__plugin_dir = 'plugins'
|
||||
__type = 'data/json/type.json'
|
||||
__index = 'data/json/index.json'
|
||||
setupPath = None
|
||||
|
||||
def __init__(self):
|
||||
self.setupPath = 'server'
|
||||
self.__plugin_dir = mw.getRunDir() + '/plugins'
|
||||
self.__type = mw.getRunDir() + '/data/json/type.json'
|
||||
self.__index = mw.getRunDir() + '/data/json/index.json'
|
||||
|
||||
##### ----- start ----- ###
|
||||
def listApi(self):
|
||||
sType = request.args.get('type', '0')
|
||||
sPage = request.args.get('p', '1')
|
||||
|
||||
if not mw.isNumber(sPage):
|
||||
sPage = 1
|
||||
|
||||
if not mw.isNumber(sType):
|
||||
sType = 0
|
||||
|
||||
# print sPage
|
||||
data = self.getPluginList(sType, int(sPage))
|
||||
return mw.getJson(data)
|
||||
|
||||
def fileApi(self):
|
||||
name = request.args.get('name', '')
|
||||
if name.strip() == '':
|
||||
return ''
|
||||
|
||||
f = request.args.get('f', '')
|
||||
if f.strip() == '':
|
||||
return ''
|
||||
|
||||
file = mw.getPluginDir() + '/' + name + '/' + f
|
||||
if not os.path.exists(file):
|
||||
return ''
|
||||
|
||||
c = open(file, 'rb').read()
|
||||
return c
|
||||
|
||||
def indexListApi(self):
|
||||
data = self.getIndexList()
|
||||
return mw.getJson(data)
|
||||
|
||||
def indexSortApi(self):
|
||||
sort = request.form.get('ssort', '')
|
||||
if sort.strip() == '':
|
||||
return mw.returnJson(False, '排序数据不能为空!')
|
||||
data = self.setIndexSort(sort)
|
||||
if data:
|
||||
return mw.returnJson(True, '成功!')
|
||||
return mw.returnJson(False, '失败!')
|
||||
|
||||
def initApi(self):
|
||||
|
||||
plugin_names = {
|
||||
'openresty': '1.21.4.1',
|
||||
'php': '56',
|
||||
'swap': '1.1',
|
||||
'mysql': '5.7',
|
||||
'phpmyadmin': '4.4.15',
|
||||
}
|
||||
|
||||
pn_dir = mw.getPluginDir()
|
||||
pn_server_dir = mw.getServerDir()
|
||||
pn_list = []
|
||||
for pn in plugin_names:
|
||||
info = {}
|
||||
pn_json = pn_dir + '/' + pn + '/info.json'
|
||||
pn_server = pn_server_dir + '/' + pn
|
||||
if not os.path.exists(pn_server):
|
||||
|
||||
tmp = mw.readFile(pn_json)
|
||||
tmp = json.loads(tmp)
|
||||
|
||||
info['title'] = tmp['title']
|
||||
info['name'] = tmp['name']
|
||||
info['versions'] = tmp['versions']
|
||||
info['default_ver'] = plugin_names[pn]
|
||||
pn_list.append(info)
|
||||
else:
|
||||
return mw.returnJson(False, 'ok')
|
||||
|
||||
return mw.returnJson(True, 'ok', pn_list)
|
||||
|
||||
def initInstallApi(self):
|
||||
pn_list = request.form.get('list', '')
|
||||
try:
|
||||
pn_list = json.loads(pn_list)
|
||||
|
||||
for pn in pn_list:
|
||||
name = pn['name']
|
||||
version = pn['version']
|
||||
infoJsonPos = self.__plugin_dir + '/' + name + '/' + 'info.json'
|
||||
pluginInfo = json.loads(mw.readFile(infoJsonPos))
|
||||
self.hookInstall(pluginInfo)
|
||||
execstr = 'cd ' + mw.getPluginDir() + '/' + name + ' && bash ' + \
|
||||
pluginInfo['shell'] + ' install ' + version
|
||||
|
||||
taskAdd = ('安装[' + name + '-' + version + ']',
|
||||
'execshell', '0', time.strftime('%Y-%m-%d %H:%M:%S'), execstr)
|
||||
|
||||
mw.M('tasks').add('name,type,status,addtime, execstr', taskAdd)
|
||||
os.mkdir(mw.getServerDir() + '/php')
|
||||
# 任务执行相关
|
||||
mw.triggerTask()
|
||||
return mw.returnJson(True, '添加成功')
|
||||
except Exception as e:
|
||||
return mw.returnJson(False, mw.getTracebackInfo())
|
||||
|
||||
def installApi(self):
|
||||
rundir = mw.getRunDir()
|
||||
name = request.form.get('name', '')
|
||||
version = request.form.get('version', '')
|
||||
|
||||
mmsg = '安装'
|
||||
if hasattr(request.form, 'upgrade'):
|
||||
mtype = 'update'
|
||||
mmsg = 'upgrade'
|
||||
|
||||
if name.strip() == '':
|
||||
return mw.returnJson(False, '缺少插件名称!', ())
|
||||
|
||||
if version.strip() == '':
|
||||
return mw.returnJson(False, '缺少版本信息!', ())
|
||||
|
||||
infoJsonPos = self.__plugin_dir + '/' + name + '/' + 'info.json'
|
||||
# print infoJsonPos
|
||||
|
||||
if not os.path.exists(infoJsonPos):
|
||||
return mw.returnJson(False, '配置文件不存在!', ())
|
||||
|
||||
pluginInfo = json.loads(mw.readFile(infoJsonPos))
|
||||
self.hookInstall(pluginInfo)
|
||||
|
||||
execstr = 'cd ' + mw.getPluginDir() + '/' + name + ' && bash ' + \
|
||||
pluginInfo['shell'] + ' install ' + version
|
||||
|
||||
if mw.isAppleSystem():
|
||||
print(execstr)
|
||||
|
||||
taskAdd = (mmsg + '[' + name + '-' + version + ']',
|
||||
'execshell', '0', time.strftime('%Y-%m-%d %H:%M:%S'), execstr)
|
||||
|
||||
mw.M('tasks').add('name,type,status,addtime, execstr', taskAdd)
|
||||
|
||||
# 任务执行相关
|
||||
mw.triggerTask()
|
||||
return mw.returnJson(True, '已将安装任务添加到队列!')
|
||||
|
||||
def hookInstallFile(self, hook_name, info):
|
||||
hookPath = mw.getPanelDataDir() + "/hook_" + hook_name + ".json"
|
||||
data = []
|
||||
if os.path.exists(hookPath):
|
||||
t = mw.readFile(hookPath)
|
||||
data = json.loads(t)
|
||||
|
||||
isNeedAdd = True
|
||||
for x in range(len(data)):
|
||||
if data[x]['title'] == info['title'] and data[x]['name'] == info['name']:
|
||||
isNeedAdd = False
|
||||
|
||||
if isNeedAdd:
|
||||
tmp = {}
|
||||
tmp['title'] = info['title']
|
||||
tmp['name'] = info['name']
|
||||
data.append(tmp)
|
||||
mw.writeFile(hookPath, json.dumps(data))
|
||||
|
||||
def hookUninstallFile(self, hook_name, info):
|
||||
hookPath = mw.getPanelDataDir() + "/hook_" + hook_name + ".json"
|
||||
data = []
|
||||
if os.path.exists(hookPath):
|
||||
t = mw.readFile(hookPath)
|
||||
data = json.loads(t)
|
||||
|
||||
for idx in range(len(data)):
|
||||
if data[idx]['title'] == info['title'] and data[idx]['name'] == info['name']:
|
||||
data.remove(data[idx])
|
||||
mw.writeFile(hookPath, json.dumps(data))
|
||||
|
||||
def hookInstall(self, info):
|
||||
if 'hook' in info:
|
||||
hooks = info['hook']
|
||||
for x in hooks:
|
||||
if x in ['backup']:
|
||||
self.hookInstallFile(x, info)
|
||||
return True
|
||||
return False
|
||||
|
||||
def hookUninstall(self, info):
|
||||
if 'hook' in info:
|
||||
hooks = info['hook']
|
||||
for x in hooks:
|
||||
if x in ['backup']:
|
||||
self.hookUninstallFile(x, info)
|
||||
return True
|
||||
return False
|
||||
|
||||
def uninstallOldApi(self):
|
||||
rundir = mw.getRunDir()
|
||||
name = request.form.get('name', '')
|
||||
version = request.form.get('version', '')
|
||||
if name.strip() == '':
|
||||
return mw.returnJson(False, "缺少插件名称!", ())
|
||||
|
||||
if version.strip() == '':
|
||||
return mw.returnJson(False, "缺少版本信息!", ())
|
||||
|
||||
infoJsonPos = self.__plugin_dir + '/' + name + '/' + 'info.json'
|
||||
|
||||
if not os.path.exists(infoJsonPos):
|
||||
return mw.returnJson(False, "配置文件不存在!", ())
|
||||
|
||||
pluginInfo = json.loads(mw.readFile(infoJsonPos))
|
||||
execstr = "cd " + os.getcwd() + "/plugins/" + \
|
||||
name + " && /bin/bash " + pluginInfo["shell"] \
|
||||
+ " uninstall " + version
|
||||
|
||||
taskAdd = (None, '卸载[' + name + '-' + version + ']',
|
||||
'execshell', '0', time.strftime('%Y-%m-%d %H:%M:%S'), execstr)
|
||||
|
||||
mw.M('tasks').add('id,name,type,status,addtime, execstr', taskAdd)
|
||||
return mw.returnJson(True, '已将卸载任务添加到队列!')
|
||||
|
||||
# 卸载时间短,不加入任务中...
|
||||
def uninstallApi(self):
|
||||
rundir = mw.getRunDir()
|
||||
name = request.form.get('name', '')
|
||||
version = request.form.get('version', '')
|
||||
if name.strip() == '':
|
||||
return mw.returnJson(False, "缺少插件名称!", ())
|
||||
|
||||
if version.strip() == '':
|
||||
return mw.returnJson(False, "缺少版本信息!", ())
|
||||
|
||||
infoJsonPos = self.__plugin_dir + '/' + name + '/' + 'info.json'
|
||||
|
||||
if not os.path.exists(infoJsonPos):
|
||||
return mw.returnJson(False, "配置文件不存在!", ())
|
||||
|
||||
pluginInfo = json.loads(mw.readFile(infoJsonPos))
|
||||
self.hookUninstall(pluginInfo)
|
||||
execstr = "cd " + os.getcwd() + "/plugins/" + \
|
||||
name + " && /bin/bash " + pluginInfo["shell"] \
|
||||
+ " uninstall " + version
|
||||
|
||||
data = mw.execShell(execstr)
|
||||
if mw.isDebugMode():
|
||||
print(execstr)
|
||||
print(data[0], data[1])
|
||||
self.removeIndex(name, version)
|
||||
return mw.returnJson(True, '卸载执行成功!')
|
||||
# if data[1] == '':
|
||||
# return mw.returnJson(True, '已将卸载成功!')
|
||||
# else:
|
||||
# return mw.returnJson(False, '卸载出现错误信息!' + data[1])
|
||||
|
||||
def checkApi(self):
|
||||
name = request.form.get('name', '')
|
||||
if name.strip() == '':
|
||||
return mw.returnJson(False, "缺少插件名称!", ())
|
||||
|
||||
infoJsonPos = self.__plugin_dir + '/' + name + '/' + 'info.json'
|
||||
if not os.path.exists(infoJsonPos):
|
||||
return mw.returnJson(False, "配置文件不存在!", ())
|
||||
return mw.returnJson(True, "插件存在!", ())
|
||||
|
||||
def setIndexApi(self):
|
||||
name = request.form.get('name', '')
|
||||
status = request.form.get('status', '0')
|
||||
version = request.form.get('version', '')
|
||||
if status == '1':
|
||||
return self.addIndex(name, version)
|
||||
return self.removeIndex(name, version)
|
||||
|
||||
def settingApi(self):
|
||||
name = request.args.get('name', '')
|
||||
html = self.__plugin_dir + '/' + name + '/index.html'
|
||||
return mw.readFile(html)
|
||||
|
||||
def runApi(self):
|
||||
name = request.form.get('name', '')
|
||||
func = request.form.get('func', '')
|
||||
version = request.form.get('version', '')
|
||||
args = request.form.get('args', '')
|
||||
script = request.form.get('script', 'index')
|
||||
|
||||
data = self.run(name, func, version, args, script)
|
||||
if data[1] == '':
|
||||
r = mw.returnJson(True, "OK", data[0].strip())
|
||||
else:
|
||||
r = mw.returnJson(False, data[1].strip())
|
||||
return r
|
||||
|
||||
def callbackApi(self):
|
||||
name = request.form.get('name', '')
|
||||
func = request.form.get('func', '')
|
||||
args = request.form.get('args', '')
|
||||
script = request.form.get('script', 'index')
|
||||
|
||||
data = self.callback(name, func, args, script)
|
||||
if data[0]:
|
||||
return mw.returnJson(True, "OK", data[1])
|
||||
return mw.returnJson(False, data[1])
|
||||
|
||||
def updateZipApi(self):
|
||||
tmp_path = mw.getRootDir() + '/temp'
|
||||
if not os.path.exists(tmp_path):
|
||||
os.makedirs(tmp_path)
|
||||
mw.execShell("rm -rf " + tmp_path + '/*')
|
||||
|
||||
tmp_file = tmp_path + '/plugin_tmp.zip'
|
||||
from werkzeug.utils import secure_filename
|
||||
from flask import request
|
||||
f = request.files['plugin_zip']
|
||||
if f.filename[-4:] != '.zip':
|
||||
return mw.returnJson(False, '仅支持zip文件!')
|
||||
f.save(tmp_file)
|
||||
mw.execShell('cd ' + tmp_path + ' && unzip ' + tmp_file)
|
||||
os.remove(tmp_file)
|
||||
|
||||
p_info = tmp_path + '/info.json'
|
||||
if not os.path.exists(p_info):
|
||||
d_path = None
|
||||
for df in os.walk(tmp_path):
|
||||
if len(df[2]) < 3:
|
||||
continue
|
||||
if not 'info.json' in df[2]:
|
||||
continue
|
||||
if not 'install.sh' in df[2]:
|
||||
continue
|
||||
if not os.path.exists(df[0] + '/info.json'):
|
||||
continue
|
||||
d_path = df[0]
|
||||
if d_path:
|
||||
tmp_path = d_path
|
||||
p_info = tmp_path + '/info.json'
|
||||
try:
|
||||
data = json.loads(mw.readFile(p_info))
|
||||
data['size'] = mw.getPathSize(tmp_path)
|
||||
if not 'author' in data:
|
||||
data['author'] = '未知'
|
||||
if not 'home' in data:
|
||||
data['home'] = 'https://github.com/midoks/mdserver-web'
|
||||
plugin_path = mw.getPluginDir() + data['name'] + '/info.json'
|
||||
data['old_version'] = '0'
|
||||
data['tmp_path'] = tmp_path
|
||||
if os.path.exists(plugin_path):
|
||||
try:
|
||||
old_info = json.loads(mw.ReadFile(plugin_path))
|
||||
data['old_version'] = old_info['versions']
|
||||
except:
|
||||
pass
|
||||
except:
|
||||
mw.execShell("rm -rf " + tmp_path)
|
||||
return mw.returnJson(False, '在压缩包中没有找到插件信息,请检查插件包!')
|
||||
protectPlist = ('openresty', 'mysql', 'php', 'redis', 'memcached'
|
||||
'mongodb', 'swap', 'gogs', 'pureftp')
|
||||
if data['name'] in protectPlist:
|
||||
return mw.returnJson(False, '[' + data['name'] + '],重要插件不可修改!')
|
||||
return mw.getJson(data)
|
||||
|
||||
def inputZipApi(self):
|
||||
plugin_name = request.form.get('plugin_name', '')
|
||||
tmp_path = request.form.get('tmp_path', '')
|
||||
|
||||
if not os.path.exists(tmp_path):
|
||||
return mw.returnJson(False, '临时文件不存在,请重新上传!')
|
||||
plugin_path = mw.getPluginDir() + '/' + plugin_name
|
||||
if not os.path.exists(plugin_path):
|
||||
print(mw.execShell('mkdir -p ' + plugin_path))
|
||||
mw.execShell("\cp -rf " + tmp_path + '/* ' + plugin_path + '/')
|
||||
mw.execShell('chmod -R 755 ' + plugin_path)
|
||||
p_info = mw.readFile(plugin_path + '/info.json')
|
||||
if p_info:
|
||||
mw.writeLog('软件管理', '安装第三方插件[%s]' %
|
||||
json.loads(p_info)['title'])
|
||||
return mw.returnJson(True, '安装成功!')
|
||||
mw.execShell("rm -rf " + plugin_path)
|
||||
return mw.returnJson(False, '安装失败!')
|
||||
##### ----- end ----- ###
|
||||
|
||||
# 进程是否存在
|
||||
def processExists(self, pname, exe=None):
|
||||
try:
|
||||
if not self.pids:
|
||||
self.pids = psutil.pids()
|
||||
for pid in self.pids:
|
||||
try:
|
||||
p = psutil.Process(pid)
|
||||
if p.name() == pname:
|
||||
if not exe:
|
||||
return True
|
||||
else:
|
||||
if p.exe() == exe:
|
||||
return True
|
||||
except:
|
||||
pass
|
||||
return False
|
||||
except:
|
||||
return True
|
||||
|
||||
# 检查是否正在安装
|
||||
def checkSetupTask(self, sName, sVer, sCoexist):
|
||||
if not self.__tasks:
|
||||
self.__tasks = mw.M('tasks').where(
|
||||
"status!=?", ('1',)).field('status,name').select()
|
||||
isTask = '1'
|
||||
for task in self.__tasks:
|
||||
tmpt = mw.getStrBetween('[', ']', task['name'])
|
||||
if not tmpt:
|
||||
continue
|
||||
task_sign = tmpt.split('-')
|
||||
task_len = len(task_sign)
|
||||
|
||||
task_name = task_sign[0].lower()
|
||||
task_ver = task_sign[1]
|
||||
if task_len > 2:
|
||||
nameArr = task_sign[0:task_len - 1]
|
||||
task_name = '-'.join(nameArr).lower()
|
||||
task_ver = task_sign[task_len - 1]
|
||||
if sCoexist:
|
||||
if task_name == sName and task_ver == sVer:
|
||||
isTask = task['status']
|
||||
else:
|
||||
if task_name == sName:
|
||||
isTask = task['status']
|
||||
return isTask
|
||||
|
||||
def checkStatus(self, info):
|
||||
if not info['setup']:
|
||||
return False
|
||||
|
||||
data = self.run(info['name'], 'status', info['setup_version'])
|
||||
if data[0] == 'start':
|
||||
return True
|
||||
return False
|
||||
|
||||
def checkStatusProcess(self, info, i, return_dict):
|
||||
if not info['setup']:
|
||||
return_dict[i] = False
|
||||
return
|
||||
|
||||
data = self.run(info['name'], 'status', info['setup_version'])
|
||||
if data[0] == 'start':
|
||||
return_dict[i] = True
|
||||
else:
|
||||
return_dict[i] = False
|
||||
|
||||
def checkStatusThreads(self, info, i):
|
||||
if not info['setup']:
|
||||
return False
|
||||
data = self.run(info['name'], 'status', info['setup_version'])
|
||||
if data[0] == 'start':
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
# 还无法正常运行,先保留
|
||||
def checkStatusThreadsCallback(self, info, i):
|
||||
if not info['setup']:
|
||||
return False
|
||||
|
||||
try:
|
||||
data = self.callback(info['name'], 'status', info['setup_version'])
|
||||
except Exception as e:
|
||||
data = self.callback(info['name'], 'status')
|
||||
|
||||
# data = self.run(info['name'], 'status', info['setup_version'])
|
||||
if data[0] == 'start':
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
def checkStatusMThreads(self, plugins_info):
|
||||
try:
|
||||
threads = []
|
||||
ntmp_list = range(len(plugins_info))
|
||||
for i in ntmp_list:
|
||||
t = pa_thread(self.checkStatusThreads,
|
||||
(plugins_info[i], i))
|
||||
threads.append(t)
|
||||
|
||||
for i in ntmp_list:
|
||||
threads[i].start()
|
||||
for i in ntmp_list:
|
||||
threads[i].join()
|
||||
|
||||
for i in ntmp_list:
|
||||
t = threads[i].getResult()
|
||||
plugins_info[i]['status'] = t
|
||||
except Exception as e:
|
||||
print('checkStatusMThreads:', str(e))
|
||||
|
||||
return plugins_info
|
||||
|
||||
def checkStatusMProcess(self, plugins_info):
|
||||
manager = multiprocessing.Manager()
|
||||
return_dict = manager.dict()
|
||||
jobs = []
|
||||
for i in range(len(plugins_info)):
|
||||
p = multiprocessing.Process(
|
||||
target=self.checkStatusProcess, args=(plugins_info[i], i, return_dict))
|
||||
jobs.append(p)
|
||||
p.start()
|
||||
|
||||
for proc in jobs:
|
||||
proc.join()
|
||||
|
||||
returnData = return_dict.values()
|
||||
for i in ntmp_list:
|
||||
plugins_info[i]['status'] = returnData[i]
|
||||
|
||||
return plugins_info
|
||||
|
||||
def checkDisplayIndex(self, name, version):
|
||||
if not os.path.exists(self.__index):
|
||||
mw.writeFile(self.__index, '[]')
|
||||
|
||||
indexList = json.loads(mw.readFile(self.__index))
|
||||
if type(version) == list:
|
||||
for index in range(len(version)):
|
||||
vname = name + '-' + version[index]
|
||||
if vname in indexList:
|
||||
return True
|
||||
else:
|
||||
vname = name + '-' + version
|
||||
if vname in indexList:
|
||||
return True
|
||||
return False
|
||||
|
||||
def getVersion(self, path):
|
||||
version_f = path + '/version.pl'
|
||||
if os.path.exists(version_f):
|
||||
return mw.readFile(version_f).strip()
|
||||
return ''
|
||||
|
||||
# 构造本地插件信息
|
||||
def getPluginInfo(self, info):
|
||||
checks = ''
|
||||
path = ''
|
||||
coexist = False
|
||||
|
||||
if info["checks"][0:1] == '/':
|
||||
checks = info["checks"]
|
||||
else:
|
||||
checks = mw.getRootDir() + '/' + info['checks']
|
||||
|
||||
if 'path' in info:
|
||||
path = info['path']
|
||||
|
||||
if path[0:1] != '/':
|
||||
path = mw.getRootDir() + '/' + path
|
||||
|
||||
if 'coexist' in info and info['coexist']:
|
||||
coexist = True
|
||||
|
||||
pInfo = {
|
||||
"id": 10000,
|
||||
"pid": info['pid'],
|
||||
"type": 1000,
|
||||
"name": info['name'],
|
||||
"title": info['title'],
|
||||
"ps": info['ps'],
|
||||
"dependnet": "",
|
||||
"mutex": "",
|
||||
"path": path,
|
||||
"install_checks": checks,
|
||||
"uninsatll_checks": checks,
|
||||
"coexist": coexist,
|
||||
"versions": info['versions'],
|
||||
# "updates": info['updates'],
|
||||
"display": False,
|
||||
"setup": False,
|
||||
"setup_version": "",
|
||||
"status": False,
|
||||
"install_pre_inspection": False,
|
||||
"uninstall_pre_inspection": False,
|
||||
}
|
||||
|
||||
if checks.find('VERSION') > -1:
|
||||
pInfo['install_checks'] = checks.replace(
|
||||
'VERSION', info['versions'])
|
||||
|
||||
if path.find('VERSION') > -1:
|
||||
pInfo['path'] = path.replace(
|
||||
'VERSION', info['versions'])
|
||||
|
||||
pInfo['task'] = self.checkSetupTask(
|
||||
pInfo['name'], info['versions'], coexist)
|
||||
pInfo['display'] = self.checkDisplayIndex(
|
||||
info['name'], pInfo['versions'])
|
||||
|
||||
pInfo['setup'] = os.path.exists(pInfo['install_checks'])
|
||||
|
||||
if coexist and pInfo['setup']:
|
||||
pInfo['setup_version'] = info['versions']
|
||||
else:
|
||||
pInfo['setup_version'] = self.getVersion(pInfo['install_checks'])
|
||||
# pluginInfo['status'] = self.checkStatus(pluginInfo)
|
||||
|
||||
if 'install_pre_inspection' in info:
|
||||
pInfo['install_pre_inspection'] = info['install_pre_inspection']
|
||||
if 'uninstall_pre_inspection' in info:
|
||||
pInfo['uninstall_pre_inspection'] = info[
|
||||
'uninstall_pre_inspection']
|
||||
|
||||
pInfo['status'] = False
|
||||
return pInfo
|
||||
|
||||
def makeCoexist(self, data):
|
||||
plugins_info = []
|
||||
for index in range(len(data['versions'])):
|
||||
tmp = data.copy()
|
||||
tmp['title'] = tmp['title'] + \
|
||||
'-' + data['versions'][index]
|
||||
tmp['versions'] = data['versions'][index]
|
||||
pg = self.getPluginInfo(tmp)
|
||||
plugins_info.append(pg)
|
||||
|
||||
return plugins_info
|
||||
|
||||
def makeList(self, data, sType='0'):
|
||||
plugins_info = []
|
||||
|
||||
if (data['pid'] == sType):
|
||||
if type(data['versions']) == list and 'coexist' in data and data['coexist']:
|
||||
tmp_data = self.makeCoexist(data)
|
||||
for index in range(len(tmp_data)):
|
||||
plugins_info.append(tmp_data[index])
|
||||
else:
|
||||
pg = self.getPluginInfo(data)
|
||||
plugins_info.append(pg)
|
||||
return plugins_info
|
||||
|
||||
if sType == '0':
|
||||
if type(data['versions']) == list and 'coexist' in data and data['coexist']:
|
||||
tmp_data = self.makeCoexist(data)
|
||||
for index in range(len(tmp_data)):
|
||||
plugins_info.append(tmp_data[index])
|
||||
else:
|
||||
pg = self.getPluginInfo(data)
|
||||
plugins_info.append(pg)
|
||||
|
||||
# print plugins_info, data
|
||||
return plugins_info
|
||||
|
||||
def getAllList(self, sType='0'):
|
||||
plugins_info = []
|
||||
for dirinfo in os.listdir(self.__plugin_dir):
|
||||
if dirinfo[0:1] == '.':
|
||||
continue
|
||||
path = self.__plugin_dir + '/' + dirinfo
|
||||
json_file = path + '/info.json'
|
||||
if os.path.exists(json_file):
|
||||
try:
|
||||
data = json.loads(mw.readFile(json_file))
|
||||
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)
|
||||
return plugins_info
|
||||
|
||||
def getAllListPage(self, sType='0', page=1, pageSize=10):
|
||||
plugins_info = []
|
||||
for dirinfo in os.listdir(self.__plugin_dir):
|
||||
if dirinfo[0:1] == '.':
|
||||
continue
|
||||
path = self.__plugin_dir + '/' + dirinfo
|
||||
if os.path.isdir(path):
|
||||
json_file = path + '/info.json'
|
||||
if os.path.exists(json_file):
|
||||
try:
|
||||
data = json.loads(mw.readFile(json_file))
|
||||
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)
|
||||
|
||||
start = (page - 1) * pageSize
|
||||
end = start + pageSize
|
||||
_plugins_info = plugins_info[start:end]
|
||||
|
||||
_plugins_info = self.checkStatusMThreads(_plugins_info)
|
||||
return (_plugins_info, len(plugins_info))
|
||||
|
||||
def makeListThread(self, data, sType='0'):
|
||||
plugins_info = []
|
||||
|
||||
if (data['pid'] == sType):
|
||||
if type(data['versions']) == list and data.has_key('coexist') and data['coexist']:
|
||||
tmp_data = self.makeCoexist(data)
|
||||
for index in range(len(tmp_data)):
|
||||
plugins_info.append(tmp_data[index])
|
||||
else:
|
||||
pg = self.getPluginInfo(data)
|
||||
plugins_info.append(pg)
|
||||
return plugins_info
|
||||
|
||||
if sType == '0':
|
||||
if type(data['versions']) == list and data.has_key('coexist') and data['coexist']:
|
||||
tmp_data = self.makeCoexist(data)
|
||||
for index in range(len(tmp_data)):
|
||||
plugins_info.append(tmp_data[index])
|
||||
else:
|
||||
pg = self.getPluginInfo(data)
|
||||
plugins_info.append(pg)
|
||||
|
||||
# print plugins_info, data
|
||||
return plugins_info
|
||||
|
||||
def getAllListThread(self, sType='0'):
|
||||
plugins_info = []
|
||||
tmp_list = []
|
||||
threads = []
|
||||
for dirinfo in os.listdir(self.__plugin_dir):
|
||||
if dirinfo[0:1] == '.':
|
||||
continue
|
||||
path = self.__plugin_dir + '/' + dirinfo
|
||||
if os.path.isdir(path):
|
||||
json_file = path + '/info.json'
|
||||
if os.path.exists(json_file):
|
||||
data = json.loads(mw.readFile(json_file))
|
||||
if sType == '0':
|
||||
tmp_list.append(data)
|
||||
|
||||
if (data['pid'] == sType):
|
||||
tmp_list.append(data)
|
||||
|
||||
ntmp_list = range(len(tmp_list))
|
||||
for i in ntmp_list:
|
||||
t = pa_thread(self.makeListThread, (tmp_list[i], sType))
|
||||
threads.append(t)
|
||||
for i in ntmp_list:
|
||||
threads[i].start()
|
||||
for i in ntmp_list:
|
||||
threads[i].join()
|
||||
|
||||
for i in ntmp_list:
|
||||
t = threads[i].getResult()
|
||||
for index in range(len(t)):
|
||||
plugins_info.append(t[index])
|
||||
|
||||
return plugins_info
|
||||
|
||||
def makeListProcess(self, data, sType, i, return_dict):
|
||||
plugins_info = []
|
||||
|
||||
if (data['pid'] == sType):
|
||||
if type(data['versions']) == list and data.has_key('coexist') and data['coexist']:
|
||||
tmp_data = self.makeCoexist(data)
|
||||
for index in range(len(tmp_data)):
|
||||
plugins_info.append(tmp_data[index])
|
||||
else:
|
||||
pg = self.getPluginInfo(data)
|
||||
plugins_info.append(pg)
|
||||
# return plugins_info
|
||||
|
||||
if sType == '0':
|
||||
if type(data['versions']) == list and data.has_key('coexist') and data['coexist']:
|
||||
tmp_data = self.makeCoexist(data)
|
||||
for index in range(len(tmp_data)):
|
||||
plugins_info.append(tmp_data[index])
|
||||
else:
|
||||
pg = self.getPluginInfo(data)
|
||||
plugins_info.append(pg)
|
||||
|
||||
return_dict[i] = plugins_info
|
||||
# return plugins_info
|
||||
|
||||
def getAllListProcess(self, sType='0'):
|
||||
plugins_info = []
|
||||
tmp_list = []
|
||||
manager = multiprocessing.Manager()
|
||||
return_dict = manager.dict()
|
||||
jobs = []
|
||||
for dirinfo in os.listdir(self.__plugin_dir):
|
||||
if dirinfo[0:1] == '.':
|
||||
continue
|
||||
path = self.__plugin_dir + '/' + dirinfo
|
||||
if os.path.isdir(path):
|
||||
json_file = path + '/info.json'
|
||||
if os.path.exists(json_file):
|
||||
data = json.loads(mw.readFile(json_file))
|
||||
if sType == '0':
|
||||
tmp_list.append(data)
|
||||
|
||||
if (data['pid'] == sType):
|
||||
tmp_list.append(data)
|
||||
|
||||
ntmp_list = range(len(tmp_list))
|
||||
for i in ntmp_list:
|
||||
p = multiprocessing.Process(
|
||||
target=self.makeListProcess, args=(tmp_list[i], sType, i, return_dict))
|
||||
jobs.append(p)
|
||||
p.start()
|
||||
|
||||
for proc in jobs:
|
||||
proc.join()
|
||||
|
||||
returnData = return_dict.values()
|
||||
for i in ntmp_list:
|
||||
for index in range(len(returnData[i])):
|
||||
plugins_info.append(returnData[i][index])
|
||||
|
||||
return plugins_info
|
||||
|
||||
def getPluginList(self, sType, sPage=1, sPageSize=10):
|
||||
# print sType, 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)
|
||||
ret['data'] = data[0]
|
||||
|
||||
args = {}
|
||||
args['count'] = data[1]
|
||||
args['p'] = sPage
|
||||
args['tojs'] = 'getSList'
|
||||
args['row'] = sPageSize
|
||||
|
||||
ret['list'] = mw.getPage(args)
|
||||
return ret
|
||||
|
||||
def getIndexList(self):
|
||||
if not os.path.exists(self.__index):
|
||||
mw.writeFile(self.__index, '[]')
|
||||
|
||||
indexList = json.loads(mw.readFile(self.__index))
|
||||
plist = []
|
||||
for i in indexList:
|
||||
tmp = i.split('-')
|
||||
tmp_len = len(tmp)
|
||||
plugin_name = tmp[0]
|
||||
plugin_ver = tmp[1]
|
||||
if tmp_len > 2:
|
||||
tmpArr = tmp[0:tmp_len - 1]
|
||||
plugin_name = '-'.join(tmpArr)
|
||||
plugin_ver = tmp[tmp_len - 1]
|
||||
|
||||
json_file = self.__plugin_dir + '/' + plugin_name + '/info.json'
|
||||
if os.path.exists(json_file):
|
||||
content = mw.readFile(json_file)
|
||||
try:
|
||||
data = json.loads(content)
|
||||
data = self.makeList(data)
|
||||
for index in range(len(data)):
|
||||
if data[index]['versions'] == plugin_ver or plugin_ver in data[index]['versions']:
|
||||
data[index]['display'] = True
|
||||
plist.append(data[index])
|
||||
continue
|
||||
except Exception as e:
|
||||
print('getIndexList:', e)
|
||||
|
||||
# 使用gevent模式时,无法使用多进程
|
||||
# plist = self.checkStatusMProcess(plist)
|
||||
plist = self.checkStatusMThreads(plist)
|
||||
return plist
|
||||
|
||||
def setIndexSort(self, sort):
|
||||
data = sort.split('|')
|
||||
mw.writeFile(self.__index, json.dumps(data))
|
||||
return True
|
||||
|
||||
def addIndex(self, name, version):
|
||||
if not os.path.exists(self.__index):
|
||||
mw.writeFile(self.__index, '[]')
|
||||
|
||||
indexList = json.loads(mw.readFile(self.__index))
|
||||
vname = name + '-' + version
|
||||
|
||||
if vname in indexList:
|
||||
return mw.returnJson(False, '请不要重复添加!')
|
||||
if len(indexList) > 12:
|
||||
return mw.returnJson(False, '首页最多只能显示12个软件!')
|
||||
|
||||
indexList.append(vname)
|
||||
mw.writeFile(self.__index, json.dumps(indexList))
|
||||
return mw.returnJson(True, '添加成功!')
|
||||
|
||||
def removeIndex(self, name, version):
|
||||
if not os.path.exists(self.__index):
|
||||
mw.writeFile(self.__index, '[]')
|
||||
|
||||
indexList = json.loads(mw.readFile(self.__index))
|
||||
vname = name + '-' + version
|
||||
if not vname in indexList:
|
||||
return mw.returnJson(True, '删除成功!')
|
||||
indexList.remove(vname)
|
||||
mw.writeFile(self.__index, json.dumps(indexList))
|
||||
return mw.returnJson(True, '删除成功!')
|
||||
|
||||
# shell 调用
|
||||
def run(self, name, func, version='', args='', script='index'):
|
||||
path = self.__plugin_dir + \
|
||||
'/' + name + '/' + script + '.py'
|
||||
py = 'python3 ' + path
|
||||
|
||||
if args == '':
|
||||
py_cmd = py + ' ' + func + ' ' + version
|
||||
else:
|
||||
py_cmd = py + ' ' + func + ' ' + version + ' ' + args
|
||||
|
||||
if not os.path.exists(path):
|
||||
return ('', '')
|
||||
data = mw.execShell(py_cmd)
|
||||
|
||||
if mw.isDebugMode():
|
||||
print('run', py_cmd)
|
||||
print(data)
|
||||
# print os.path.exists(py_cmd)
|
||||
|
||||
return (data[0].strip(), data[1].strip())
|
||||
|
||||
# 映射包调用
|
||||
def callback(self, name, func, args='', script='index'):
|
||||
package = self.__plugin_dir + '/' + name
|
||||
if not os.path.exists(package):
|
||||
return (False, "插件不存在!")
|
||||
|
||||
sys.path.append(package)
|
||||
eval_str = "__import__('" + script + "')." + func + '(' + args + ')'
|
||||
newRet = eval(eval_str)
|
||||
if mw.isDebugMode():
|
||||
print('callback', eval_str)
|
||||
|
||||
return (True, newRet)
|
||||
@@ -1,744 +0,0 @@
|
||||
# coding: utf-8
|
||||
|
||||
# ---------------------------------------------------------------------------------
|
||||
# MW-Linux面板
|
||||
# ---------------------------------------------------------------------------------
|
||||
# copyright (c) 2018-∞(https://github.com/midoks/mdserver-web) All rights reserved.
|
||||
# ---------------------------------------------------------------------------------
|
||||
# Author: midoks <midoks@163.com>
|
||||
# ---------------------------------------------------------------------------------
|
||||
|
||||
# ---------------------------------------------------------------------------------
|
||||
# 系统信息操作
|
||||
# ---------------------------------------------------------------------------------
|
||||
|
||||
|
||||
import psutil
|
||||
import time
|
||||
import os
|
||||
import re
|
||||
import math
|
||||
import sys
|
||||
import json
|
||||
|
||||
from flask import Flask, session
|
||||
from flask import request
|
||||
|
||||
import db
|
||||
import mw
|
||||
|
||||
import config_api
|
||||
import requests
|
||||
|
||||
from threading import Thread
|
||||
from time import sleep
|
||||
|
||||
|
||||
def mw_async(f):
|
||||
def wrapper(*args, **kwargs):
|
||||
thr = Thread(target=f, args=args, kwargs=kwargs)
|
||||
thr.start()
|
||||
return wrapper
|
||||
|
||||
|
||||
class system_api:
|
||||
setupPath = None
|
||||
pids = None
|
||||
|
||||
def __init__(self):
|
||||
self.setupPath = mw.getServerDir()
|
||||
|
||||
##### ----- start ----- ###
|
||||
def networkApi(self):
|
||||
data = self.getNetWork()
|
||||
return mw.getJson(data)
|
||||
|
||||
def updateServerApi(self):
|
||||
stype = request.args.get('type', 'check')
|
||||
version = request.args.get('version', '')
|
||||
return self.updateServer(stype, version)
|
||||
|
||||
def systemTotalApi(self):
|
||||
data = self.getSystemTotal()
|
||||
return mw.getJson(data)
|
||||
|
||||
def diskInfoApi(self):
|
||||
diskInfo = self.getDiskInfo()
|
||||
return mw.getJson(diskInfo)
|
||||
|
||||
def setControlApi(self):
|
||||
stype = request.form.get('type', '')
|
||||
day = request.form.get('day', '')
|
||||
data = self.setControl(stype, day)
|
||||
return data
|
||||
|
||||
def getLoadAverageApi(self):
|
||||
start = request.args.get('start', '')
|
||||
end = request.args.get('end', '')
|
||||
data = self.getLoadAverageData(start, end)
|
||||
return mw.getJson(data)
|
||||
|
||||
def getCpuIoApi(self):
|
||||
start = request.args.get('start', '')
|
||||
end = request.args.get('end', '')
|
||||
data = self.getCpuIoData(start, end)
|
||||
return mw.getJson(data)
|
||||
|
||||
def getDiskIoApi(self):
|
||||
start = request.args.get('start', '')
|
||||
end = request.args.get('end', '')
|
||||
data = self.getDiskIoData(start, end)
|
||||
return mw.getJson(data)
|
||||
|
||||
def getNetworkIoApi(self):
|
||||
start = request.args.get('start', '')
|
||||
end = request.args.get('end', '')
|
||||
data = self.getNetWorkIoData(start, end)
|
||||
return mw.getJson(data)
|
||||
|
||||
def rememoryApi(self):
|
||||
os.system('sync')
|
||||
scriptFile = mw.getRunDir() + '/script/rememory.sh'
|
||||
mw.execShell("/bin/bash " + scriptFile)
|
||||
data = self.getMemInfo()
|
||||
return mw.getJson(data)
|
||||
|
||||
# 重启面板
|
||||
def restartApi(self):
|
||||
self.restartMw()
|
||||
return mw.returnJson(True, '面板已重启!')
|
||||
|
||||
def restartServerApi(self):
|
||||
if mw.isAppleSystem():
|
||||
return mw.returnJson(False, "开发环境不可重起")
|
||||
self.restartServer()
|
||||
return mw.returnJson(True, '正在重启服务器!')
|
||||
##### ----- end ----- ###
|
||||
|
||||
def restartTask(self):
|
||||
initd = mw.getRunDir() + '/scripts/init.d/mw'
|
||||
if os.path.exists(initd):
|
||||
os.system(initd + ' ' + 'restart_task')
|
||||
return True
|
||||
|
||||
def restartMw(self):
|
||||
mw.writeFile('data/restart.pl', 'True')
|
||||
return True
|
||||
|
||||
@mw_async
|
||||
def restartServer(self):
|
||||
if not mw.isRestart():
|
||||
return mw.returnJson(False, '请等待所有安装任务完成再执行!')
|
||||
mw.execShell("sync && init 6 &")
|
||||
return mw.returnJson(True, '命令发送成功!')
|
||||
|
||||
# 名取PID
|
||||
def getPid(self, pname):
|
||||
try:
|
||||
if not self.pids:
|
||||
self.pids = psutil.pids()
|
||||
for pid in self.pids:
|
||||
if psutil.Process(pid).name() == pname:
|
||||
return True
|
||||
return False
|
||||
except:
|
||||
return False
|
||||
|
||||
# 检查端口是否占用
|
||||
def isOpen(self, port):
|
||||
import socket
|
||||
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
try:
|
||||
s.connect(('127.0.0.1', int(port)))
|
||||
s.shutdown(2)
|
||||
return True
|
||||
except:
|
||||
return False
|
||||
|
||||
# 检测指定进程是否存活
|
||||
def checkProcess(self, pid):
|
||||
try:
|
||||
if not self.pids:
|
||||
self.pids = psutil.pids()
|
||||
if int(pid) in self.pids:
|
||||
return True
|
||||
return False
|
||||
except:
|
||||
return False
|
||||
|
||||
def getPanelInfo(self, get=None):
|
||||
# 取面板配置
|
||||
address = mw.GetLocalIp()
|
||||
try:
|
||||
try:
|
||||
port = web.ctx.host.split(':')[1]
|
||||
except:
|
||||
port = mw.readFile('data/port.pl')
|
||||
except:
|
||||
port = '7200'
|
||||
domain = ''
|
||||
if os.path.exists('data/domain.conf'):
|
||||
domain = mw.readFile('data/domain.conf')
|
||||
|
||||
autoUpdate = ''
|
||||
if os.path.exists('data/autoUpdate.pl'):
|
||||
autoUpdate = 'checked'
|
||||
limitip = ''
|
||||
if os.path.exists('data/limitip.conf'):
|
||||
limitip = mw.readFile('data/limitip.conf')
|
||||
|
||||
templates = []
|
||||
for template in os.listdir('templates/'):
|
||||
if os.path.isdir('templates/' + template):
|
||||
templates.append(template)
|
||||
template = mw.readFile('data/templates.pl')
|
||||
|
||||
check502 = ''
|
||||
if os.path.exists('data/502Task.pl'):
|
||||
check502 = 'checked'
|
||||
return {'port': port, 'address': address, 'domain': domain, 'auto': autoUpdate, '502': check502, 'limitip': limitip, 'templates': templates, 'template': template}
|
||||
|
||||
def getSystemTotal(self, interval=1):
|
||||
# 取系统统计信息
|
||||
data = self.getMemInfo()
|
||||
cpu = self.getCpuInfo(interval)
|
||||
data['cpuNum'] = cpu[1]
|
||||
data['cpuRealUsed'] = cpu[0]
|
||||
data['time'] = self.getBootTime()
|
||||
data['system'] = self.getSystemVersion()
|
||||
data['isuser'] = mw.M('users').where(
|
||||
'username=?', ('admin',)).count()
|
||||
data['version'] = '0.0.1'
|
||||
return data
|
||||
|
||||
def getLoadAverage(self):
|
||||
c = os.getloadavg()
|
||||
data = {}
|
||||
data['one'] = round(float(c[0]), 2)
|
||||
data['five'] = round(float(c[1]), 2)
|
||||
data['fifteen'] = round(float(c[2]), 2)
|
||||
data['max'] = psutil.cpu_count() * 2
|
||||
data['limit'] = data['max']
|
||||
data['safe'] = data['max'] * 0.75
|
||||
return data
|
||||
|
||||
def getAllInfo(self, get):
|
||||
data = {}
|
||||
data['load_average'] = self.GetLoadAverage(get)
|
||||
data['title'] = self.GetTitle()
|
||||
data['network'] = self.GetNetWorkApi(get)
|
||||
data['panel_status'] = not os.path.exists(
|
||||
'/www/server/mdserver-web/data/close.pl')
|
||||
import firewalls
|
||||
ssh_info = firewalls.firewalls().GetSshInfo(None)
|
||||
data['enable_ssh_status'] = ssh_info['status']
|
||||
data['disable_ping_status'] = not ssh_info['ping']
|
||||
data['time'] = self.GetBootTime()
|
||||
# data['system'] = self.GetSystemVersion();
|
||||
# data['mem'] = self.GetMemInfo();
|
||||
data['version'] = web.ctx.session.version
|
||||
return data
|
||||
|
||||
def getTitle(self):
|
||||
titlePl = 'data/title.pl'
|
||||
title = 'Linux面板'
|
||||
if os.path.exists(titlePl):
|
||||
title = mw.readFile(titlePl).strip()
|
||||
return title
|
||||
|
||||
def getSystemVersion(self):
|
||||
# 取操作系统版本
|
||||
if mw.getOs() == 'darwin':
|
||||
data = mw.execShell('sw_vers')[0]
|
||||
data_list = data.strip().split("\n")
|
||||
mac_version = ''
|
||||
for x in data_list:
|
||||
mac_version += x.split("\t")[1] + ' '
|
||||
return mac_version
|
||||
|
||||
redhat_series = '/etc/redhat-release'
|
||||
if os.path.exists(redhat_series):
|
||||
version = mw.readFile('/etc/redhat-release')
|
||||
version = version.replace('release ', '').strip()
|
||||
return version
|
||||
|
||||
os_series = '/etc/os-release'
|
||||
if os.path.exists(os_series):
|
||||
version = mw.execShell(
|
||||
"cat /etc/*-release | grep PRETTY_NAME | awk -F = '{print $2}' | awk -F '\"' '{print $2}'")
|
||||
return version[0].strip()
|
||||
|
||||
return '未识别系统信息'
|
||||
|
||||
def getBootTime(self):
|
||||
# 取系统启动时间
|
||||
uptime = mw.readFile('/proc/uptime')
|
||||
if uptime == False:
|
||||
start_time = psutil.boot_time()
|
||||
run_time = time.time() - start_time
|
||||
else:
|
||||
run_time = uptime.split()[0]
|
||||
tStr = float(run_time)
|
||||
min = tStr / 60
|
||||
hours = min / 60
|
||||
days = math.floor(hours / 24)
|
||||
hours = math.floor(hours - (days * 24))
|
||||
min = math.floor(min - (days * 60 * 24) - (hours * 60))
|
||||
return mw.getInfo('已不间断运行: {1}天{2}小时{3}分钟', (str(int(days)), str(int(hours)), str(int(min))))
|
||||
|
||||
def getCpuInfo(self, interval=1):
|
||||
# 取CPU信息
|
||||
cpuCount = psutil.cpu_count()
|
||||
cpuLogicalNum = psutil.cpu_count(logical=False)
|
||||
used = psutil.cpu_percent(interval=interval)
|
||||
|
||||
if os.path.exists('/proc/cpuinfo'):
|
||||
c_tmp = mw.readFile('/proc/cpuinfo')
|
||||
d_tmp = re.findall("physical id.+", c_tmp)
|
||||
cpuLogicalNum = len(set(d_tmp))
|
||||
|
||||
used_all = psutil.cpu_percent(percpu=True)
|
||||
cpu_name = mw.getCpuType() + " * {}".format(cpuLogicalNum)
|
||||
return used, cpuCount, used_all, cpu_name, cpuCount, cpuLogicalNum
|
||||
|
||||
def getMemInfo(self):
|
||||
# 取内存信息
|
||||
mem = psutil.virtual_memory()
|
||||
if mw.getOs() == 'darwin':
|
||||
memInfo = {
|
||||
'memTotal': mem.total / 1024 / 1024
|
||||
}
|
||||
memInfo['memRealUsed'] = memInfo['memTotal'] * (mem.percent / 100)
|
||||
else:
|
||||
memInfo = {
|
||||
'memTotal': mem.total / 1024 / 1024,
|
||||
'memFree': mem.free / 1024 / 1024,
|
||||
'memBuffers': mem.buffers / 1024 / 1024,
|
||||
'memCached': mem.cached / 1024 / 1024
|
||||
}
|
||||
|
||||
memInfo['memRealUsed'] = memInfo['memTotal'] - \
|
||||
memInfo['memFree'] - memInfo['memBuffers'] - \
|
||||
memInfo['memCached']
|
||||
return memInfo
|
||||
|
||||
def getMemUsed(self):
|
||||
# 取内存使用率
|
||||
try:
|
||||
import psutil
|
||||
mem = psutil.virtual_memory()
|
||||
|
||||
if mw.getOs() == 'darwin':
|
||||
return mem.percent
|
||||
|
||||
memInfo = {'memTotal': mem.total / 1024 / 1024, 'memFree': mem.free / 1024 / 1024,
|
||||
'memBuffers': mem.buffers / 1024 / 1024, 'memCached': mem.cached / 1024 / 1024}
|
||||
tmp = memInfo['memTotal'] - memInfo['memFree'] - \
|
||||
memInfo['memBuffers'] - memInfo['memCached']
|
||||
tmp1 = memInfo['memTotal'] / 100
|
||||
return (tmp / tmp1)
|
||||
except Exception as ex:
|
||||
return 1
|
||||
|
||||
def getDiskInfo(self):
|
||||
info = self.getDiskInfo2()
|
||||
if len(info) != 0:
|
||||
return info
|
||||
|
||||
# 取磁盘分区信息
|
||||
diskIo = psutil.disk_partitions()
|
||||
diskInfo = []
|
||||
|
||||
for disk in diskIo:
|
||||
if disk[1] == '/mnt/cdrom':
|
||||
continue
|
||||
if disk[1] == '/boot':
|
||||
continue
|
||||
tmp = {}
|
||||
tmp['path'] = disk[1]
|
||||
size_tmp = psutil.disk_usage(disk[1])
|
||||
tmp['size'] = [mw.toSize(size_tmp[0]), mw.toSize(
|
||||
size_tmp[1]), mw.toSize(size_tmp[2]), str(size_tmp[3]) + '%']
|
||||
diskInfo.append(tmp)
|
||||
return diskInfo
|
||||
|
||||
def getDiskInfo2(self):
|
||||
# 取磁盘分区信息
|
||||
temp = mw.execShell(
|
||||
"df -h -P|grep '/'|grep -v tmpfs | grep -v devfs")[0]
|
||||
tempInodes = mw.execShell(
|
||||
"df -i -P|grep '/'|grep -v tmpfs | grep -v devfs")[0]
|
||||
temp1 = temp.split('\n')
|
||||
tempInodes1 = tempInodes.split('\n')
|
||||
diskInfo = []
|
||||
n = 0
|
||||
cuts = ['/mnt/cdrom', '/boot', '/boot/efi', '/dev',
|
||||
'/dev/shm', '/zroot', '/run/lock', '/run', '/run/shm', '/run/user']
|
||||
for tmp in temp1:
|
||||
n += 1
|
||||
inodes = tempInodes1[n - 1].split()
|
||||
disk = tmp.split()
|
||||
if len(disk) < 5:
|
||||
continue
|
||||
if disk[1].find('M') != -1:
|
||||
continue
|
||||
if disk[1].find('K') != -1:
|
||||
continue
|
||||
if len(disk[5].split('/')) > 4:
|
||||
continue
|
||||
if disk[5] in cuts:
|
||||
continue
|
||||
arr = {}
|
||||
arr['path'] = disk[5]
|
||||
tmp1 = [disk[1], disk[2], disk[3], disk[4]]
|
||||
arr['size'] = tmp1
|
||||
arr['inodes'] = [inodes[1], inodes[2], inodes[3], inodes[4]]
|
||||
diskInfo.append(arr)
|
||||
return diskInfo
|
||||
|
||||
# 清理系统垃圾
|
||||
def clearSystem(self, get):
|
||||
count = total = 0
|
||||
tmp_total, tmp_count = self.ClearMail()
|
||||
count += tmp_count
|
||||
total += tmp_total
|
||||
tmp_total, tmp_count = self.ClearOther()
|
||||
count += tmp_count
|
||||
total += tmp_total
|
||||
return count, total
|
||||
|
||||
# 清理邮件日志
|
||||
def clearMail(self):
|
||||
rpath = '/var/spool'
|
||||
total = count = 0
|
||||
import shutil
|
||||
con = ['cron', 'anacron', 'mail']
|
||||
for d in os.listdir(rpath):
|
||||
if d in con:
|
||||
continue
|
||||
dpath = rpath + '/' + d
|
||||
time.sleep(0.2)
|
||||
num = size = 0
|
||||
for n in os.listdir(dpath):
|
||||
filename = dpath + '/' + n
|
||||
fsize = os.path.getsize(filename)
|
||||
size += fsize
|
||||
if os.path.isdir(filename):
|
||||
shutil.rmtree(filename)
|
||||
else:
|
||||
os.remove(filename)
|
||||
print('mail clear ok')
|
||||
num += 1
|
||||
total += size
|
||||
count += num
|
||||
return total, count
|
||||
|
||||
# 清理其它
|
||||
def clearOther(self):
|
||||
clearPath = [
|
||||
{'path': '/www/server/mdserver-web', 'find': 'testDisk_'},
|
||||
{'path': '/www/wwwlogs', 'find': 'log'},
|
||||
{'path': '/tmp', 'find': 'panelBoot.pl'},
|
||||
{'path': '/www/server/mdserver-web/install', 'find': '.rpm'}
|
||||
]
|
||||
|
||||
total = count = 0
|
||||
for c in clearPath:
|
||||
for d in os.listdir(c['path']):
|
||||
if d.find(c['find']) == -1:
|
||||
continue
|
||||
filename = c['path'] + '/' + d
|
||||
fsize = os.path.getsize(filename)
|
||||
total += fsize
|
||||
if os.path.isdir(filename):
|
||||
shutil.rmtree(filename)
|
||||
else:
|
||||
os.remove(filename)
|
||||
count += 1
|
||||
mw.restartWeb()
|
||||
os.system('echo > /tmp/panelBoot.pl')
|
||||
return total, count
|
||||
|
||||
def psutilNetIoCounters(self):
|
||||
'''
|
||||
统计网卡流量
|
||||
'''
|
||||
stat_pl = 'data/only_netio_counters.pl'
|
||||
if os.path.exists(stat_pl):
|
||||
local_lo = (0, 0, 0, 0)
|
||||
ioName = psutil.net_io_counters(pernic=True).keys()
|
||||
for x in ioName:
|
||||
|
||||
if x.find("lo") > -1:
|
||||
local_lo = psutil.net_io_counters(
|
||||
pernic=True).get(x)[:4]
|
||||
|
||||
all_io = psutil.net_io_counters()[:4]
|
||||
result_io = tuple([all_io[i] - local_lo[i]
|
||||
for i in range(0, len(all_io))])
|
||||
|
||||
# print(local_lo)
|
||||
# print(all_io)
|
||||
# print(result_io)
|
||||
return result_io
|
||||
return psutil.net_io_counters()[:4]
|
||||
|
||||
def getNetWork(self):
|
||||
# 取网络流量信息
|
||||
try:
|
||||
# 取网络流量信息
|
||||
networkIo = self.psutilNetIoCounters()
|
||||
if not "otime" in session:
|
||||
session['up'] = networkIo[0]
|
||||
session['down'] = networkIo[1]
|
||||
session['otime'] = time.time()
|
||||
|
||||
ntime = time.time()
|
||||
networkInfo = {}
|
||||
networkInfo['upTotal'] = networkIo[0]
|
||||
networkInfo['downTotal'] = networkIo[1]
|
||||
networkInfo['up'] = round(float(
|
||||
networkIo[0] - session['up']) / 1024 / (ntime - session['otime']), 2)
|
||||
networkInfo['down'] = round(
|
||||
float(networkIo[1] - session['down']) / 1024 / (ntime - session['otime']), 2)
|
||||
networkInfo['downPackets'] = networkIo[3]
|
||||
networkInfo['upPackets'] = networkIo[2]
|
||||
|
||||
# print networkIo[1], session['down'], ntime, session['otime']
|
||||
session['up'] = networkIo[0]
|
||||
session['down'] = networkIo[1]
|
||||
session['otime'] = time.time()
|
||||
|
||||
networkInfo['cpu'] = self.getCpuInfo()
|
||||
networkInfo['load'] = self.getLoadAverage()
|
||||
networkInfo['mem'] = self.getMemInfo()
|
||||
|
||||
return networkInfo
|
||||
except Exception as e:
|
||||
print("getNetWork error:", e)
|
||||
return None
|
||||
|
||||
def getNetWorkIoData(self, start, end):
|
||||
# 取指定时间段的网络Io
|
||||
data = mw.M('network').dbfile('system').where("addtime>=? AND addtime<=?", (start, end)).field(
|
||||
'id,up,down,total_up,total_down,down_packets,up_packets,addtime').order('id asc').select()
|
||||
return self.toAddtime(data)
|
||||
|
||||
def getDiskIoData(self, start, end):
|
||||
# 取指定时间段的磁盘Io
|
||||
data = mw.M('diskio').dbfile('system').where("addtime>=? AND addtime<=?", (start, end)).field(
|
||||
'id,read_count,write_count,read_bytes,write_bytes,read_time,write_time,addtime').order('id asc').select()
|
||||
return self.toAddtime(data)
|
||||
|
||||
def getCpuIoData(self, start, end):
|
||||
# 取指定时间段的CpuIo
|
||||
data = mw.M('cpuio').dbfile('system').where("addtime>=? AND addtime<=?",
|
||||
(start, end)).field('id,pro,mem,addtime').order('id asc').select()
|
||||
return self.toAddtime(data, True)
|
||||
|
||||
def getLoadAverageData(self, start, end):
|
||||
data = mw.M('load_average').dbfile('system').where("addtime>=? AND addtime<=?", (
|
||||
start, end)).field('id,pro,one,five,fifteen,addtime').order('id asc').select()
|
||||
return self.toAddtime(data)
|
||||
|
||||
# 格式化addtime列
|
||||
def toAddtime(self, data, tomem=False):
|
||||
import time
|
||||
if tomem:
|
||||
import psutil
|
||||
mPre = (psutil.virtual_memory().total / 1024 / 1024) / 100
|
||||
length = len(data)
|
||||
he = 1
|
||||
if length > 100:
|
||||
he = 1
|
||||
if length > 1000:
|
||||
he = 3
|
||||
if length > 10000:
|
||||
he = 15
|
||||
if he == 1:
|
||||
for i in range(length):
|
||||
data[i]['addtime'] = time.strftime(
|
||||
'%m/%d %H:%M', time.localtime(float(data[i]['addtime'])))
|
||||
if tomem and data[i]['mem'] > 100:
|
||||
data[i]['mem'] = data[i]['mem'] / mPre
|
||||
|
||||
return data
|
||||
else:
|
||||
count = 0
|
||||
tmp = []
|
||||
for value in data:
|
||||
if count < he:
|
||||
count += 1
|
||||
continue
|
||||
value['addtime'] = time.strftime(
|
||||
'%m/%d %H:%M', time.localtime(float(value['addtime'])))
|
||||
if tomem and value['mem'] > 100:
|
||||
value['mem'] = value['mem'] / mPre
|
||||
tmp.append(value)
|
||||
count = 0
|
||||
return tmp
|
||||
|
||||
def setControl(self, stype, day):
|
||||
|
||||
filename = 'data/control.conf'
|
||||
stat_pl = 'data/only_netio_counters.pl'
|
||||
|
||||
if stype == '0':
|
||||
mw.execShell("rm -rf " + filename)
|
||||
elif stype == '1':
|
||||
_day = int(day)
|
||||
if _day < 1:
|
||||
return mw.returnJson(False, "设置失败!")
|
||||
mw.writeFile(filename, day)
|
||||
elif stype == '2':
|
||||
mw.execShell("rm -rf " + stat_pl)
|
||||
elif stype == '3':
|
||||
mw.execShell("echo 'True' > " + stat_pl)
|
||||
elif stype == 'del':
|
||||
if not mw.isRestart():
|
||||
return mw.returnJson(False, '请等待所有安装任务完成再执行')
|
||||
os.remove("data/system.db")
|
||||
|
||||
sql = db.Sql().dbfile('system')
|
||||
csql = mw.readFile('data/sql/system.sql')
|
||||
csql_list = csql.split(';')
|
||||
for index in range(len(csql_list)):
|
||||
sql.execute(csql_list[index], ())
|
||||
return mw.returnJson(True, "监控服务已关闭")
|
||||
else:
|
||||
data = {}
|
||||
if os.path.exists(filename):
|
||||
try:
|
||||
data['day'] = int(mw.readFile(filename))
|
||||
except:
|
||||
data['day'] = 30
|
||||
data['status'] = True
|
||||
else:
|
||||
data['day'] = 30
|
||||
data['status'] = False
|
||||
|
||||
if os.path.exists(stat_pl):
|
||||
data['stat_all_status'] = True
|
||||
else:
|
||||
data['stat_all_status'] = False
|
||||
|
||||
return mw.getJson(data)
|
||||
|
||||
return mw.returnJson(True, "设置成功!")
|
||||
|
||||
def versionDiff(self, old, new):
|
||||
'''
|
||||
test 测试
|
||||
new 有新版本
|
||||
none 没有新版本
|
||||
'''
|
||||
new_list = new.split('.')
|
||||
if len(new_list) > 3:
|
||||
return 'test'
|
||||
|
||||
old_list = old.split('.')
|
||||
ret = 'none'
|
||||
|
||||
isHasNew = True
|
||||
if int(new_list[0]) == int(old_list[0]) and int(new_list[1]) == int(old_list[1]) and int(new_list[2]) == int(old_list[2]):
|
||||
isHasNew = False
|
||||
|
||||
if isHasNew:
|
||||
return 'new'
|
||||
return ret
|
||||
|
||||
def getServerInfo(self):
|
||||
import urllib.request
|
||||
import ssl
|
||||
upAddr = 'https://api.github.com/repos/midoks/mdserver-web/releases/latest'
|
||||
try:
|
||||
context = ssl._create_unverified_context()
|
||||
req = urllib.request.urlopen(upAddr, context=context, timeout=3)
|
||||
result = req.read().decode('utf-8')
|
||||
version = json.loads(result)
|
||||
return version
|
||||
except Exception as e:
|
||||
print('getServerInfo', e)
|
||||
return {}
|
||||
|
||||
def updateServer(self, stype, version=''):
|
||||
# 更新服务
|
||||
try:
|
||||
if not mw.isRestart():
|
||||
return mw.returnJson(False, '请等待所有安装任务完成再执行!')
|
||||
|
||||
version_new_info = self.getServerInfo()
|
||||
version_now = config_api.config_api().getVersion()
|
||||
|
||||
new_ver = version_new_info['name']
|
||||
|
||||
if stype == 'check':
|
||||
if not 'name' in version_new_info:
|
||||
return mw.returnJson(False, '服务器数据或网络有问题!')
|
||||
|
||||
diff = self.versionDiff(version_now, new_ver)
|
||||
if diff == 'new':
|
||||
return mw.returnJson(True, '有新版本!', new_ver)
|
||||
elif diff == 'test':
|
||||
return mw.returnJson(True, '有测试版本!', new_ver)
|
||||
else:
|
||||
return mw.returnJson(False, '已经是最新,无需更新!')
|
||||
|
||||
if stype == 'info':
|
||||
if not 'name' in version_new_info:
|
||||
return mw.returnJson(False, '服务器数据有问题!')
|
||||
diff = self.versionDiff(version_now, new_ver)
|
||||
data = {}
|
||||
data['version'] = new_ver
|
||||
data['content'] = version_new_info[
|
||||
'body'].replace("\n", "<br/>")
|
||||
return mw.returnJson(True, '更新信息!', data)
|
||||
|
||||
if stype == 'update':
|
||||
if version == '':
|
||||
return mw.returnJson(False, '缺少版本信息!')
|
||||
|
||||
if new_ver != version:
|
||||
return mw.returnJson(False, '更新失败,请重试!')
|
||||
|
||||
toPath = mw.getRootDir() + '/temp'
|
||||
if not os.path.exists(toPath):
|
||||
mw.execShell('mkdir -p ' + toPath)
|
||||
|
||||
newUrl = "https://github.com/midoks/mdserver-web/archive/refs/tags/" + version + ".zip"
|
||||
|
||||
dist_mw = toPath + '/mw.zip'
|
||||
if not os.path.exists(dist_mw):
|
||||
mw.execShell(
|
||||
'wget --no-check-certificate -O ' + dist_mw + ' ' + newUrl)
|
||||
|
||||
dist_to = toPath + "/mdserver-web-" + version
|
||||
if not os.path.exists(dist_to):
|
||||
os.system('unzip -o ' + toPath +
|
||||
'/mw.zip' + ' -d ' + toPath)
|
||||
|
||||
cmd_cp = 'cp -rf ' + toPath + '/mdserver-web-' + \
|
||||
version + '/* ' + mw.getServerDir() + '/mdserver-web'
|
||||
mw.execShell(cmd_cp)
|
||||
|
||||
mw.execShell('rm -rf ' + toPath + '/mdserver-web-' + version)
|
||||
mw.execShell('rm -rf ' + toPath + '/mw.zip')
|
||||
|
||||
self.restartMw()
|
||||
return mw.returnJson(True, '安装更新成功!')
|
||||
|
||||
return mw.returnJson(False, '已经是最新,无需更新!')
|
||||
except Exception as ex:
|
||||
# print('updateServer', ex)
|
||||
return mw.returnJson(False, "连接服务器失败!" + str(ex))
|
||||
|
||||
# 修复面板
|
||||
def repPanel(self, get):
|
||||
vp = ''
|
||||
if mw.readFile('/www/server/mdserver-web/class/common.py').find('checkSafe') != -1:
|
||||
vp = '_pro'
|
||||
mw.ExecShell("wget -O update.sh " + mw.get_url() +
|
||||
"/install/update" + vp + ".sh && bash update.sh")
|
||||
if hasattr(web.ctx.session, 'getCloudPlugin'):
|
||||
del(web.ctx.session['getCloudPlugin'])
|
||||
return True
|
||||
@@ -1,103 +0,0 @@
|
||||
# coding: utf-8
|
||||
|
||||
# ---------------------------------------------------------------------------------
|
||||
# MW-Linux面板
|
||||
# ---------------------------------------------------------------------------------
|
||||
# copyright (c) 2018-∞(https://github.com/midoks/mdserver-web) All rights reserved.
|
||||
# ---------------------------------------------------------------------------------
|
||||
# Author: midoks <midoks@163.com>
|
||||
# ---------------------------------------------------------------------------------
|
||||
|
||||
# ---------------------------------------------------------------------------------
|
||||
# 任务页操作
|
||||
# ---------------------------------------------------------------------------------
|
||||
|
||||
|
||||
import psutil
|
||||
import time
|
||||
import os
|
||||
import sys
|
||||
import mw
|
||||
import re
|
||||
import json
|
||||
import pwd
|
||||
|
||||
|
||||
from flask import request
|
||||
|
||||
|
||||
class task_api:
|
||||
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
def countApi(self):
|
||||
c = mw.M('tasks').where("status!=?", ('1',)).count()
|
||||
return str(c)
|
||||
|
||||
def listApi(self):
|
||||
|
||||
p = request.form.get('p', '1')
|
||||
limit = request.form.get('limit', '10').strip()
|
||||
search = request.form.get('search', '').strip()
|
||||
|
||||
start = (int(p) - 1) * int(limit)
|
||||
limit_str = str(start) + ',' + str(limit)
|
||||
|
||||
_list = mw.M('tasks').where('', ()).field(
|
||||
'id,name,type,status,addtime,start,end').limit(limit_str).order('id desc').select()
|
||||
_ret = {}
|
||||
_ret['data'] = _list
|
||||
|
||||
count = mw.M('tasks').where('', ()).count()
|
||||
_page = {}
|
||||
_page['count'] = count
|
||||
_page['tojs'] = 'remind'
|
||||
_page['p'] = p
|
||||
|
||||
# return data
|
||||
_ret['count'] = count
|
||||
_ret['page'] = mw.getPage(_page)
|
||||
return mw.getJson(_ret)
|
||||
|
||||
def getExecLogApi(self):
|
||||
file = os.getcwd() + "/tmp/panelExec.log"
|
||||
v = mw.getLastLine(file, 100)
|
||||
return v
|
||||
|
||||
def getTaskSpeedApi(self):
|
||||
tempFile = mw.getRunDir() + '/tmp/panelExec.log'
|
||||
freshFile = mw.getRunDir() + '/tmp/panelFresh'
|
||||
|
||||
find = mw.M('tasks').where('status=? OR status=?',
|
||||
('-1', '0')).field('id,type,name,execstr').find()
|
||||
if not len(find):
|
||||
return mw.returnJson(False, '当前没有任务队列在执行-2!')
|
||||
|
||||
mw.triggerTask()
|
||||
|
||||
data = {}
|
||||
data['name'] = find['name']
|
||||
data['execstr'] = find['execstr']
|
||||
if find['type'] == 'download':
|
||||
readLine = ""
|
||||
for i in range(3):
|
||||
try:
|
||||
readLine = mw.readFile(tempFile)
|
||||
if len(readLine) > 10:
|
||||
data['msg'] = json.loads(readLine)
|
||||
data['isDownload'] = True
|
||||
break
|
||||
except Exception as e:
|
||||
if i == 2:
|
||||
mw.M('tasks').where("id=?", (find['id'],)).save(
|
||||
'status', ('0',))
|
||||
return mw.returnJson(False, '当前没有任务队列在执行-4:' + str(e))
|
||||
time.sleep(0.5)
|
||||
else:
|
||||
data['msg'] = mw.getLastLine(tempFile, 10)
|
||||
data['isDownload'] = False
|
||||
|
||||
data['task'] = mw.M('tasks').where("status!=?", ('1',)).field(
|
||||
'id,status,name,type').order("id asc").select()
|
||||
return mw.getJson(data)
|
||||
@@ -1,143 +0,0 @@
|
||||
# coding: utf-8
|
||||
|
||||
# ---------------------------------------------------------------------------------
|
||||
# MW-Linux面板
|
||||
# ---------------------------------------------------------------------------------
|
||||
# copyright (c) 2018-∞(https://github.com/midoks/mdserver-web) All rights reserved.
|
||||
# ---------------------------------------------------------------------------------
|
||||
# Author: midoks <midoks@163.com>
|
||||
# ---------------------------------------------------------------------------------
|
||||
|
||||
# ---------------------------------------------------------------------------------
|
||||
# 验证码操作
|
||||
# ---------------------------------------------------------------------------------
|
||||
|
||||
|
||||
import random
|
||||
import math
|
||||
from PIL import Image, ImageDraw, ImageFont, ImageFilter
|
||||
|
||||
|
||||
class vieCode:
|
||||
__fontSize = 20 # 字体大小
|
||||
__width = 120 # 画布宽度
|
||||
__heigth = 45 # 画布高度
|
||||
__length = 4 # 验证码长度
|
||||
__draw = None # 画布
|
||||
__img = None # 图片资源
|
||||
__code = None # 验证码字符
|
||||
__str = None # 自定义验证码字符集
|
||||
__inCurve = True # 是否画干扰线
|
||||
__inNoise = True # 是否画干扰点
|
||||
__type = 2 # 验证码类型 1、纯字母 2、数字字母混合
|
||||
__fontPatn = 'class/fonts/2.ttf' # 字体
|
||||
|
||||
def GetCodeImage(self, size=80, length=4):
|
||||
'''获取验证码图片
|
||||
@param int size 验证码大小
|
||||
@param int length 验证码长度
|
||||
'''
|
||||
# 准备基础数据
|
||||
self.__length = length
|
||||
self.__fontSize = size
|
||||
self.__width = self.__fontSize * self.__length
|
||||
self.__heigth = int(self.__fontSize * 1.5)
|
||||
|
||||
# 生成验证码图片
|
||||
self.__createCode()
|
||||
self.__createImage()
|
||||
self.__createNoise()
|
||||
self.__printString()
|
||||
self.__cerateFilter()
|
||||
|
||||
return self.__img, self.__code
|
||||
|
||||
def __cerateFilter(self):
|
||||
'''模糊处理'''
|
||||
self.__img = self.__img.filter(ImageFilter.BLUR)
|
||||
filter = ImageFilter.ModeFilter(8)
|
||||
self.__img = self.__img.filter(filter)
|
||||
|
||||
def __createCode(self):
|
||||
'''创建验证码字符'''
|
||||
# 是否自定义字符集合
|
||||
if not self.__str:
|
||||
# 源文本
|
||||
number = "3456789"
|
||||
srcLetter = "qwertyuipasdfghjkzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM"
|
||||
srcUpper = srcLetter.upper()
|
||||
if self.__type == 1:
|
||||
self.__str = number
|
||||
else:
|
||||
self.__str = srcLetter + srcUpper + number
|
||||
|
||||
# 构造验证码
|
||||
self.__code = random.sample(self.__str, self.__length)
|
||||
|
||||
def __createImage(self):
|
||||
'''创建画布'''
|
||||
bgColor = (random.randint(200, 255), random.randint(
|
||||
200, 255), random.randint(200, 255))
|
||||
self.__img = Image.new('RGB', (self.__width, self.__heigth), bgColor)
|
||||
self.__draw = ImageDraw.Draw(self.__img)
|
||||
|
||||
def __createNoise(self):
|
||||
'''画干扰点'''
|
||||
if not self.__inNoise:
|
||||
return
|
||||
font = ImageFont.truetype(self.__fontPatn, int(self.__fontSize / 1.5))
|
||||
for i in range(5):
|
||||
# 杂点颜色
|
||||
noiseColor = (random.randint(150, 200), random.randint(
|
||||
150, 200), random.randint(150, 200))
|
||||
putStr = random.sample(self.__str, 2)
|
||||
for j in range(2):
|
||||
# 绘杂点
|
||||
size = (random.randint(-10, self.__width),
|
||||
random.randint(-10, self.__heigth))
|
||||
self.__draw.text(size, putStr[j], font=font, fill=noiseColor)
|
||||
pass
|
||||
|
||||
def __createCurve(self):
|
||||
'''画干扰线'''
|
||||
if not self.__inCurve:
|
||||
return
|
||||
x = y = 0
|
||||
|
||||
# 计算曲线系数
|
||||
a = random.uniform(1, self.__heigth / 2)
|
||||
b = random.uniform(-self.__width / 4, self.__heigth / 4)
|
||||
f = random.uniform(-self.__heigth / 4, self.__heigth / 4)
|
||||
t = random.uniform(self.__heigth, self.__width * 2)
|
||||
xend = random.randint(self.__width / 2, self.__width * 2)
|
||||
w = (2 * math.pi) / t
|
||||
|
||||
# 画曲线
|
||||
color = (random.randint(30, 150), random.randint(
|
||||
30, 150), random.randint(30, 150))
|
||||
for x in range(xend):
|
||||
if w != 0:
|
||||
for k in range(int(self.__heigth / 10)):
|
||||
y = a * math.sin(w * x + f) + b + self.__heigth / 2
|
||||
i = int(self.__fontSize / 5)
|
||||
while i > 0:
|
||||
px = x + i
|
||||
py = y + i + k
|
||||
self.__draw.point((px, py), color)
|
||||
i -= i
|
||||
|
||||
def __printString(self):
|
||||
'''打印验证码字符串'''
|
||||
font = ImageFont.truetype(self.__fontPatn, self.__fontSize)
|
||||
x = 0
|
||||
# 打印字符到画板
|
||||
for i in range(self.__length):
|
||||
# 设置字体随机颜色
|
||||
color = (random.randint(30, 150), random.randint(
|
||||
30, 150), random.randint(30, 150))
|
||||
# 计算座标
|
||||
x = random.uniform(self.__fontSize * i * 0.95,
|
||||
self.__fontSize * i * 1.1)
|
||||
y = self.__fontSize * random.uniform(0.3, 0.5)
|
||||
# 打印字符
|
||||
self.__draw.text((x, y), self.__code[i], font=font, fill=color)
|
||||
@@ -1,5 +0,0 @@
|
||||
|
||||
- fcgi-client.py
|
||||
```
|
||||
https://pypi.org/project/flup6/1.1.1/#files
|
||||
```
|
||||
@@ -1,408 +0,0 @@
|
||||
# Copyright (c) 2006 Allan Saddi <allan@saddi.com>
|
||||
# All rights reserved.
|
||||
#
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions
|
||||
# are met:
|
||||
# 1. Redistributions of source code must retain the above copyright
|
||||
# notice, this list of conditions and the following disclaimer.
|
||||
# 2. Redistributions in binary form must reproduce the above copyright
|
||||
# notice, this list of conditions and the following disclaimer in the
|
||||
# documentation and/or other materials provided with the distribution.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
||||
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
||||
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
||||
# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
||||
# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
||||
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
||||
# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
||||
# SUCH DAMAGE.
|
||||
#
|
||||
# $Id$
|
||||
#
|
||||
# Copyright (c) 2011 Vladimir Rusinov <vladimir@greenmice.info>
|
||||
|
||||
__author__ = 'Allan Saddi <allan@saddi.com>'
|
||||
__version__ = '$Revision$'
|
||||
import sys
|
||||
import select
|
||||
import struct
|
||||
import socket
|
||||
import errno
|
||||
import types
|
||||
|
||||
|
||||
__all__ = ['FCGIApp']
|
||||
|
||||
# Constants from the spec.
|
||||
FCGI_LISTENSOCK_FILENO = 0
|
||||
|
||||
FCGI_HEADER_LEN = 8
|
||||
|
||||
FCGI_VERSION_1 = 1
|
||||
|
||||
FCGI_BEGIN_REQUEST = 1
|
||||
FCGI_ABORT_REQUEST = 2
|
||||
FCGI_END_REQUEST = 3
|
||||
FCGI_PARAMS = 4
|
||||
FCGI_STDIN = 5
|
||||
FCGI_STDOUT = 6
|
||||
FCGI_STDERR = 7
|
||||
FCGI_DATA = 8
|
||||
FCGI_GET_VALUES = 9
|
||||
FCGI_GET_VALUES_RESULT = 10
|
||||
FCGI_UNKNOWN_TYPE = 11
|
||||
FCGI_MAXTYPE = FCGI_UNKNOWN_TYPE
|
||||
|
||||
FCGI_NULL_REQUEST_ID = 0
|
||||
|
||||
FCGI_KEEP_CONN = 1
|
||||
|
||||
FCGI_RESPONDER = 1
|
||||
FCGI_AUTHORIZER = 2
|
||||
FCGI_FILTER = 3
|
||||
|
||||
FCGI_REQUEST_COMPLETE = 0
|
||||
FCGI_CANT_MPX_CONN = 1
|
||||
FCGI_OVERLOADED = 2
|
||||
FCGI_UNKNOWN_ROLE = 3
|
||||
|
||||
FCGI_MAX_CONNS = 'FCGI_MAX_CONNS'
|
||||
FCGI_MAX_REQS = 'FCGI_MAX_REQS'
|
||||
FCGI_MPXS_CONNS = 'FCGI_MPXS_CONNS'
|
||||
|
||||
FCGI_Header = '!BBHHBx'
|
||||
FCGI_BeginRequestBody = '!HB5x'
|
||||
FCGI_EndRequestBody = '!LB3x'
|
||||
FCGI_UnknownTypeBody = '!B7x'
|
||||
|
||||
FCGI_BeginRequestBody_LEN = struct.calcsize(FCGI_BeginRequestBody)
|
||||
FCGI_EndRequestBody_LEN = struct.calcsize(FCGI_EndRequestBody)
|
||||
FCGI_UnknownTypeBody_LEN = struct.calcsize(FCGI_UnknownTypeBody)
|
||||
|
||||
if __debug__:
|
||||
import time
|
||||
|
||||
# Set non-zero to write debug output to a file.
|
||||
DEBUG = 0
|
||||
DEBUGLOG = '/www/server/mdserver-web/logs/fastcgi.log'
|
||||
|
||||
def _debug(level, msg):
|
||||
if DEBUG < level:
|
||||
return
|
||||
|
||||
try:
|
||||
f = open(DEBUGLOG, 'a')
|
||||
f.write('%sfcgi: %s\n' % (time.ctime()[4:-4], msg))
|
||||
f.close()
|
||||
except:
|
||||
pass
|
||||
|
||||
|
||||
def decode_pair(s, pos=0):
|
||||
"""
|
||||
Decodes a name/value pair.
|
||||
|
||||
The number of bytes decoded as well as the name/value pair
|
||||
are returned.
|
||||
"""
|
||||
nameLength = ord(s[pos])
|
||||
if nameLength & 128:
|
||||
nameLength = struct.unpack('!L', s[pos:pos + 4])[0] & 0x7fffffff
|
||||
pos += 4
|
||||
else:
|
||||
pos += 1
|
||||
|
||||
valueLength = ord(s[pos])
|
||||
if valueLength & 128:
|
||||
valueLength = struct.unpack('!L', s[pos:pos + 4])[0] & 0x7fffffff
|
||||
pos += 4
|
||||
else:
|
||||
pos += 1
|
||||
|
||||
name = s[pos:pos + nameLength]
|
||||
pos += nameLength
|
||||
value = s[pos:pos + valueLength]
|
||||
pos += valueLength
|
||||
|
||||
return (pos, (name, value))
|
||||
|
||||
|
||||
def encode_pair(name, value):
|
||||
"""
|
||||
Encodes a name/value pair.
|
||||
|
||||
The encoded string is returned.
|
||||
"""
|
||||
nameLength = len(name)
|
||||
if nameLength < 128:
|
||||
s = chr(nameLength).encode()
|
||||
else:
|
||||
s = struct.pack('!L', nameLength | 0x80000000)
|
||||
|
||||
valueLength = len(value)
|
||||
if valueLength < 128:
|
||||
s += chr(valueLength).encode()
|
||||
else:
|
||||
s += struct.pack('!L', valueLength | 0x80000000)
|
||||
|
||||
return s + name + value
|
||||
|
||||
|
||||
class Record(object):
|
||||
"""
|
||||
A FastCGI Record.
|
||||
|
||||
Used for encoding/decoding records.
|
||||
"""
|
||||
|
||||
def __init__(self, type=FCGI_UNKNOWN_TYPE, requestId=FCGI_NULL_REQUEST_ID):
|
||||
self.version = FCGI_VERSION_1
|
||||
self.type = type
|
||||
self.requestId = requestId
|
||||
self.contentLength = 0
|
||||
self.paddingLength = 0
|
||||
self.contentData = ''
|
||||
|
||||
def _recvall(sock, length):
|
||||
"""
|
||||
Attempts to receive length bytes from a socket, blocking if necessary.
|
||||
(Socket may be blocking or non-blocking.)
|
||||
"""
|
||||
dataList = []
|
||||
recvLen = 0
|
||||
while length:
|
||||
try:
|
||||
data = sock.recv(length)
|
||||
except socket.error as e:
|
||||
if e[0] == errno.EAGAIN:
|
||||
select.select([sock], [], [])
|
||||
continue
|
||||
else:
|
||||
raise
|
||||
if not data: # EOF
|
||||
break
|
||||
dataList.append(data)
|
||||
dataLen = len(data)
|
||||
recvLen += dataLen
|
||||
length -= dataLen
|
||||
return b''.join(dataList), recvLen
|
||||
_recvall = staticmethod(_recvall)
|
||||
|
||||
def read(self, sock):
|
||||
"""Read and decode a Record from a socket."""
|
||||
try:
|
||||
header, length = self._recvall(sock, FCGI_HEADER_LEN)
|
||||
except:
|
||||
raise EOFError
|
||||
|
||||
if length < FCGI_HEADER_LEN:
|
||||
raise EOFError
|
||||
|
||||
self.version, self.type, self.requestId, self.contentLength, \
|
||||
self.paddingLength = struct.unpack(FCGI_Header, header)
|
||||
|
||||
if __debug__:
|
||||
_debug(9, 'read: fd = %d, type = %d, requestId = %d, '
|
||||
'contentLength = %d' %
|
||||
(sock.fileno(), self.type, self.requestId,
|
||||
self.contentLength))
|
||||
|
||||
if self.contentLength:
|
||||
try:
|
||||
self.contentData, length = self._recvall(sock,
|
||||
self.contentLength)
|
||||
except:
|
||||
raise EOFError
|
||||
|
||||
if length < self.contentLength:
|
||||
raise EOFError
|
||||
|
||||
if self.paddingLength:
|
||||
try:
|
||||
self._recvall(sock, self.paddingLength)
|
||||
except:
|
||||
raise EOFError
|
||||
|
||||
def _sendall(sock, data):
|
||||
"""
|
||||
Writes data to a socket and does not return until all the data is sent.
|
||||
"""
|
||||
length = len(data)
|
||||
while length:
|
||||
try:
|
||||
sent = sock.send(data)
|
||||
except socket.error as e:
|
||||
if e[0] == errno.EAGAIN:
|
||||
select.select([], [sock], [])
|
||||
continue
|
||||
else:
|
||||
raise
|
||||
data = data[sent:]
|
||||
length -= sent
|
||||
_sendall = staticmethod(_sendall)
|
||||
|
||||
def write(self, sock):
|
||||
"""Encode and write a Record to a socket."""
|
||||
self.paddingLength = -self.contentLength & 7
|
||||
|
||||
if __debug__:
|
||||
_debug(9, 'write: fd = %d, type = %d, requestId = %d, '
|
||||
'contentLength = %d' %
|
||||
(sock.fileno(), self.type, self.requestId,
|
||||
self.contentLength))
|
||||
|
||||
header = struct.pack(FCGI_Header, self.version, self.type,
|
||||
self.requestId, self.contentLength,
|
||||
self.paddingLength)
|
||||
self._sendall(sock, header)
|
||||
if self.contentLength:
|
||||
self._sendall(sock, self.contentData)
|
||||
if self.paddingLength:
|
||||
self._sendall(sock, b'\x00' * self.paddingLength)
|
||||
|
||||
|
||||
class FCGIApp(object):
|
||||
|
||||
def __init__(self, connect=None, host=None, port=None, filterEnviron=True):
|
||||
if host is not None:
|
||||
assert port is not None
|
||||
connect = (host, port)
|
||||
|
||||
self._connect = connect
|
||||
self._filterEnviron = filterEnviron
|
||||
|
||||
def __call__(self, environ, io, start_response=None):
|
||||
# For sanity's sake, we don't care about FCGI_MPXS_CONN
|
||||
# (connection multiplexing). For every request, we obtain a new
|
||||
# transport socket, perform the request, then discard the socket.
|
||||
# This is, I believe, how mod_fastcgi does things...
|
||||
|
||||
sock = self._getConnection()
|
||||
|
||||
# Since this is going to be the only request on this connection,
|
||||
# set the request ID to 1.
|
||||
requestId = 1
|
||||
|
||||
# Begin the request
|
||||
rec = Record(FCGI_BEGIN_REQUEST, requestId)
|
||||
rec.contentData = struct.pack(FCGI_BeginRequestBody, FCGI_RESPONDER, 0)
|
||||
rec.contentLength = FCGI_BeginRequestBody_LEN
|
||||
rec.write(sock)
|
||||
|
||||
# Filter WSGI environ and send it as FCGI_PARAMS
|
||||
if self._filterEnviron:
|
||||
params = self._defaultFilterEnviron(environ)
|
||||
else:
|
||||
params = self._lightFilterEnviron(environ)
|
||||
# TODO: Anything not from environ that needs to be sent also?
|
||||
# return '200 OK',[],str(params),''
|
||||
self._fcgiParams(sock, requestId, params)
|
||||
self._fcgiParams(sock, requestId, {})
|
||||
|
||||
# Transfer wsgi.input to FCGI_STDIN
|
||||
content_length = int(environ.get('CONTENT_LENGTH') or 0)
|
||||
s = ''
|
||||
#io = StringIO(stdin)
|
||||
while True:
|
||||
if not io:
|
||||
break
|
||||
chunk_size = min(content_length, 4096)
|
||||
s = io.read(chunk_size)
|
||||
content_length -= len(s)
|
||||
rec = Record(FCGI_STDIN, requestId)
|
||||
rec.contentData = s
|
||||
rec.contentLength = len(s)
|
||||
rec.write(sock)
|
||||
if not s:
|
||||
break
|
||||
# Empty FCGI_DATA stream
|
||||
rec = Record(FCGI_DATA, requestId)
|
||||
rec.write(sock)
|
||||
return sock
|
||||
|
||||
def _getConnection(self):
|
||||
if self._connect is not None:
|
||||
# The simple case. Create a socket and connect to the
|
||||
# application.
|
||||
if isinstance(self._connect, str):
|
||||
sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
|
||||
sock.connect(self._connect)
|
||||
elif hasattr(socket, 'create_connection'):
|
||||
sock = socket.create_connection(self._connect)
|
||||
else:
|
||||
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
sock.connect(self._connect)
|
||||
return sock
|
||||
|
||||
# To be done when I have more time...
|
||||
# , 'Launching and managing FastCGI programs not yet implemented'
|
||||
raise NotImplementedError
|
||||
|
||||
def _fcgiGetValues(self, sock, vars):
|
||||
# Construct FCGI_GET_VALUES record
|
||||
outrec = Record(FCGI_GET_VALUES)
|
||||
data = []
|
||||
for name in vars:
|
||||
data.append(encode_pair(name, ''))
|
||||
data = ''.join(data)
|
||||
outrec.contentData = data
|
||||
outrec.contentLength = len(data)
|
||||
outrec.write(sock)
|
||||
|
||||
# Await response
|
||||
inrec = Record()
|
||||
inrec.read(sock)
|
||||
result = {}
|
||||
if inrec.type == FCGI_GET_VALUES_RESULT:
|
||||
pos = 0
|
||||
while pos < inrec.contentLength:
|
||||
pos, (name, value) = decode_pair(inrec.contentData, pos)
|
||||
result[name] = value
|
||||
return result
|
||||
|
||||
def _fcgiParams(self, sock, requestId, params):
|
||||
rec = Record(FCGI_PARAMS, requestId)
|
||||
data = []
|
||||
for name, value in params.items():
|
||||
data.append(encode_pair(name.encode(
|
||||
'latin-1'), value.encode('latin-1')))
|
||||
data = b''.join(data)
|
||||
rec.contentData = data
|
||||
rec.contentLength = len(data)
|
||||
rec.write(sock)
|
||||
|
||||
_environPrefixes = ['SERVER_', 'HTTP_', 'REQUEST_', 'REMOTE_', 'PATH_',
|
||||
'CONTENT_', 'DOCUMENT_', 'SCRIPT_']
|
||||
_environCopies = ['SCRIPT_NAME', 'QUERY_STRING', 'AUTH_TYPE']
|
||||
_environRenames = []
|
||||
|
||||
def _defaultFilterEnviron(self, environ):
|
||||
result = {}
|
||||
for n in environ.keys():
|
||||
iv = False
|
||||
for p in self._environPrefixes:
|
||||
if n.startswith(p):
|
||||
result[n] = environ[n]
|
||||
iv = True
|
||||
if n in self._environCopies:
|
||||
result[n] = environ[n]
|
||||
iv = True
|
||||
if n in self._environRenames:
|
||||
result[self._environRenames[n]] = environ[n]
|
||||
iv = True
|
||||
if not iv:
|
||||
result[n] = environ[n]
|
||||
|
||||
return result
|
||||
|
||||
def _lightFilterEnviron(self, environ):
|
||||
result = {}
|
||||
for n in environ.keys():
|
||||
if n.upper() == n:
|
||||
result[n] = environ[n]
|
||||
return result
|
||||
@@ -1,208 +0,0 @@
|
||||
# coding:utf-8
|
||||
|
||||
|
||||
import json
|
||||
import os
|
||||
import time
|
||||
import re
|
||||
import sys
|
||||
import time
|
||||
import struct
|
||||
import fcgi_client
|
||||
|
||||
FCGI_Header = '!BBHHBx'
|
||||
|
||||
if sys.version_info[0] == 2:
|
||||
try:
|
||||
from cStringIO import StringIO
|
||||
except:
|
||||
from StringIO import StringIO
|
||||
else:
|
||||
from io import BytesIO as StringIO
|
||||
|
||||
|
||||
def get_header_data(sock):
|
||||
'''
|
||||
@name 获取头部32KB数据
|
||||
@param sock socketobject(fastcgi套接字对象)
|
||||
@return bytes
|
||||
'''
|
||||
headers_data = b''
|
||||
total_len = 0
|
||||
header_len = 1024 * 128
|
||||
while True:
|
||||
fastcgi_header = sock.recv(8)
|
||||
if not fastcgi_header:
|
||||
break
|
||||
if len(fastcgi_header) != 8:
|
||||
headers_data += fastcgi_header
|
||||
break
|
||||
fast_pack = struct.unpack(FCGI_Header, fastcgi_header)
|
||||
if fast_pack[1] == 3:
|
||||
break
|
||||
|
||||
tlen = fast_pack[3]
|
||||
while tlen > 0:
|
||||
sd = sock.recv(tlen)
|
||||
if not sd:
|
||||
break
|
||||
headers_data += sd
|
||||
tlen -= len(sd)
|
||||
|
||||
total_len += fast_pack[3]
|
||||
if fast_pack[4]:
|
||||
sock.recv(fast_pack[4])
|
||||
if total_len > header_len:
|
||||
break
|
||||
return headers_data
|
||||
|
||||
|
||||
def format_header_data(headers_data):
|
||||
'''
|
||||
@name 格式化响应头
|
||||
@param headers_data bytes(fastcgi头部32KB数据)
|
||||
@return status int(响应状态), headers dict(响应头), bdata bytes(格式化响应头后的多余数据)
|
||||
'''
|
||||
status = '200 OK'
|
||||
headers = {}
|
||||
pos = 0
|
||||
while True:
|
||||
eolpos = headers_data.find(b'\n', pos)
|
||||
if eolpos < 0:
|
||||
break
|
||||
line = headers_data[pos:eolpos - 1]
|
||||
pos = eolpos + 1
|
||||
line = line.strip()
|
||||
if len(line) < 2:
|
||||
break
|
||||
if line.find(b':') == -1:
|
||||
continue
|
||||
header, value = line.split(b':', 1)
|
||||
header = header.strip()
|
||||
value = value.strip()
|
||||
if isinstance(header, bytes):
|
||||
header = header.decode()
|
||||
value = value.decode()
|
||||
if header == 'Status':
|
||||
status = value
|
||||
if status.find(' ') < 0:
|
||||
status += ' BTPanel'
|
||||
else:
|
||||
headers[header] = value
|
||||
bdata = headers_data[pos:]
|
||||
status = int(status.split(' ')[0])
|
||||
return status, headers, bdata
|
||||
|
||||
|
||||
def resp_sock(sock, bdata):
|
||||
'''
|
||||
@name 以流的方式发送剩余数据
|
||||
@param sock socketobject(fastcgi套接字对象)
|
||||
@param bdata bytes(格式化响应头后的多余数据)
|
||||
@return yield bytes
|
||||
'''
|
||||
# 发送除响应头以外的多余头部数据
|
||||
yield bdata
|
||||
while True:
|
||||
fastcgi_header = sock.recv(8)
|
||||
if not fastcgi_header:
|
||||
break
|
||||
if len(fastcgi_header) != 8:
|
||||
yield fastcgi_header
|
||||
break
|
||||
fast_pack = struct.unpack(FCGI_Header, fastcgi_header)
|
||||
if fast_pack[1] == 3:
|
||||
break
|
||||
tlen = fast_pack[3]
|
||||
while tlen > 0:
|
||||
sd = sock.recv(tlen)
|
||||
if not sd:
|
||||
break
|
||||
tlen -= len(sd)
|
||||
if sd:
|
||||
yield sd
|
||||
|
||||
if fast_pack[4]:
|
||||
sock.recv(fast_pack[4])
|
||||
sock.close()
|
||||
|
||||
|
||||
class fpm(object):
|
||||
|
||||
def __init__(self, sock=None, document_root='', last_path=''):
|
||||
'''
|
||||
@name 实例化FPM对象
|
||||
@param sock string(unixsocket路径)
|
||||
@param document_root string(PHP文档根目录)
|
||||
@return FPM
|
||||
'''
|
||||
if sock:
|
||||
self.fcgi_sock = sock
|
||||
if document_root[-1:] != '/':
|
||||
document_root += '/'
|
||||
self.document_root = document_root
|
||||
self.last_path = last_path
|
||||
|
||||
def load_url_public(self, url, content=b'', method='GET', content_type='application/x-www-form-urlencoded'):
|
||||
'''
|
||||
@name 转发URL到PHP-FPM 公共
|
||||
@param url string(URI地址)
|
||||
@param content stream(POST数据io对象)
|
||||
@return fastcgi-socket
|
||||
'''
|
||||
fcgi = fcgi_client.FCGIApp(connect=self.fcgi_sock)
|
||||
try:
|
||||
script_name, query_string = url.split('?')
|
||||
except ValueError:
|
||||
script_name = url
|
||||
query_string = ''
|
||||
|
||||
content_length = len(content)
|
||||
if content:
|
||||
content = StringIO(content)
|
||||
|
||||
env = {
|
||||
'SCRIPT_FILENAME': '%s%s' % (self.document_root, script_name),
|
||||
'QUERY_STRING': query_string,
|
||||
'REQUEST_METHOD': method,
|
||||
'SCRIPT_NAME': self.last_path + script_name,
|
||||
'REQUEST_URI': url,
|
||||
'GATEWAY_INTERFACE': 'CGI/1.1',
|
||||
'SERVER_SOFTWARE': 'MW-Panel',
|
||||
'REDIRECT_STATUS': '200',
|
||||
'CONTENT_TYPE': content_type,
|
||||
'CONTENT_LENGTH': str(content_length),
|
||||
'DOCUMENT_URI': script_name,
|
||||
'DOCUMENT_ROOT': self.document_root,
|
||||
'SERVER_PROTOCOL': 'HTTP/1.1',
|
||||
'REMOTE_ADDR': '127.0.0.1',
|
||||
'REMOTE_PORT': '7200',
|
||||
'SERVER_ADDR': '127.0.0.1',
|
||||
'SERVER_PORT': '80',
|
||||
'SERVER_NAME': 'MW-Panel'
|
||||
}
|
||||
|
||||
fpm_sock = fcgi(env, content)
|
||||
|
||||
_data = b''
|
||||
while True:
|
||||
fastcgi_header = fpm_sock.recv(8)
|
||||
if not fastcgi_header:
|
||||
break
|
||||
if len(fastcgi_header) != 8:
|
||||
_data += fastcgi_header
|
||||
break
|
||||
fast_pack = struct.unpack(FCGI_Header, fastcgi_header)
|
||||
if fast_pack[1] == 3:
|
||||
break
|
||||
tlen = fast_pack[3]
|
||||
while tlen > 0:
|
||||
sd = fpm_sock.recv(tlen)
|
||||
if not sd:
|
||||
break
|
||||
tlen -= len(sd)
|
||||
_data += sd
|
||||
if fast_pack[4]:
|
||||
fpm_sock.recv(fast_pack[4])
|
||||
status, headers, data = format_header_data(_data)
|
||||
return data
|
||||
@@ -1,113 +0,0 @@
|
||||
# coding: utf-8
|
||||
|
||||
import re
|
||||
import os
|
||||
import sys
|
||||
|
||||
import pymysql.cursors
|
||||
|
||||
|
||||
class ORM:
|
||||
__DB_PASS = None
|
||||
__DB_USER = 'root'
|
||||
__DB_PORT = 3306
|
||||
__DB_NAME = ''
|
||||
__DB_HOST = 'localhost'
|
||||
__DB_CONN = None
|
||||
__DB_CUR = None
|
||||
__DB_ERR = None
|
||||
__DB_CNF = '/etc/my.cnf'
|
||||
__DB_SOCKET = '/www/server/mysql/mysql.sock'
|
||||
|
||||
__DB_CHARSET = "utf8"
|
||||
|
||||
def __Conn(self):
|
||||
'''连接数据库'''
|
||||
try:
|
||||
|
||||
if os.path.exists(self.__DB_SOCKET):
|
||||
try:
|
||||
self.__DB_CONN = pymysql.connect(host=self.__DB_HOST, user=self.__DB_USER, passwd=self.__DB_PASS,
|
||||
database=self.__DB_NAME,
|
||||
port=int(self.__DB_PORT), charset=self.__DB_CHARSET, connect_timeout=1,
|
||||
unix_socket=self.__DB_SOCKET, cursorclass=pymysql.cursors.DictCursor)
|
||||
except Exception as e:
|
||||
self.__DB_HOST = '127.0.0.1'
|
||||
self.__DB_CONN = pymysql.connect(host=self.__DB_HOST, user=self.__DB_USER, passwd=self.__DB_PASS,
|
||||
database=self.__DB_NAME,
|
||||
port=int(self.__DB_PORT), charset=self.__DB_CHARSET, connect_timeout=1,
|
||||
unix_socket=self.__DB_SOCKET, cursorclass=pymysql.cursors.DictCursor)
|
||||
else:
|
||||
try:
|
||||
self.__DB_CONN = pymysql.connect(host=self.__DB_HOST, user=self.__DB_USER, passwd=self.__DB_PASS,
|
||||
database=self.__DB_NAME,
|
||||
port=int(self.__DB_PORT), charset=self.__DB_CHARSET, connect_timeout=1,
|
||||
cursorclass=pymysql.cursors.DictCursor)
|
||||
except Exception as e:
|
||||
self.__DB_HOST = '127.0.0.1'
|
||||
self.__DB_CONN = pymysql.connect(host=self.__DB_HOST, user=self.__DB_USER, passwd=self.__DB_PASS,
|
||||
database=self.__DB_NAME,
|
||||
port=int(self.__DB_PORT), charset=self.__DB_CHARSET, connect_timeout=1,
|
||||
cursorclass=pymysql.cursors.DictCursor)
|
||||
|
||||
self.__DB_CUR = self.__DB_CONN.cursor()
|
||||
return True
|
||||
except Exception as e:
|
||||
self.__DB_ERR = e
|
||||
return False
|
||||
|
||||
def setDbConf(self, conf):
|
||||
self.__DB_CNF = conf
|
||||
|
||||
def setSocket(self, sock):
|
||||
self.__DB_SOCKET = sock
|
||||
|
||||
def setCharset(self, charset):
|
||||
self.__DB_CHARSET = charset
|
||||
|
||||
def setPort(self, port):
|
||||
self.__DB_PORT = port
|
||||
|
||||
def setUser(self, user):
|
||||
self.__DB_USER = user
|
||||
|
||||
def setPwd(self, pwd):
|
||||
self.__DB_PASS = pwd
|
||||
|
||||
def getPwd(self):
|
||||
return self.__DB_PASS
|
||||
|
||||
def setDbName(self, name):
|
||||
self.__DB_NAME = name
|
||||
|
||||
def execute(self, sql):
|
||||
# 执行SQL语句返回受影响行
|
||||
if not self.__Conn():
|
||||
return self.__DB_ERR
|
||||
try:
|
||||
result = self.__DB_CUR.execute(sql)
|
||||
self.__DB_CONN.commit()
|
||||
self.__Close()
|
||||
return result
|
||||
except Exception as ex:
|
||||
return ex
|
||||
|
||||
def query(self, sql):
|
||||
# 执行SQL语句返回数据集
|
||||
if not self.__Conn():
|
||||
return self.__DB_ERR
|
||||
try:
|
||||
self.__DB_CUR.execute(sql)
|
||||
result = self.__DB_CUR.fetchall()
|
||||
# print(result)
|
||||
# 将元组转换成列表
|
||||
# data = map(list, result)
|
||||
self.__Close()
|
||||
return result
|
||||
except Exception as ex:
|
||||
return ex
|
||||
|
||||
def __Close(self):
|
||||
# 关闭连接
|
||||
self.__DB_CUR.close()
|
||||
self.__DB_CONN.close()
|
||||
@@ -1,115 +0,0 @@
|
||||
# coding: utf-8
|
||||
|
||||
import re
|
||||
import os
|
||||
import sys
|
||||
|
||||
|
||||
class ORM:
|
||||
__DB_PASS = None
|
||||
__DB_USER = 'root'
|
||||
__DB_PORT = 3306
|
||||
__DB_HOST = 'localhost'
|
||||
__DB_CONN = None
|
||||
__DB_CUR = None
|
||||
__DB_ERR = None
|
||||
__DB_CNF = '/etc/my.cnf'
|
||||
|
||||
__DB_SOCKET = '/www/server/mysql/mysql.sock'
|
||||
__DB_CHARSET = 'utf8'
|
||||
|
||||
def __Conn(self):
|
||||
'''连接MYSQL数据库'''
|
||||
try:
|
||||
|
||||
try:
|
||||
import MySQLdb
|
||||
except Exception as ex:
|
||||
self.__DB_ERR = ex
|
||||
return False
|
||||
|
||||
# print(self.__DB_HOST)
|
||||
# print(self.__DB_USER)
|
||||
# print(self.__DB_PASS)
|
||||
# print(self.__DB_PORT)
|
||||
# print(self.__DB_CHARSET)
|
||||
# print(self.__DB_SOCKET)
|
||||
if os.path.exists(self.__DB_SOCKET):
|
||||
try:
|
||||
self.__DB_CONN = MySQLdb.connect(host=self.__DB_HOST, user=self.__DB_USER, passwd=self.__DB_PASS,
|
||||
port=int(self.__DB_PORT), charset=self.__DB_CHARSET, connect_timeout=1, unix_socket=self.__DB_SOCKET)
|
||||
except Exception as e:
|
||||
print(e)
|
||||
self.__DB_HOST = '127.0.0.1'
|
||||
self.__DB_CONN = MySQLdb.connect(host=self.__DB_HOST, user=self.__DB_USER, passwd=self.__DB_PASS,
|
||||
port=int(self.__DB_PORT), charset=self.__DB_CHARSET, connect_timeout=1, unix_socket=self.__DB_SOCKET)
|
||||
else:
|
||||
try:
|
||||
self.__DB_CONN = MySQLdb.connect(host=self.__DB_HOST, user=self.__DB_USER, passwd=self.__DB_PASS,
|
||||
port=int(self.__DB_PORT), charset=self.__DB_CHARSET, connect_timeout=1)
|
||||
except Exception as e:
|
||||
self.__DB_HOST = '127.0.0.1'
|
||||
self.__DB_CONN = MySQLdb.connect(host=self.__DB_HOST, user=self.__DB_USER, passwd=self.__DB_PASS,
|
||||
port=int(self.__DB_PORT), charset=self.__DB_CHARSET, connect_timeout=1)
|
||||
|
||||
self.__DB_CUR = self.__DB_CONN.cursor()
|
||||
|
||||
return True
|
||||
except MySQLdb.Error as e:
|
||||
self.__DB_ERR = e
|
||||
return False
|
||||
|
||||
def setDbConf(self, conf):
|
||||
self.__DB_CNF = conf
|
||||
|
||||
def setSocket(self, sock):
|
||||
self.__DB_SOCKET = sock
|
||||
|
||||
def setCharset(self, charset):
|
||||
self.__DB_CHARSET = charset
|
||||
|
||||
def setPort(self, port):
|
||||
self.__DB_PORT = port
|
||||
|
||||
def setPwd(self, pwd):
|
||||
self.__DB_PASS = pwd
|
||||
|
||||
def getPwd(self):
|
||||
return self.__DB_PASS
|
||||
|
||||
def setDbName(self, name):
|
||||
self.__DB_NAME = name
|
||||
|
||||
def setUser(self, user):
|
||||
self.__DB_USER = user
|
||||
|
||||
def execute(self, sql):
|
||||
# 执行SQL语句返回受影响行
|
||||
if not self.__Conn():
|
||||
return self.__DB_ERR
|
||||
try:
|
||||
result = self.__DB_CUR.execute(sql)
|
||||
self.__DB_CONN.commit()
|
||||
self.__Close()
|
||||
return result
|
||||
except Exception as ex:
|
||||
return ex
|
||||
|
||||
def query(self, sql):
|
||||
# 执行SQL语句返回数据集
|
||||
if not self.__Conn():
|
||||
return self.__DB_ERR
|
||||
try:
|
||||
self.__DB_CUR.execute(sql)
|
||||
result = self.__DB_CUR.fetchall()
|
||||
# 将元组转换成列表
|
||||
data = map(list, result)
|
||||
self.__Close()
|
||||
return data
|
||||
except Exception as ex:
|
||||
return ex
|
||||
|
||||
# 关闭连接
|
||||
def __Close(self):
|
||||
self.__DB_CUR.close()
|
||||
self.__DB_CONN.close()
|
||||
@@ -1,7 +0,0 @@
|
||||
#!/bin/bash
|
||||
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin:/usr/local/lib/python2.7/bin
|
||||
|
||||
|
||||
|
||||
find . -name .DS_Store | xargs rm -rf
|
||||
find . -type d -name "*.pyc" | xargs rm -rf
|
||||
@@ -1,86 +0,0 @@
|
||||
#!/bin/bash
|
||||
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
|
||||
DIR=$(cd "$(dirname "$0")"; pwd)
|
||||
MDIR=$(dirname "$DIR")
|
||||
|
||||
|
||||
PATH=$PATH:$DIR/bin
|
||||
if [ -f bin/activate ];then
|
||||
source bin/activate
|
||||
fi
|
||||
|
||||
export LC_ALL="en_US.UTF-8"
|
||||
|
||||
|
||||
mw_start_task()
|
||||
{
|
||||
isStart=$(ps aux |grep 'task.py'|grep -v grep|awk '{print $2}')
|
||||
if [ "$isStart" == '' ];then
|
||||
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;
|
||||
fi
|
||||
echo -e "\033[32mdone\033[0m"
|
||||
else
|
||||
echo "Starting mw-tasks... mw-tasks (pid $(echo $isStart)) already running"
|
||||
fi
|
||||
}
|
||||
|
||||
mw_start(){
|
||||
gunicorn -c setting.py app:app
|
||||
#安全启动
|
||||
mw_start_task
|
||||
}
|
||||
|
||||
|
||||
mw_start_debug(){
|
||||
python3 task.py >> $DIR/logs/task.log 2>&1 &
|
||||
gunicorn -b :7200 -k gevent -w 1 app:app
|
||||
}
|
||||
|
||||
mw_start_debug2(){
|
||||
python3 task.py >> $DIR/logs/task.log 2>&1 &
|
||||
gunicorn -b :7200 -k geventwebsocket.gunicorn.workers.GeventWebSocketWorker -w 1 app:app
|
||||
}
|
||||
|
||||
|
||||
mw_stop()
|
||||
{
|
||||
PLIST=`ps -ef|grep app:app |grep -v grep|awk '{print $2}'`
|
||||
for i in $PLIST
|
||||
do
|
||||
kill -9 $i
|
||||
done
|
||||
|
||||
pids=`ps -ef|grep task.py | grep -v grep |awk '{print $2}'`
|
||||
arr=($pids)
|
||||
for p in ${arr[@]}
|
||||
do
|
||||
kill -9 $p
|
||||
done
|
||||
}
|
||||
|
||||
case "$1" in
|
||||
'start') mw_start;;
|
||||
'stop') mw_stop;;
|
||||
'restart')
|
||||
mw_stop
|
||||
mw_start
|
||||
;;
|
||||
'debug')
|
||||
mw_stop
|
||||
mw_start_debug
|
||||
;;
|
||||
'debug2')
|
||||
mw_stop
|
||||
mw_start_debug2
|
||||
;;
|
||||
esac
|
||||
@@ -1,68 +0,0 @@
|
||||
### 常用命令说明
|
||||
|
||||
|
||||
- 面板相关命令
|
||||
|
||||
```
|
||||
/etc/init.d/mw default
|
||||
|
||||
service mw [start|stop|reload|restart|status]
|
||||
```
|
||||
|
||||
- OpenResty
|
||||
|
||||
```
|
||||
|
||||
systemctl [start|stop|reload|restart|status] openresty
|
||||
|
||||
```
|
||||
|
||||
- MySQL
|
||||
|
||||
```
|
||||
|
||||
systemctl [start|stop|reload|restart|status] mysql
|
||||
|
||||
```
|
||||
|
||||
- MariaDB
|
||||
|
||||
```
|
||||
|
||||
systemctl [start|stop|reload|restart|status] mariadb
|
||||
|
||||
```
|
||||
|
||||
- PHP
|
||||
|
||||
```
|
||||
|
||||
systemctl [start|stop|reload|restart|status] php[54-81]
|
||||
|
||||
systemctl start php71
|
||||
```
|
||||
|
||||
- Redis
|
||||
|
||||
```
|
||||
|
||||
systemctl [start|stop|reload|restart|status] redis
|
||||
|
||||
```
|
||||
|
||||
- Memcached
|
||||
|
||||
```
|
||||
|
||||
systemctl [start|stop|reload|restart|status] memcached
|
||||
|
||||
```
|
||||
|
||||
|
||||
- sphinx
|
||||
|
||||
```
|
||||
|
||||
systemctl [start|stop|reload|restart|status] sphinx
|
||||
|
||||
```
|
||||
@@ -1,68 +0,0 @@
|
||||
### 兼容性测试报告
|
||||
|
||||
快速测试命令
|
||||
|
||||
```
|
||||
cd /www/server/mdserver-web/scripts/quick && bash debug.sh
|
||||
cd /www/server/mdserver-web/plugins/php/versions && bash all_test.sh
|
||||
```
|
||||
|
||||
|
||||
| 系统名称 | 面板 | OpenResty |
|
||||
| ----------------- |---------------|---------------|
|
||||
| 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 |
|
||||
| ----------------- |-------|-------|-------|-------|-------|-------|-------|-------|-------|-------|-------|
|
||||
| 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 |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |
|
||||
|
||||
|
||||
|
||||
| 系统名称 | MySQL55|MySQL56|MySQL57|MySQL80|
|
||||
| ----------------- |-------|--------|-------|-------|
|
||||
| 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 |✅ |✅ |✅ |✅ |
|
||||
|
||||
@@ -1,37 +0,0 @@
|
||||
[
|
||||
{
|
||||
"title":"全部",
|
||||
"type":0,
|
||||
"ps":""
|
||||
},
|
||||
{
|
||||
"title":"运行环境",
|
||||
"type":1,
|
||||
"ps":""
|
||||
},
|
||||
{
|
||||
"title":"数据软件",
|
||||
"type":2,
|
||||
"ps":""
|
||||
},
|
||||
{
|
||||
"title":"代码管理",
|
||||
"type":3,
|
||||
"ps":""
|
||||
},
|
||||
{
|
||||
"title":"系统工具",
|
||||
"type":4,
|
||||
"ps":""
|
||||
},
|
||||
{
|
||||
"title":"其他插件",
|
||||
"type":5,
|
||||
"ps":""
|
||||
},
|
||||
{
|
||||
"title":"辅助插件",
|
||||
"type":6,
|
||||
"ps":""
|
||||
}
|
||||
]
|
||||
@@ -1 +0,0 @@
|
||||
True
|
||||
@@ -1,134 +0,0 @@
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `backup` (
|
||||
`id` INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
`type` INTEGER,
|
||||
`name` TEXT,
|
||||
`pid` INTEGER,
|
||||
`filename` TEXT,
|
||||
`size` INTEGER,
|
||||
`addtime` TEXT
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `binding` (
|
||||
`id` INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
`pid` INTEGER,
|
||||
`domain` TEXT,
|
||||
`path` TEXT,
|
||||
`port` INTEGER,
|
||||
`addtime` TEXT
|
||||
);
|
||||
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `crontab` (
|
||||
`id` INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
`name` TEXT,
|
||||
`type` TEXT,
|
||||
`where1` TEXT,
|
||||
`where_hour` INTEGER,
|
||||
`where_minute` INTEGER,
|
||||
`echo` TEXT,
|
||||
`addtime` TEXT,
|
||||
`status` INTEGER DEFAULT '1',
|
||||
`save` INTEGER DEFAULT '3',
|
||||
`backup_to` TEXT DEFAULT 'off',
|
||||
`sname` TEXT,
|
||||
`sbody` TEXT,
|
||||
'stype' TEXT,
|
||||
`urladdress` TEXT
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `firewall` (
|
||||
`id` INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
`port` TEXT,
|
||||
`ps` TEXT,
|
||||
`addtime` TEXT
|
||||
);
|
||||
|
||||
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');
|
||||
|
||||
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `logs` (
|
||||
`id` INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
`type` TEXT,
|
||||
`log` TEXT,
|
||||
`uid` TEXT,
|
||||
`addtime` TEXT
|
||||
);
|
||||
ALTER TABLE `logs` ADD COLUMN `uid` INTEGER DEFAULT '1';
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `sites` (
|
||||
`id` INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
`name` TEXT,
|
||||
`path` TEXT,
|
||||
`status` TEXT,
|
||||
`index` TEXT,
|
||||
`type_id` INTEGER,
|
||||
`ps` TEXT,
|
||||
`edate` TEXT,
|
||||
`addtime` TEXT
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `site_types` (
|
||||
`id` INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
`name` TEXT
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `domain` (
|
||||
`id` INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
`pid` INTEGER,
|
||||
`name` TEXT,
|
||||
`port` INTEGER,
|
||||
`addtime` TEXT
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `users` (
|
||||
`id` INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
`username` TEXT,
|
||||
`password` TEXT,
|
||||
`login_ip` TEXT,
|
||||
`login_time` TEXT,
|
||||
`phone` TEXT,
|
||||
`email` TEXT
|
||||
);
|
||||
|
||||
INSERT INTO `users` (`id`, `username`, `password`, `login_ip`, `login_time`, `phone`, `email`) VALUES
|
||||
(1, 'admin', '21232f297a57a5a743894a0e4a801fc3', '192.168.0.10', '2022-02-02 00:00:00', 0, 'midoks@163.com');
|
||||
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `tasks` (
|
||||
`id` INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
`name` TEXT,
|
||||
`type` TEXT,
|
||||
`status` TEXT,
|
||||
`addtime` TEXT,
|
||||
`start` INTEGER,
|
||||
`end` INTEGER,
|
||||
`execstr` TEXT
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `temp_login` (
|
||||
`id` INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
`token` REAL,
|
||||
`salt` REAL,
|
||||
`state` INTEGER,
|
||||
`login_time` INTEGER,
|
||||
`login_addr` REAL,
|
||||
`logout_time` INTEGER,
|
||||
`expire` INTEGER,
|
||||
`addtime` INTEGER
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `panel` (
|
||||
`id` INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
`title` TEXT,
|
||||
`url` TEXT,
|
||||
`username` TEXT,
|
||||
`password` TEXT,
|
||||
`click` INTEGER,
|
||||
`addtime` INTEGER
|
||||
);
|
||||
@@ -1,37 +0,0 @@
|
||||
CREATE TABLE IF NOT EXISTS `network` (
|
||||
`id` INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
`up` INTEGER,
|
||||
`down` INTEGER,
|
||||
`total_up` INTEGER,
|
||||
`total_down` INTEGER,
|
||||
`down_packets` INTEGER,
|
||||
`up_packets` INTEGER,
|
||||
`addtime` INTEGER
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `cpuio` (
|
||||
`id` INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
`pro` INTEGER,
|
||||
`mem` INTEGER,
|
||||
`addtime` INTEGER
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `diskio` (
|
||||
`id` INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
`read_count` INTEGER,
|
||||
`write_count` INTEGER,
|
||||
`read_bytes` INTEGER,
|
||||
`write_bytes` INTEGER,
|
||||
`read_time` INTEGER,
|
||||
`write_time` INTEGER,
|
||||
`addtime` INTEGER
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `load_average` (
|
||||
`id` INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
`pro` REAL,
|
||||
`one` REAL,
|
||||
`five` REAL,
|
||||
`fifteen` REAL,
|
||||
`addtime` INTEGER
|
||||
);
|
||||
@@ -1,58 +0,0 @@
|
||||
server
|
||||
{
|
||||
listen {$PORT};
|
||||
listen [::]:{$PORT};
|
||||
server_name {$SERVER_NAME};
|
||||
index index.php index.html index.htm default.php default.htm default.html;
|
||||
root {$ROOT_DIR};
|
||||
|
||||
#SSL-START
|
||||
#error_page 404/404.html;
|
||||
#SSL-END
|
||||
|
||||
#301-START
|
||||
|
||||
#PROXY-START
|
||||
|
||||
#ERROR-PAGE-START
|
||||
#error_page 404 /404.html;
|
||||
#error_page 502 /502.html;
|
||||
#ERROR-PAGE-END
|
||||
|
||||
#PHP-INFO-START
|
||||
include {$PHP_DIR}/conf/enable-php-{$PHPVER}.conf;
|
||||
#PHP-INFO-END
|
||||
|
||||
#REWRITE-START
|
||||
include {$OR_REWRITE}/{$SERVER_NAME}.conf;
|
||||
#REWRITE-END
|
||||
|
||||
|
||||
#禁止访问的文件或目录
|
||||
location ~ ^/(\.user.ini|\.htaccess|\.git|\.svn|\.project|LICENSE|README.md)
|
||||
{
|
||||
return 404;
|
||||
}
|
||||
|
||||
#一键申请SSL证书验证目录相关设置
|
||||
location ~ \.well-known{
|
||||
allow all;
|
||||
}
|
||||
|
||||
location ~ .*\\.(gif|jpg|jpeg|png|bmp|swf)$
|
||||
{
|
||||
expires 30d;
|
||||
error_log off;
|
||||
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;
|
||||
}
|
||||
@@ -1,55 +0,0 @@
|
||||
|
||||
#BINDING-{$DIRBIND}-START
|
||||
server
|
||||
{
|
||||
listen {$PORT};
|
||||
server_name {$DIRBIND};
|
||||
index index.php index.html index.htm default.php default.htm default.html;
|
||||
root {$ROOT_DIR};
|
||||
|
||||
#SSL-START
|
||||
#error_page 404/404.html;
|
||||
#SSL-END
|
||||
|
||||
#ERROR-PAGE-START
|
||||
#error_page 404 /404.html;
|
||||
#error_page 502 /502.html;
|
||||
#ERROR-PAGE-END
|
||||
|
||||
#PHP-INFO-START
|
||||
include enable-php-{$PHPVER}.conf;
|
||||
#PHP-INFO-END
|
||||
|
||||
#REWRITE-START
|
||||
include {$OR_REWRITE}/{$SERVER_MAIN}.conf;
|
||||
#REWRITE-END
|
||||
|
||||
#禁止访问的文件或目录
|
||||
location ~ ^/(\.user.ini|\.htaccess|\.git|\.svn|\.project|LICENSE|README.md)
|
||||
{
|
||||
return 404;
|
||||
}
|
||||
|
||||
#一键申请SSL证书验证目录相关设置
|
||||
location ~ \.well-known{
|
||||
allow all;
|
||||
}
|
||||
|
||||
location ~ .*\\.(gif|jpg|jpeg|png|bmp|swf)$
|
||||
{
|
||||
expires 30d;
|
||||
error_log off;
|
||||
access_log /dev/null;
|
||||
}
|
||||
|
||||
location ~ .*\\.(js|css)?$
|
||||
{
|
||||
expires 12h;
|
||||
error_log off;
|
||||
access_log /dev/null;
|
||||
}
|
||||
|
||||
access_log {$LOGPATH}/{$SERVER_MAIN}_{$DIRBIND}.log;
|
||||
error_log {$LOGPATH}/{$SERVER_MAIN}_{$DIRBIND}.error.log;
|
||||
}
|
||||
#BINDING-{$DIRBIND}-END
|
||||
@@ -1,62 +0,0 @@
|
||||
FROM debian:10.12-slim
|
||||
|
||||
STOPSIGNAL SIGRTMIN+3
|
||||
|
||||
RUN rm -f /lib/systemd/system/multi-user.target.wants/* \
|
||||
/etc/systemd/system/*.wants/* \
|
||||
/lib/systemd/system/local-fs.target.wants/* \
|
||||
/lib/systemd/system/sockets.target.wants/*udev* \
|
||||
/lib/systemd/system/sockets.target.wants/*initctl* \
|
||||
/lib/systemd/system/sysinit.target.wants/systemd-tmpfiles-setup* \
|
||||
/lib/systemd/system/systemd-update-utmp*
|
||||
|
||||
RUN apt update -y && \
|
||||
apt install -y devscripts && \
|
||||
apt install -y wget zip unzip && \
|
||||
apt-get install -y locales && \
|
||||
localedef -i en_US -c -f UTF-8 -A /usr/share/locale/locale.alias en_US.UTF-8 && \
|
||||
locale-gen en_US.UTF-8 && \
|
||||
export LC_ALL=en_US.UTF-8 && \
|
||||
export LANG=en_US.UTF-8 && \
|
||||
export LANGUAGE=en_US.UTF-8 && \
|
||||
localedef -i en_US -f UTF-8 en_US.UTF-8 && \
|
||||
mkdir -p /www/server && \
|
||||
mkdir -p /www/wwwroot && \
|
||||
mkdir -p /www/wwwlogs && \
|
||||
mkdir -p /www/backup/database && \
|
||||
mkdir -p /www/backup/site && \
|
||||
wget -O /tmp/dev.zip https://github.com/midoks/mdserver-web/archive/refs/heads/dev.zip && \
|
||||
cd /tmp && unzip /tmp/dev.zip && \
|
||||
mv -f /tmp/mdserver-web-dev /www/server/mdserver-web && \
|
||||
rm -rf /tmp/dev.zip && \
|
||||
rm -rf /tmp/mdserver-web-dev && \
|
||||
cd /www/server/mdserver-web && \
|
||||
bash scripts/install/debian.sh && \
|
||||
cd /www/server/mdserver-web/ && \
|
||||
/www/server/mdserver-web/bin/python tools.py username username && \
|
||||
cd /www/server/mdserver-web/ && \
|
||||
/www/server/mdserver-web/bin/python tools.py panel password
|
||||
|
||||
RUN cd /www/server/mdserver-web/plugins/php && \
|
||||
bash install.sh install 74 && \
|
||||
cd /www/server/mdserver-web/plugins/openresty && \
|
||||
bash install.sh install 1.21.4.1 && \
|
||||
cd /www/server/mdserver-web/plugins/mysql && \
|
||||
bash install.sh install 5.6 && \
|
||||
cd /www/server/mdserver-web/plugins/phpmyadmin && \
|
||||
bash install.sh install 4.4.15 && \
|
||||
systemctl enable openresty && \
|
||||
systemctl enable php74 && \
|
||||
systemctl enable mysql
|
||||
|
||||
RUN rm -rf /www/server/mysql/data
|
||||
|
||||
ADD ./start.sh /start.sh
|
||||
ADD start.service /usr/lib/systemd/system/start.service
|
||||
#RUN systemctl enable start
|
||||
|
||||
CMD [ "/lib/systemd/systemd", "log-level=info", "unit=sysinit.target" ]
|
||||
|
||||
EXPOSE 7200 80 443 888
|
||||
|
||||
VOLUME [ "/www" ]
|
||||
@@ -1,9 +0,0 @@
|
||||
[Unit]
|
||||
Description=Customize the script
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
ExecStart=/start.sh
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
@@ -1 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
After Width: | Height: | Size: 55 KiB |
@@ -0,0 +1,96 @@
|
||||
<!DOCTYPE html>
|
||||
|
||||
<html lang="en">
|
||||
<head>
|
||||
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
|
||||
<link rel="icon" type="image/x-icon" href="./static/images/logo.png">
|
||||
<link type="text/css" href="./static/css/pages.css" media="all" rel="stylesheet">
|
||||
|
||||
<title>mdserver-web | Linux开源面板</title>
|
||||
<meta name="generator" content="Jekyll v3.9.3">
|
||||
<meta property="og:title" content="Linux开源面板">
|
||||
<meta property="og:locale" content="en_US">
|
||||
<meta name="description" content="Linux开源面板">
|
||||
<meta property="og:description" content="Linux开源面板">
|
||||
<link rel="canonical" href="https://www.midoks.me/">
|
||||
<meta property="og:url" content="https://www.midoks.me/">
|
||||
<meta property="og:site_name" content="Linux开源面板">
|
||||
<meta property="og:type" content="website">
|
||||
<meta name="twitter:card" content="summary">
|
||||
<meta property="twitter:title" content="Linux开源面板">
|
||||
<meta name="twitter:site" content="@github">
|
||||
<script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-7736695762519033"
|
||||
crossorigin="anonymous"></script>
|
||||
</head>
|
||||
|
||||
<body class="home" style="">
|
||||
|
||||
<section id="hero-spot" class="hero-spot">
|
||||
<a href="https://pages.github.com/">
|
||||
<img src="./static/images/logo.png" alt="Linux开源面板" class="logo">
|
||||
</a>
|
||||
|
||||
<h1>管理你的服务器</h1>
|
||||
|
||||
<a href="https://github.com/midoks/mdserver-web" class="help-link">Github</a>
|
||||
|
||||
<div id="slideshow">
|
||||
<img src="./static/images/mw.jpg" alt="mdserver-web" class="slide active" width="893">
|
||||
</div>
|
||||
</section>
|
||||
|
||||
|
||||
<section id="tutorial" class="tutorial">
|
||||
<h1>准备好开始了吗?</h1>
|
||||
|
||||
|
||||
<!-- ### Start of tutorials -->
|
||||
<ul id="user-site" class="tutorial-list wrapper active">
|
||||
|
||||
|
||||
<li id="terminal-step-1" class="option-terminal">
|
||||
<h4>安装面板</h4>
|
||||
|
||||
<div class="terminal">
|
||||
<div class="header"></div>
|
||||
<div class="shell">
|
||||
<p><span class="path">~</span><span class="prompt">$</span>curl -fsSL https://cdn.jsdelivr.net/gh/midoks/mdserver-web@latest/scripts/install.sh | bash</p>
|
||||
<p><span class="path">~</span><span class="prompt">$</span>mw default</p>
|
||||
<p>==================================================================</p>
|
||||
<p>MW-Panel default info!</p>
|
||||
<p>==================================================================</p>
|
||||
<p>MW-Panel-Url: http://0.0.0.0:13148/xxx</p>
|
||||
<p>username: yrajbw9q</p>
|
||||
<p>password: oqxotjgb</p>
|
||||
<p>Warning:</p>
|
||||
<p>If you cannot access the panel. </p>
|
||||
<p>release the following port (44010|888|80|443|22) in the security group.</p>
|
||||
<p>==================================================================</p>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
<!-- End of user site tutorial -->
|
||||
|
||||
</section>
|
||||
<!-- End of tutorial section -->
|
||||
|
||||
<script src="./static/js/jquery.js"></script>
|
||||
<script>window.slides = {"mdserver-web":"mw","mw-php":"php",}</script>
|
||||
<script src="./static/js/application.js"></script>
|
||||
|
||||
<footer class="page-footer">
|
||||
<ul class="site-footer-links right">
|
||||
<li><a href="https://bbs.midoks.icu">论坛</a></li>
|
||||
</ul>
|
||||
|
||||
<ul class="site-footer-links">
|
||||
<li>mdserver-web ©2018-∞ Linux面板</li>
|
||||
<li><a href="https://github.com/midoks/mdserver-web">Github</a></li>
|
||||
</ul>
|
||||
</footer>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -1,418 +0,0 @@
|
||||
# coding:utf-8
|
||||
|
||||
'''
|
||||
doc: https://docs.python.org/zh-cn/3/library/ftplib.html
|
||||
'''
|
||||
|
||||
import sys
|
||||
import io
|
||||
import os
|
||||
import time
|
||||
import re
|
||||
import json
|
||||
|
||||
import paramiko
|
||||
import ftplib
|
||||
|
||||
sys.path.append(os.getcwd() + "/class/core")
|
||||
import mw
|
||||
|
||||
DEBUG = True
|
||||
BLOCK_SIZE = 1024 * 1024 * 2
|
||||
# BLOCK_SIZE = 50
|
||||
PROGRESS_FILE_NAME = "PROGRESS_FILE_NAME"
|
||||
|
||||
"""
|
||||
=============自定义异常===================
|
||||
"""
|
||||
|
||||
|
||||
class OsError(Exception):
|
||||
"""OS端异常"""
|
||||
|
||||
|
||||
class ObjectNotFound(OsError):
|
||||
"""对象不存在时抛出的异常"""
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
message = "文件对象不存在。"
|
||||
super(ObjectNotFound, self).__init__(message, *args, **kwargs)
|
||||
|
||||
|
||||
class APIError(Exception):
|
||||
"""API参数错误异常"""
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
_api_error_msg = 'API资料校验失败,请核实!'
|
||||
super(APIError, self).__init__(_api_error_msg, *args, **kwargs)
|
||||
|
||||
|
||||
class FtpPSClient:
|
||||
_title = "FTP"
|
||||
_name = "ftp"
|
||||
__host = None
|
||||
__port = None
|
||||
__user = None
|
||||
__password = None
|
||||
default_port = 21
|
||||
default_backup_path = "/backup/"
|
||||
config_file = "cfg.json"
|
||||
|
||||
def __init__(self, load_config=True, timeout=10):
|
||||
self.timeout = timeout
|
||||
if load_config:
|
||||
data = self.get_config()
|
||||
self.injection_config(data)
|
||||
|
||||
def get_config(self):
|
||||
default_config = {
|
||||
"ftp_host": '',
|
||||
"ftp_user": '',
|
||||
"ftp_pass": '',
|
||||
"backup_path": self.default_backup_path
|
||||
}
|
||||
|
||||
cfg = mw.getServerDir() + "/backup_ftp/" + self.config_file
|
||||
if os.path.exists(cfg):
|
||||
data = mw.readFile(cfg)
|
||||
return json.loads(data)
|
||||
else:
|
||||
return default_config
|
||||
|
||||
def injection_config(self, data):
|
||||
host = data["ftp_host"].strip()
|
||||
if host.find(':') == -1:
|
||||
self.__port = self.default_port
|
||||
|
||||
self.__host = data['ftp_host'].strip()
|
||||
self.__user = data['ftp_user'].strip()
|
||||
self.__password = data['ftp_pass'].strip()
|
||||
bp = data['backup_path'].strip()
|
||||
if bp:
|
||||
self.backup_path = self.getPath(bp)
|
||||
else:
|
||||
self.backup_path = self.getPath(self.default_backup_path)
|
||||
|
||||
def authorize(self):
|
||||
try:
|
||||
if self.timeout is not None:
|
||||
ftp = ftplib.FTP(timeout=self.timeout)
|
||||
else:
|
||||
ftp = ftplib.FTP()
|
||||
|
||||
debuglevel = 0
|
||||
# if DEBUG:
|
||||
# debuglevel = 3
|
||||
ftp.set_debuglevel(debuglevel)
|
||||
# ftp.set_pasv(True)
|
||||
ftp.connect(self.__host, int(self.__port))
|
||||
ftp.login(self.__user, self.__password)
|
||||
return ftp
|
||||
except Exception as e:
|
||||
raise OsError("无法连接FTP客户端,请检查配置参数是否正确!")
|
||||
|
||||
# 取目录路径
|
||||
def getPath(self, path):
|
||||
if path[-1:] != '/':
|
||||
path += '/'
|
||||
if path[:1] != '/':
|
||||
path = '/' + path
|
||||
return path.replace('//', '/')
|
||||
|
||||
def generateDownloadUrl(self, object_name):
|
||||
|
||||
return 'ftp://' + \
|
||||
self.__user + ':' + \
|
||||
self.__password + '@' + \
|
||||
self.__host + ':' + \
|
||||
"/" + object_name
|
||||
|
||||
def buildDirName(self, data_type, file_name):
|
||||
import re
|
||||
prefix_dict = {
|
||||
"site": "web",
|
||||
"database": "db",
|
||||
"path": "path",
|
||||
}
|
||||
file_regx = prefix_dict.get(data_type) + "_(.+)_20\d+_\d+\."
|
||||
sub_search = re.search(file_regx, file_name)
|
||||
sub_path_name = ""
|
||||
if sub_search:
|
||||
sub_path_name = sub_search.groups()[0]
|
||||
sub_path_name += '/'
|
||||
|
||||
# 构建OS存储路径
|
||||
object_name = self.backup_path + \
|
||||
data_type + '/' + \
|
||||
sub_path_name + \
|
||||
file_name
|
||||
return object_name
|
||||
|
||||
def uploadFile(self, filename, data_type=None, *args, **kwargs):
|
||||
|
||||
client = self.authorize()
|
||||
|
||||
local_file_name = filename
|
||||
filename = os.path.abspath(filename)
|
||||
dirname = os.path.dirname(filename)
|
||||
temp_name = os.path.split(filename)[1]
|
||||
|
||||
object_name = self.buildDirName(data_type, temp_name)
|
||||
|
||||
upload_tmp_dir = os.path.join(dirname, ".upload_tmp")
|
||||
if not os.path.exists(upload_tmp_dir):
|
||||
os.mkdir(upload_tmp_dir)
|
||||
|
||||
print("|-正在上传文件到 {}".format(object_name))
|
||||
|
||||
total_bytes = os.path.getsize(filename)
|
||||
object_md5_name = mw.md5(object_name)
|
||||
pg_file = os.path.join(upload_tmp_dir, object_md5_name + ".pl")
|
||||
|
||||
block_size = BLOCK_SIZE
|
||||
if kwargs.get("block_size"):
|
||||
try:
|
||||
block_size = float(kwargs.get("block_size"))
|
||||
except:
|
||||
pass
|
||||
|
||||
remote_file_size = None
|
||||
if not os.path.exists(pg_file):
|
||||
# import uuid
|
||||
# uid = str(uuid.uuid1())
|
||||
progress_info = {
|
||||
"filename": local_file_name,
|
||||
"total_bytes": total_bytes,
|
||||
"uploaded_bytes": 0,
|
||||
}
|
||||
mw.writeFile(pg_file, json.dumps(progress_info))
|
||||
else:
|
||||
progress_info = json.loads(mw.readFile(pg_file))
|
||||
if total_bytes == progress_info.get("total_bytes"):
|
||||
# 取远程文件大小
|
||||
_max_loop = 10
|
||||
while _max_loop > 0:
|
||||
try:
|
||||
time.sleep(1)
|
||||
remote_file_size = client.size(object_name)
|
||||
if remote_file_size > total_bytes:
|
||||
remote_file_size = None
|
||||
break
|
||||
except Exception as e:
|
||||
if DEBUG:
|
||||
print(type(e))
|
||||
print(e)
|
||||
_max_loop -= 1
|
||||
else:
|
||||
remote_file_size = None
|
||||
|
||||
uploaded_bytes = 0 if remote_file_size is None else remote_file_size
|
||||
|
||||
dir_name = os.path.split(object_name)[0]
|
||||
if dir_name:
|
||||
self.createDirP(dir_name)
|
||||
|
||||
upload_start = time.time()
|
||||
|
||||
try:
|
||||
if total_bytes > 1024 * 1024 * 1024:
|
||||
with open(local_file_name, 'rb') as file_handler:
|
||||
if remote_file_size is not None:
|
||||
file_handler.seek(remote_file_size)
|
||||
|
||||
client.voidcmd("TYPE I")
|
||||
datasock = ''
|
||||
esize = ''
|
||||
|
||||
datasock, esize = client.ntransfercmd(
|
||||
"STOR " + object_name, remote_file_size)
|
||||
|
||||
while True:
|
||||
buf = file_handler.read(block_size)
|
||||
if not len(buf):
|
||||
break
|
||||
datasock.sendall(buf)
|
||||
uploaded_bytes += len(buf)
|
||||
if DEBUG:
|
||||
print('\ruploading %.2f%%' %
|
||||
(float(uploaded_bytes) / total_bytes * 100))
|
||||
|
||||
print("uploaded_bytes", uploaded_bytes)
|
||||
if uploaded_bytes == total_bytes:
|
||||
break
|
||||
datasock.close()
|
||||
|
||||
if DEBUG:
|
||||
print('close data handle')
|
||||
try:
|
||||
client.voidcmd('NOOP')
|
||||
except Exception as e:
|
||||
if DEBUG:
|
||||
print("Send NOOP command error:")
|
||||
print(e)
|
||||
else:
|
||||
if DEBUG:
|
||||
print('keep alive cmd success')
|
||||
client.voidresp()
|
||||
if DEBUG:
|
||||
print('No loop cmd')
|
||||
else:
|
||||
# 小于1G文件直接上传
|
||||
file_handler = open(local_file_name, "rb")
|
||||
client.storbinary('STOR %s' % object_name,
|
||||
file_handler, blocksize=block_size)
|
||||
file_handler.close()
|
||||
except Exception as e:
|
||||
print(str(e))
|
||||
|
||||
completed_file_size = None
|
||||
_max_loop = 10
|
||||
while _max_loop > 0:
|
||||
try:
|
||||
time.sleep(1)
|
||||
completed_file_size = client.size(object_name)
|
||||
break
|
||||
except Exception as e:
|
||||
_max_loop -= 1
|
||||
if DEBUG:
|
||||
print("size error:" + str(e))
|
||||
|
||||
# 上传完成
|
||||
if completed_file_size == total_bytes:
|
||||
if DEBUG:
|
||||
upload_completed = time.time()
|
||||
upload_diff = upload_completed - upload_start
|
||||
print("文件上传成功, 耗时: {}s。".format(upload_diff))
|
||||
if os.path.exists(pg_file):
|
||||
os.remove(pg_file)
|
||||
return True
|
||||
else:
|
||||
if os.path.exists(pg_file):
|
||||
os.remove(pg_file)
|
||||
print("文件上传后大小不一致!")
|
||||
|
||||
print("completed_file_size:" + str(completed_file_size))
|
||||
print("total_bytes:", total_bytes)
|
||||
print("object_md5_name:", object_md5_name)
|
||||
print("pg_file:", pg_file)
|
||||
print("filename:", filename)
|
||||
print("dirname:", dirname)
|
||||
print("object_name:", object_name)
|
||||
return False
|
||||
|
||||
def createDirP(self, dir_name):
|
||||
"""创建远程目录
|
||||
|
||||
:param dir_name: 目录名称
|
||||
:return:
|
||||
"""
|
||||
try:
|
||||
dirnames = dir_name.split('/')
|
||||
ftp = self.authorize()
|
||||
# ftp.cwd(get.path);
|
||||
for dirname in dirnames:
|
||||
if not dirname or not dirname.strip():
|
||||
continue
|
||||
try:
|
||||
flist = ftp.nlst()
|
||||
if not dirname in flist:
|
||||
ftp.mkd(dirname)
|
||||
except:
|
||||
# print("mlsd mode.")
|
||||
try:
|
||||
flist = list(ftp.mlsd())[1:]
|
||||
for f in flist:
|
||||
if dirname == f[0]:
|
||||
break
|
||||
else:
|
||||
ftp.mkd(dirname)
|
||||
except:
|
||||
return False
|
||||
ftp.cwd(dirname)
|
||||
return True
|
||||
except:
|
||||
return False
|
||||
|
||||
def createDir(self, path, name):
|
||||
ftp = self.authorize()
|
||||
path = self.getPath(path)
|
||||
ftp.cwd(path)
|
||||
try:
|
||||
ftp.mkd(name)
|
||||
ftp.close()
|
||||
return True
|
||||
except Exception as e:
|
||||
print(str(e))
|
||||
ftp.close()
|
||||
return False
|
||||
|
||||
def deleteDir(self, path, dir_name):
|
||||
try:
|
||||
ftp = self.authorize()
|
||||
ftp.rmd(dir_name)
|
||||
return True
|
||||
except ftplib.error_perm as e:
|
||||
print("deleteDir:" + str(e) + ":" + dir_name)
|
||||
except Exception as e:
|
||||
print(e)
|
||||
return False
|
||||
|
||||
def deleteFile(self, filename):
|
||||
try:
|
||||
ftp = self.authorize()
|
||||
ftp.delete(filename)
|
||||
return True
|
||||
except Exception as e:
|
||||
print(str(e))
|
||||
return False
|
||||
|
||||
def getList(self, path="/"):
|
||||
ftp = self.authorize()
|
||||
path = self.getPath(path)
|
||||
ftp.cwd(path)
|
||||
mlsd = False
|
||||
try:
|
||||
files = list(ftp.mlsd())
|
||||
mlsd = True
|
||||
except:
|
||||
try:
|
||||
files = ftp.nlst(path)
|
||||
mlsd = False
|
||||
except:
|
||||
raise RuntimeError("FTP服务器数据返回异常!")
|
||||
ftp.close()
|
||||
# print(files)
|
||||
f_list = []
|
||||
dirs = []
|
||||
data = []
|
||||
default_time = '1971/01/01 01:01:01'
|
||||
for dt in files:
|
||||
# print(dt)
|
||||
if mlsd:
|
||||
dt_name = dt[0]
|
||||
dt_info = dt[1]
|
||||
else:
|
||||
if dt.find("/") >= 0:
|
||||
dt = dt.split("/")[-1]
|
||||
tmp = {}
|
||||
tmp['name'] = dt_name
|
||||
if dt_name == '.' or dt_name == '..':
|
||||
continue
|
||||
|
||||
tmp['time'] = dt_info['modify']
|
||||
try:
|
||||
tmp['size'] = dt_info['size']
|
||||
tmp['type'] = "File"
|
||||
tmp['download'] = self.generateDownloadUrl(path + dt_name)
|
||||
f_list.append(tmp)
|
||||
except:
|
||||
tmp['size'] = dt_info['sizd']
|
||||
tmp['type'] = None
|
||||
tmp['download'] = ''
|
||||
dirs.append(tmp)
|
||||
data = dirs + f_list
|
||||
|
||||
mlist = {}
|
||||
mlist['path'] = path
|
||||
mlist['list'] = data
|
||||
return mlist
|
||||
|
Before Width: | Height: | Size: 1.5 KiB |
@@ -1,101 +0,0 @@
|
||||
<style>
|
||||
.upyunCon {
|
||||
height: 428px;
|
||||
}
|
||||
|
||||
.up-place {
|
||||
height: 62px;
|
||||
border-bottom: 1px solid #ddd;
|
||||
}
|
||||
|
||||
.up-place .btn {
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
.up-place .place-input {
|
||||
background-color: #f3f3f3;
|
||||
border: 1px solid #ccc;
|
||||
height: 30px;
|
||||
line-height: 28px;
|
||||
overflow: hidden;
|
||||
margin: 1px 0 0 -1px;
|
||||
width: 340px;
|
||||
}
|
||||
|
||||
.place-input ul {
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
width: auto;
|
||||
}
|
||||
|
||||
.place-input ul li {
|
||||
background: url("/static/img/ico/ico-ltr.png") no-repeat right center;
|
||||
float: left;
|
||||
padding-left: 10px;
|
||||
padding-right: 18px;
|
||||
}
|
||||
|
||||
.place-input ul li a {
|
||||
height: 28px;
|
||||
cursor: pointer;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.upyunlist {
|
||||
height: 516px;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.up-bottom {
|
||||
background-color: #fafafa;
|
||||
border-top: 1px solid #eee;
|
||||
bottom: 0;
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.up-use {
|
||||
line-height: 50px
|
||||
}
|
||||
|
||||
.list-list .cursor span {
|
||||
line-height: 30px;
|
||||
}
|
||||
|
||||
.btn-title {
|
||||
margin-top: 1px
|
||||
}
|
||||
|
||||
.tip {
|
||||
font-size: 10px;
|
||||
font-style: oblique;
|
||||
color: green;
|
||||
}
|
||||
</style>
|
||||
<div class="upyunCon">
|
||||
<div class="up-place pd15">
|
||||
<button id="backBtn" class="btn btn-default btn-sm glyphicon glyphicon-arrow-left pull-left" title="后退"></button>
|
||||
<input id="myPath" style="display:none;" type="text" value="">
|
||||
<div class="place-input pull-left">
|
||||
<div style="width:1400px;height:28px"><ul></ul></div>
|
||||
</div>
|
||||
<button class="refreshBtn btn btn-default btn-sm glyphicon glyphicon-refresh pull-left mr20" title="刷新" style="margin-left:-1px;"></button>
|
||||
<button class="btn btn-default btn-sm pull-right btn-title" onclick="upyunApi()">帐户设置</button>
|
||||
<button class="btn btn-default btn-sm pull-right mr20 btn-title" onclick="createDir()">新建文件夹</button>
|
||||
</div>
|
||||
|
||||
<div class="upyunlist pd15">
|
||||
<div class="divtable" style="margin-bottom:15px">
|
||||
<table class="table table-hover">
|
||||
<thead><tr><th>名称</th><th>大小</th><th>更新时间</th><th class="text-right">操作</th></tr></thead>
|
||||
<tbody class="list-list"></tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script type="text/javascript">
|
||||
$.getScript( "/plugins/file?name=backup_ftp&f=js/backup_ftp.js", function(){
|
||||
osList('/');
|
||||
});
|
||||
</script>
|
||||
@@ -1,257 +0,0 @@
|
||||
# coding:utf-8
|
||||
|
||||
import sys
|
||||
import io
|
||||
import os
|
||||
import time
|
||||
import re
|
||||
import json
|
||||
|
||||
# print(sys.platform)
|
||||
if sys.platform != "darwin":
|
||||
os.chdir("/www/server/mdserver-web")
|
||||
|
||||
|
||||
sys.path.append(os.getcwd() + "/class/core")
|
||||
import mw
|
||||
|
||||
_ver = sys.version_info
|
||||
is_py2 = (_ver[0] == 2)
|
||||
is_py3 = (_ver[0] == 3)
|
||||
|
||||
DEBUG = False
|
||||
|
||||
if is_py2:
|
||||
reload(sys)
|
||||
sys.setdefaultencoding('utf-8')
|
||||
|
||||
app_debug = False
|
||||
if mw.isAppleSystem():
|
||||
app_debug = True
|
||||
|
||||
|
||||
def getPluginName():
|
||||
return 'backup_ftp'
|
||||
|
||||
|
||||
def getPluginDir():
|
||||
return mw.getPluginDir() + '/' + getPluginName()
|
||||
|
||||
|
||||
sys.path.append(getPluginDir() + "/class")
|
||||
from ftp_client import FtpPSClient
|
||||
|
||||
|
||||
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('}')
|
||||
t = t.split(':')
|
||||
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 status():
|
||||
return 'start'
|
||||
|
||||
|
||||
def getConf():
|
||||
cfg = getServerDir() + "/cfg.json"
|
||||
if not os.path.exists(cfg):
|
||||
return mw.returnJson(False, "未配置", [])
|
||||
data = mw.readFile(cfg)
|
||||
data = json.loads(data)
|
||||
return mw.returnJson(True, "OK", data)
|
||||
|
||||
|
||||
def setConf():
|
||||
args = getArgs()
|
||||
data = checkArgs(args, ['use_sftp', 'ftp_user',
|
||||
'ftp_pass', 'ftp_host', 'backup_path'])
|
||||
if not data[0]:
|
||||
return data[1]
|
||||
|
||||
cfg = getServerDir() + "/cfg.json"
|
||||
|
||||
values = ['ftp_user',
|
||||
'ftp_pass',
|
||||
'ftp_host']
|
||||
for v in values:
|
||||
if args[v] == '':
|
||||
return mw.returnJson(False, '必填资料不能为空,请核实!', [])
|
||||
|
||||
if args['backup_path'] == '':
|
||||
args['backup_path'] = "/backup"
|
||||
|
||||
try:
|
||||
ftp = FtpPSClient(load_config=False)
|
||||
ftp.injection_config(args)
|
||||
data = ftp.getList("/")
|
||||
if data:
|
||||
mw.writeFile(cfg, mw.getJson(args))
|
||||
return mw.returnJson(True, '设置成功', [])
|
||||
except Exception as e:
|
||||
return mw.returnJson(False, "FTP校验失败,请核实!\n" + str(e), [])
|
||||
|
||||
|
||||
def getList():
|
||||
cfg = getServerDir() + "/cfg.json"
|
||||
if not os.path.exists(cfg):
|
||||
return mw.returnJson(False, "未配置FTP,请点击`账户设置`", [])
|
||||
|
||||
args = getArgs()
|
||||
data = checkArgs(args, ['path'])
|
||||
if not data[0]:
|
||||
return data[1]
|
||||
|
||||
try:
|
||||
ftp = FtpPSClient()
|
||||
flist = ftp.getList(args['path'])
|
||||
return mw.returnJson(True, "ok", flist)
|
||||
except Exception as e:
|
||||
return mw.returnJson(False, str(e), [])
|
||||
|
||||
|
||||
def createDir():
|
||||
cfg = getServerDir() + "/cfg.json"
|
||||
if not os.path.exists(cfg):
|
||||
return mw.returnJson(False, "未配置FTP,请点击`账户设置`", [])
|
||||
|
||||
args = getArgs()
|
||||
data = checkArgs(args, ['path', 'name'])
|
||||
if not data[0]:
|
||||
return data[1]
|
||||
|
||||
ftp = FtpPSClient()
|
||||
isok = ftp.createDir(args['path'], args['name'])
|
||||
if isok:
|
||||
return mw.returnJson(True, "创建成功")
|
||||
return mw.returnJson(False, "创建失败")
|
||||
|
||||
|
||||
def deleteDir():
|
||||
args = getArgs()
|
||||
data = checkArgs(args, ['dir_name', 'path'])
|
||||
if not data[0]:
|
||||
return data[1]
|
||||
|
||||
ftp = FtpPSClient()
|
||||
isok = ftp.deleteDir(args['path'], args['dir_name'])
|
||||
if isok:
|
||||
return mw.returnJson(True, "删除成功")
|
||||
return mw.returnJson(False, "删除失败")
|
||||
|
||||
|
||||
def deleteFile():
|
||||
args = getArgs()
|
||||
data = checkArgs(args, ['path', 'filename'])
|
||||
if not data[0]:
|
||||
return data[1]
|
||||
|
||||
ftp = FtpPSClient()
|
||||
isok = ftp.deleteFile(args['path'] + "/" + args['filename'])
|
||||
if isok:
|
||||
return mw.returnJson(True, "删除成功")
|
||||
return mw.returnJson(False, "删除失败")
|
||||
|
||||
|
||||
def backupAllFunc(stype):
|
||||
os.chdir(mw.getRunDir())
|
||||
|
||||
name = sys.argv[2]
|
||||
num = sys.argv[3]
|
||||
|
||||
args = stype + " " + name + " " + num
|
||||
|
||||
cmd = 'python3 ' + mw.getRunDir() + '/scripts/backup.py ' + args
|
||||
os.system(cmd)
|
||||
|
||||
# 开始执行上传信息
|
||||
|
||||
prefix_dict = {
|
||||
"site": "web",
|
||||
"database": "db",
|
||||
"path": "path",
|
||||
}
|
||||
|
||||
find_path = mw.getBackupDir() + '/' + stype + '/' + \
|
||||
prefix_dict[stype] + '_' + name
|
||||
|
||||
find_new_file = "ls " + find_path + \
|
||||
"_* | grep '.gz' | cut -d \ -f 1 | awk 'END {print}'"
|
||||
|
||||
filename = mw.execShell(find_new_file)[0].strip()
|
||||
if filename == "":
|
||||
print("not find upload file!")
|
||||
return False
|
||||
|
||||
ftp = FtpPSClient()
|
||||
ftp.uploadFile(filename, stype)
|
||||
|
||||
return True
|
||||
|
||||
|
||||
def backupSite():
|
||||
# 备份站点
|
||||
pass
|
||||
|
||||
|
||||
def in_array(name, arr=[]):
|
||||
for x in arr:
|
||||
if name == x:
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def installPreInspection():
|
||||
return 'ok'
|
||||
|
||||
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 == 'conf':
|
||||
print(getConf())
|
||||
elif func == 'set_config':
|
||||
print(setConf())
|
||||
elif func == "get_list":
|
||||
print(getList())
|
||||
elif func == "create_dir":
|
||||
print(createDir())
|
||||
elif func == "delete_dir":
|
||||
print(deleteDir())
|
||||
elif func == 'delete_file':
|
||||
print(deleteFile())
|
||||
elif in_array(func, ['site', 'database', 'path']):
|
||||
print(backupAllFunc(func))
|
||||
else:
|
||||
print('error')
|
||||
@@ -1,17 +0,0 @@
|
||||
{
|
||||
"title":"FTP存储空间",
|
||||
"hook":["backup"],
|
||||
"tip":"soft",
|
||||
"name":"backup_ftp",
|
||||
"type":"运行环境",
|
||||
"ps":"将网站或数据库打包备份到FTP存储空间",
|
||||
"versions":["1.0"],
|
||||
"install_pre_inspection":false,
|
||||
"shell":"install.sh",
|
||||
"checks":"server/backup_ftp",
|
||||
"path": "server/backup_ftp",
|
||||
"author":"midoks",
|
||||
"home":"",
|
||||
"date":"2022-10-23",
|
||||
"pid": "4"
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
#!/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
|
||||
sys_os=`uname`
|
||||
|
||||
|
||||
Install_App()
|
||||
{
|
||||
mkdir -p ${serverPath}/backup_ftp
|
||||
echo "${1}" > ${serverPath}/backup_ftp/version.pl
|
||||
echo '安装完成' > $install_tmp
|
||||
|
||||
}
|
||||
|
||||
Uninstall_App()
|
||||
{
|
||||
rm -rf ${serverPath}/backup_ftp
|
||||
}
|
||||
|
||||
action=$1
|
||||
if [ "${1}" == 'install' ];then
|
||||
Install_App $2
|
||||
else
|
||||
Uninstall_App $2
|
||||
fi
|
||||
@@ -1,258 +0,0 @@
|
||||
|
||||
|
||||
function bkfPost(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:'backup_ftp', 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 getFtpLocalTime(data){
|
||||
var str = data.slice(0,4)+"/"+data.slice(4,6)+"/"+data.slice(6,8)
|
||||
+ " " + data.slice(8,10)+":"+data.slice(10,12)+":"+data.slice(12,14);
|
||||
return str;
|
||||
}
|
||||
|
||||
// 自定义部分
|
||||
var i = null;
|
||||
//设置API
|
||||
function upyunApi(){
|
||||
|
||||
bkfPost('conf', {}, function(rdata){
|
||||
var rdata = $.parseJSON(rdata.data);
|
||||
var token = rdata.data;
|
||||
var check_status = token.use_sftp;
|
||||
var sftp_checked = check_status === "true" ? " checked=\"checked\"" : "";
|
||||
|
||||
if (typeof(token.ftp_host) == 'undefined'){
|
||||
token.ftp_host = '';
|
||||
}
|
||||
|
||||
if (typeof(token.ftp_user) == 'undefined'){
|
||||
token.ftp_user = '';
|
||||
}
|
||||
|
||||
if (typeof(token.ftp_pass) == 'undefined'){
|
||||
token.ftp_pass = '';
|
||||
}
|
||||
|
||||
if (typeof(token.backup_path) == 'undefined'){
|
||||
token.backup_path = '';
|
||||
}
|
||||
|
||||
var apicon = '<div class="bingfa mtb15" style="padding-bottom:0px;">\
|
||||
<p>\
|
||||
<span class="span_tit">使用SFTP:</span>\ <input style="width: 20px; vertical-align:middle;" type="checkbox" name="use_sftp"'+sftp_checked+'> 是否使用SFTP进行数据传输 \
|
||||
</p>\
|
||||
<p>\
|
||||
<span class="span_tit">Host:</span>\
|
||||
<input placeholder="请输入主机地址" style="width: 200px;" type="text" name="upyun_service" value="'+token.ftp_host+'"> *服务器地址,FTP默认端口21, SFTP默认端口22\
|
||||
</p>\
|
||||
<p>\
|
||||
<span class="span_tit">用户名:</span>\
|
||||
<input style="width: 200px;" type="text" name="ftp_username" value="'+token.ftp_user+'"> *指定用户名\
|
||||
</p>\
|
||||
<p>\
|
||||
<span class="span_tit">密码:</span>\
|
||||
<input style="width: 200px;" type="password" name="ftp_password" value="'+token.ftp_pass+'"> *登录密码\
|
||||
</p>\
|
||||
<p>\
|
||||
<span class="span_tit">存储位置:</span>\
|
||||
<input placeholder="请输入存储位置" style="width: 200px;" type="text" name="backup_path" value="'+token.backup_path+'"> *相对于根目录的路径,默认是/backup\
|
||||
</p>\
|
||||
</div>';
|
||||
layer.open({
|
||||
type: 1,
|
||||
area: "600px",
|
||||
title: "FTP/SFTP帐户设置",
|
||||
closeBtn: 1,
|
||||
shift: 5,
|
||||
shadeClose: false,
|
||||
btn: ['确定','取消'],
|
||||
content:apicon,
|
||||
yes:function(index,layero){
|
||||
var data = {
|
||||
use_sftp:$("input[name='use_sftp']").prop('checked'),
|
||||
ftp_user:$("input[name='ftp_username']").val(),
|
||||
ftp_pass:$("input[name='ftp_password']").val(),
|
||||
ftp_host:$("input[name='upyun_service']").val(),
|
||||
backup_path:$("input[name='backup_path']").val()
|
||||
}
|
||||
bkfPost('set_config', data, function(rdata){
|
||||
var rdata = $.parseJSON(rdata.data);
|
||||
if (rdata.status){
|
||||
showMsg(rdata.msg,function(){
|
||||
layer.close(index);
|
||||
osList("/");
|
||||
},{icon:1},2000);
|
||||
} else{
|
||||
layer.msg(rdata.msg,{icon:2});
|
||||
}
|
||||
})
|
||||
},
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function createDir(){
|
||||
layer.open({
|
||||
type: 1,
|
||||
area: "400px",
|
||||
title: "创建目录",
|
||||
closeBtn: 1,
|
||||
shift: 5,
|
||||
shadeClose: false,
|
||||
btn: ['确定','取消'],
|
||||
content:'<div class="bingfa bt-form c6" style="padding-bottom: 10px;">\
|
||||
<p>\
|
||||
<span class="span_tit">目录名称:</span>\
|
||||
<input style="width: 200px;" type="text" name="newPath" value="">\
|
||||
</p>\
|
||||
</div>',
|
||||
success:function(){
|
||||
$("input[name='newPath']").focus().keyup(function(e){
|
||||
if(e.keyCode == 13) $(".layui-layer-btn0").click();
|
||||
});
|
||||
},
|
||||
yes:function(index,layero){
|
||||
var name = $("input[name='newPath']").val();
|
||||
if(name == ''){
|
||||
layer.msg('目录名称不能为空!',{icon:2});
|
||||
return;
|
||||
}
|
||||
var path = $("#myPath").val();
|
||||
var dirname = name;
|
||||
// var loadT = layer.msg('正在创建目录['+dirname+']...',{icon:16,time:0,shade: [0.3, '#000']});
|
||||
bkfPost('create_dir', {path:path,name:dirname}, function(data){
|
||||
var rdata = $.parseJSON(data.data);
|
||||
if(rdata.status) {
|
||||
showMsg(rdata.msg, function(){
|
||||
layer.close(index);
|
||||
osList(path);
|
||||
} ,{icon:1}, 2000);
|
||||
} else{
|
||||
layer.msg(rdata.msg,{icon:2});
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
//删除文件
|
||||
function deleteFile(name, is_dir){
|
||||
if (is_dir === false){
|
||||
safeMessage('删除文件','删除后将无法恢复,真的要删除['+name+']吗?',function(){
|
||||
var path = $("#myPath").val();
|
||||
var filename = name;
|
||||
bkfPost('delete_file', {filename:filename,path:path}, function(rdata){
|
||||
var rdata = $.parseJSON(rdata.data);
|
||||
showMsg(rdata.msg,function(){
|
||||
osList(path);
|
||||
},{icon:rdata.status?1:2},2000);
|
||||
});
|
||||
});
|
||||
} else {
|
||||
safeMessage('删除文件夹','删除后将无法恢复,真的要删除['+name+']吗?',function(){
|
||||
var path = $("#myPath").val();
|
||||
bkfPost('delete_dir', {dir_name:name,path:path}, function(rdata){
|
||||
var rdata = $.parseJSON(rdata.data);
|
||||
showMsg(rdata.msg,function(){
|
||||
osList(path);
|
||||
},{icon:rdata.status?1:2},2000);
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function osList(path){
|
||||
bkfPost('get_list', {path:path}, function(rdata){
|
||||
|
||||
var rdata = $.parseJSON(rdata.data);
|
||||
if(rdata.status === false){
|
||||
showMsg(rdata.msg,function(){
|
||||
upyunApi();
|
||||
},{icon:2},2000);
|
||||
return;
|
||||
}
|
||||
|
||||
var mlist = rdata.data;
|
||||
// console.log(mlist);
|
||||
var listBody = ''
|
||||
var listFiles = ''
|
||||
for(var i=0;i<mlist.list.length;i++){
|
||||
if(mlist.list[i].type == null){
|
||||
listBody += '<tr><td class="cursor" onclick="osList(\''+(path+'/'+mlist.list[i].name).replace('//','/')+'\')"><span class="ico ico-folder"></span>\<span>'+mlist.list[i].name+'</span></td>\
|
||||
<td>-</td>\
|
||||
<td>-</td>\
|
||||
<td class="text-right"><a class="btlink" onclick="deleteFile(\''+mlist.list[i].name+'\', true)">删除</a></td></tr>'
|
||||
}else{
|
||||
listFiles += '<tr><td class="cursor"><span class="ico ico-file"></span>\<span>'+mlist.list[i].name+'</span></td>\
|
||||
<td>'+toSize(mlist.list[i].size)+'</td>\
|
||||
<td>'+getFtpLocalTime(mlist.list[i].time)+'</td>\
|
||||
<td class="text-right"><a target="_blank" href="'+mlist.list[i].download+'" class="btlink">下载</a> | <a class="btlink" onclick="deleteFile(\''+mlist.list[i].name+'\', false)">删除</a></td></tr>'
|
||||
}
|
||||
}
|
||||
listBody += listFiles;
|
||||
|
||||
var pathLi='';
|
||||
var tmp = path.split('/')
|
||||
var pathname = '';
|
||||
var n = 0;
|
||||
for(var i=0;i<tmp.length;i++){
|
||||
if(n > 0 && tmp[i] == '') continue;
|
||||
var dirname = tmp[i];
|
||||
if(dirname == '') {
|
||||
dirname = '根目录';
|
||||
n++;
|
||||
}
|
||||
pathname += '/' + tmp[i];
|
||||
pathname = pathname.replace('//','/');
|
||||
pathLi += '<li><a title="'+pathname+'" onclick="osList(\''+pathname+'\')">'+dirname+'</a></li>';
|
||||
}
|
||||
var um = 1;
|
||||
if(tmp[tmp.length-1] == '') um = 2;
|
||||
var backPath = tmp.slice(0,tmp.length-um).join('/') || '/';
|
||||
$('#myPath').val(path);
|
||||
$(".upyunCon .place-input ul").html(pathLi);
|
||||
$(".upyunlist .list-list").html(listBody);
|
||||
|
||||
upPathLeft();
|
||||
|
||||
$('#backBtn').unbind().click(function() {
|
||||
osList(backPath);
|
||||
});
|
||||
|
||||
$('.upyunCon .refreshBtn').unbind().click(function(){
|
||||
osList(path);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
//计算当前目录偏移
|
||||
function upPathLeft(){
|
||||
var UlWidth = $(".place-input ul").width();
|
||||
var SpanPathWidth = $(".place-input").width() - 20;
|
||||
var Ml = UlWidth - SpanPathWidth;
|
||||
if(UlWidth > SpanPathWidth ){
|
||||
$(".place-input ul").css("left",-Ml)
|
||||
}
|
||||
else{
|
||||
$(".place-input ul").css("left",0)
|
||||
}
|
||||
}
|
||||
// $('.layui-layer-page').css('height','670px');
|
||||
|
Before Width: | Height: | Size: 254 B |
@@ -1,28 +0,0 @@
|
||||
<div class="bt-form">
|
||||
<div class="bt-w-main">
|
||||
<div class="bt-w-menu">
|
||||
<p class="bgw" onclick="pluginService('clean');">服务</p>
|
||||
<p onclick="pluginConfig('clean');">配置修改</p>
|
||||
<p onclick="pluginLogs('clean',null,'run_log');">运行日志</p>
|
||||
<p onclick="pRead()">说明</p>
|
||||
</div>
|
||||
<div class="bt-w-con pd15">
|
||||
<div class="soft-man-con"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script type="text/javascript">
|
||||
pluginService('clean');
|
||||
|
||||
|
||||
function pRead(){
|
||||
var readme = '<ul class="help-info-text c7">';
|
||||
readme += '<li>启动后,加入到计划中[2:30]执行。</li>';
|
||||
readme += '<li>【*】代表要删除。</li>';
|
||||
readme += '<li>添加/删除清除日志,可在`配置修改`中添加/删除。</li>';
|
||||
readme += '<li>如果是目录,会寻找【.log】后缀的文件清空。</li>';
|
||||
readme += '</ul>';
|
||||
|
||||
$('.soft-man-con').html(readme);
|
||||
}
|
||||
</script>
|
||||
@@ -1,227 +0,0 @@
|
||||
# coding:utf-8
|
||||
|
||||
import sys
|
||||
import io
|
||||
import os
|
||||
import time
|
||||
|
||||
sys.path.append(os.getcwd() + "/class/core")
|
||||
import mw
|
||||
|
||||
app_debug = False
|
||||
if mw.isAppleSystem():
|
||||
app_debug = True
|
||||
|
||||
|
||||
def getPluginName():
|
||||
return 'clean'
|
||||
|
||||
|
||||
def getPluginDir():
|
||||
return mw.getPluginDir() + '/' + getPluginName()
|
||||
|
||||
|
||||
def getServerDir():
|
||||
return mw.getServerDir() + '/' + getPluginName()
|
||||
|
||||
|
||||
def getInitDFile():
|
||||
if app_debug:
|
||||
return '/tmp/' + getPluginName()
|
||||
return '/etc/init.d/' + getPluginName()
|
||||
|
||||
|
||||
def getConf():
|
||||
path = getServerDir() + "/clean.conf"
|
||||
return path
|
||||
|
||||
|
||||
def getArgs():
|
||||
args = sys.argv[2:]
|
||||
tmp = {}
|
||||
args_len = len(args)
|
||||
|
||||
if args_len == 1:
|
||||
t = args[0].strip('{').strip('}')
|
||||
t = t.split(':')
|
||||
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 getLockFile():
|
||||
return getServerDir() + "/installed_lock.pl"
|
||||
|
||||
|
||||
def runLog():
|
||||
return getServerDir() + "/clean.log"
|
||||
|
||||
|
||||
def status():
|
||||
initConf()
|
||||
if os.path.exists(getLockFile()):
|
||||
return "start"
|
||||
return 'stop'
|
||||
|
||||
|
||||
def initConf():
|
||||
conf = getConf()
|
||||
if not os.path.exists(conf):
|
||||
content = ""
|
||||
|
||||
clog = [
|
||||
"/var/spool/clientmqueue/*",
|
||||
"/var/log/cron-*",
|
||||
"/var/log/maillog-*",
|
||||
"/var/log/secure-*",
|
||||
"/var/log/spooler-*",
|
||||
"/var/log/yum.log-*",
|
||||
"/var/log/messages-*",
|
||||
"/var/log/btmp-*",
|
||||
"/var/log/audit/audit.log.*",
|
||||
"/var/log/rhsm/rhsm.log-*",
|
||||
"/var/log/rhsm/rhsmcertd.log-*",
|
||||
"/tmp/yum_save_*",
|
||||
"/tmp/tmp.*",
|
||||
]
|
||||
for i in clog:
|
||||
content += i + "\n"
|
||||
|
||||
# 常用日志
|
||||
clogcom = [
|
||||
"/var/log/messages",
|
||||
"/var/log/btmp",
|
||||
"/var/log/wtmp",
|
||||
"/var/log/secure",
|
||||
"/var/log/lastlog",
|
||||
"/var/log/cron",
|
||||
"/www/server/cron"
|
||||
]
|
||||
for i in clogcom:
|
||||
if os.path.exists(i):
|
||||
content += i + "\n"
|
||||
|
||||
# 清理日志
|
||||
rootDir = "/var/log"
|
||||
|
||||
l = os.listdir(rootDir)
|
||||
for x in range(len(l)):
|
||||
abspath = rootDir + "/" + l[x]
|
||||
content += abspath + "\n"
|
||||
mw.writeFile(conf, content)
|
||||
|
||||
|
||||
def start():
|
||||
initConf()
|
||||
|
||||
lock_file = getLockFile()
|
||||
if not os.path.exists(lock_file):
|
||||
mw.writeFile(lock_file, "")
|
||||
|
||||
import tool_task
|
||||
tool_task.createBgTask()
|
||||
return 'ok'
|
||||
|
||||
return 'fail'
|
||||
|
||||
|
||||
def stop():
|
||||
initConf()
|
||||
lock_file = getLockFile()
|
||||
if os.path.exists(lock_file):
|
||||
os.remove(lock_file)
|
||||
import tool_task
|
||||
tool_task.removeBgTask()
|
||||
return 'ok'
|
||||
|
||||
return 'fail'
|
||||
|
||||
|
||||
def reload():
|
||||
return 'ok'
|
||||
|
||||
|
||||
def get_filePath_fileName_fileExt(filename):
|
||||
(filepath, tempfilename) = os.path.split(filename)
|
||||
(shotname, extension) = os.path.splitext(tempfilename)
|
||||
return filepath, shotname, extension
|
||||
|
||||
|
||||
def cleanFileLog(path):
|
||||
filepath, shotname, extension = get_filePath_fileName_fileExt(path)
|
||||
if extension == ".log":
|
||||
cmd = "echo \"\" > " + path
|
||||
tmp = mw.execShell(cmd)
|
||||
if tmp[1] != "":
|
||||
cmd += " | error:" + tmp[1].strip()
|
||||
print(cmd)
|
||||
|
||||
|
||||
def cleanSelfFileLog(path):
|
||||
filepath, shotname, extension = get_filePath_fileName_fileExt(path)
|
||||
if extension == ".log":
|
||||
cmd = "echo \"\" > " + path
|
||||
tmp = mw.execShell(cmd)
|
||||
if tmp[1] != "":
|
||||
cmd += " | error:" + tmp[1].strip()
|
||||
print(cmd)
|
||||
|
||||
|
||||
def cleanDirLog(path):
|
||||
l = os.listdir(path)
|
||||
for x in range(len(l)):
|
||||
abspath = path + "/" + l[x]
|
||||
if os.path.isfile(abspath):
|
||||
cleanFileLog(abspath)
|
||||
if os.path.isdir(abspath):
|
||||
cleanDirLog(abspath)
|
||||
|
||||
|
||||
def cleanLog():
|
||||
conf = getConf()
|
||||
clist = mw.readFile(conf).strip()
|
||||
clist = clist.split("\n")
|
||||
|
||||
for x in clist:
|
||||
abspath = x.strip()
|
||||
|
||||
if abspath.find('*') > 1:
|
||||
cmd = 'rm -rf ' + abspath
|
||||
print(cmd)
|
||||
data = mw.execShell(cmd)
|
||||
# print(data)
|
||||
continue
|
||||
|
||||
if os.path.exists(abspath):
|
||||
if os.path.isfile(abspath):
|
||||
cleanSelfFileLog(abspath)
|
||||
continue
|
||||
|
||||
if os.path.isdir(abspath):
|
||||
cleanDirLog(abspath)
|
||||
continue
|
||||
|
||||
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 == 'conf':
|
||||
print(getConf())
|
||||
elif func == 'run_log':
|
||||
print(runLog())
|
||||
elif func == 'clean':
|
||||
cleanLog()
|
||||
else:
|
||||
print('error')
|
||||
@@ -1,16 +0,0 @@
|
||||
{
|
||||
"title":"日志清理",
|
||||
"tip":"soft",
|
||||
"name":"clean",
|
||||
"type":"运行环境",
|
||||
"ps":"日志清理",
|
||||
"versions":"1.0",
|
||||
"updates":"1.0",
|
||||
"shell":"install.sh",
|
||||
"checks":"server/clean",
|
||||
"path": "server/clean",
|
||||
"author":"clean",
|
||||
"home":"https://github.com/midoks",
|
||||
"date":"2021-11-23",
|
||||
"pid": "5"
|
||||
}
|
||||
@@ -1,43 +0,0 @@
|
||||
#!/bin/bash
|
||||
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
|
||||
export PATH
|
||||
|
||||
# cd /Users/midoks/Desktop/mwdev/server/mdserver-web/plugins/mongodb && /bin/bash install.sh install 5.0.4
|
||||
# cd /www/server/mdserver-web/plugins/mongodb && /bin/bash install.sh install 5.0.4
|
||||
|
||||
curPath=`pwd`
|
||||
rootPath=$(dirname "$curPath")
|
||||
rootPath=$(dirname "$rootPath")
|
||||
serverPath=$(dirname "$rootPath")
|
||||
|
||||
install_tmp=${rootPath}/tmp/mw_install.pl
|
||||
VERSION=$2
|
||||
|
||||
if [ -f ${rootPath}/bin/activate ];then
|
||||
source ${rootPath}/bin/activate
|
||||
fi
|
||||
|
||||
|
||||
Install_app()
|
||||
{
|
||||
echo '正在安装脚本文件...' > $install_tmp
|
||||
mkdir -p $serverPath/clean
|
||||
|
||||
cd ${rootPath} && python3 ${rootPath}/plugins/clean/index.py start
|
||||
echo "${VERSION}" > $serverPath/clean/version.pl
|
||||
echo '安装完成' > $install_tmp
|
||||
}
|
||||
|
||||
Uninstall_app()
|
||||
{
|
||||
cd ${rootPath} && python3 ${rootPath}/plugins/clean/index.py stop
|
||||
rm -rf $serverPath/clean
|
||||
echo "Uninstall_clean" > $install_tmp
|
||||
}
|
||||
|
||||
action=$1
|
||||
if [ "${1}" == 'install' ];then
|
||||
Install_app
|
||||
else
|
||||
Uninstall_app
|
||||
fi
|
||||
@@ -1,156 +0,0 @@
|
||||
# coding:utf-8
|
||||
|
||||
import sys
|
||||
import io
|
||||
import os
|
||||
import time
|
||||
import json
|
||||
|
||||
sys.path.append(os.getcwd() + "/class/core")
|
||||
import mw
|
||||
|
||||
|
||||
app_debug = False
|
||||
if mw.isAppleSystem():
|
||||
app_debug = True
|
||||
|
||||
|
||||
def getPluginName():
|
||||
return 'clean'
|
||||
|
||||
|
||||
def getPluginDir():
|
||||
return mw.getPluginDir() + '/' + getPluginName()
|
||||
|
||||
|
||||
def getServerDir():
|
||||
return mw.getServerDir() + '/' + getPluginName()
|
||||
|
||||
|
||||
def getTaskConf():
|
||||
conf = getServerDir() + "/task_config.json"
|
||||
return conf
|
||||
|
||||
|
||||
def getConfigData():
|
||||
try:
|
||||
return json.loads(mw.readFile(getTaskConf()))
|
||||
except:
|
||||
pass
|
||||
return []
|
||||
|
||||
|
||||
def getConfigTpl():
|
||||
tpl = {
|
||||
"name": "",
|
||||
"task_id": -1,
|
||||
}
|
||||
return tpl
|
||||
|
||||
|
||||
def createBgTask():
|
||||
removeBgTask()
|
||||
args = {
|
||||
"period": "day",
|
||||
"hour": "2",
|
||||
"minute": "30",
|
||||
}
|
||||
createBgTaskByName(getPluginName(), args)
|
||||
|
||||
|
||||
def createBgTaskByName(name, args):
|
||||
cfg = getConfigTpl()
|
||||
_name = "[勿删]日志清理[" + name + "]"
|
||||
res = mw.M("crontab").field("id, name").where("name=?", (_name,)).find()
|
||||
if res:
|
||||
return True
|
||||
|
||||
if "task_id" in cfg.keys() and cfg["task_id"] > 0:
|
||||
res = mw.M("crontab").field("id, name").where(
|
||||
"id=?", (cfg["task_id"],)).find()
|
||||
if res and res["id"] == cfg["task_id"]:
|
||||
print("计划任务已经存在!")
|
||||
return True
|
||||
import crontab_api
|
||||
api = crontab_api.crontab_api()
|
||||
|
||||
period = args['period']
|
||||
_hour = ''
|
||||
_minute = ''
|
||||
_where1 = ''
|
||||
_type_day = "day"
|
||||
if period == 'day':
|
||||
_type_day = 'day'
|
||||
_hour = args['hour']
|
||||
_minute = args['minute']
|
||||
elif period == 'minute-n':
|
||||
_type_day = 'minute-n'
|
||||
_where1 = args['minute-n']
|
||||
_minute = ''
|
||||
|
||||
mw_dir = mw.getRunDir()
|
||||
cmd = '''
|
||||
mw_dir=%s
|
||||
rname=%s
|
||||
plugin_path=%s
|
||||
script_path=%s
|
||||
logs_file=$plugin_path/${rname}.log
|
||||
''' % (mw_dir, name, getServerDir(), getPluginDir())
|
||||
cmd += 'echo "★【`date +"%Y-%m-%d %H:%M:%S"`】 STSRT★" >> $logs_file' + "\n"
|
||||
cmd += 'echo ">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>" >> $logs_file' + "\n"
|
||||
cmd += 'echo "python3 $script_path/index.py clean >> $logs_file 2>&1"' + "\n"
|
||||
cmd += 'cd $mw_dir && python3 $script_path/index.py clean >> $logs_file 2>&1' + "\n"
|
||||
cmd += 'echo "【`date +"%Y-%m-%d %H:%M:%S"`】 END★" >> $logs_file' + "\n"
|
||||
cmd += 'echo "<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<" >> $logs_file' + "\n"
|
||||
|
||||
params = {
|
||||
'name': _name,
|
||||
'type': _type_day,
|
||||
'week': "",
|
||||
'where1': _where1,
|
||||
'hour': _hour,
|
||||
'minute': _minute,
|
||||
'save': "",
|
||||
'backup_to': "",
|
||||
'stype': "toShell",
|
||||
'sname': '',
|
||||
'sbody': cmd,
|
||||
'urladdress': '',
|
||||
}
|
||||
|
||||
task_id = api.add(params)
|
||||
if task_id > 0:
|
||||
cfg["task_id"] = task_id
|
||||
cfg["name"] = name
|
||||
|
||||
_dd = getConfigData()
|
||||
_dd.append(cfg)
|
||||
mw.writeFile(getTaskConf(), json.dumps(_dd))
|
||||
|
||||
|
||||
def removeBgTask():
|
||||
cfg_list = getConfigData()
|
||||
for x in range(len(cfg_list)):
|
||||
cfg = cfg_list[x]
|
||||
if "task_id" in cfg.keys() and cfg["task_id"] > 0:
|
||||
res = mw.M("crontab").field("id, name").where(
|
||||
"id=?", (cfg["task_id"],)).find()
|
||||
if res and res["id"] == cfg["task_id"]:
|
||||
import crontab_api
|
||||
api = crontab_api.crontab_api()
|
||||
data = api.delete(cfg["task_id"])
|
||||
if data[0]:
|
||||
cfg["task_id"] = -1
|
||||
cfg_list[x] = cfg
|
||||
mw.writeFile(getTaskConf(), '[]')
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
if len(sys.argv) > 1:
|
||||
action = sys.argv[1]
|
||||
if action == "remove":
|
||||
removeBgTask()
|
||||
elif action == "add":
|
||||
createBgTask()
|
||||
@@ -1,45 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
echo `date`
|
||||
|
||||
GITADDR="{$GITROOTURL}/{$USERNAME}/{$PROJECT}.git"
|
||||
GIT_SDIR="{$CODE_DIR}"
|
||||
|
||||
GIT_USER_DIR="${GIT_SDIR}/{$USERNAME}"
|
||||
GIT_PROJECT_DIR="${GIT_USER_DIR}/{$PROJECT}"
|
||||
|
||||
|
||||
git config --global credential.helper store
|
||||
git config --global pull.rebase false
|
||||
|
||||
# echo $GIT_PROJECT_DIR
|
||||
if [ ! -d $GIT_PROJECT_DIR ];then
|
||||
mkdir -p $GIT_USER_DIR && cd $GIT_USER_DIR
|
||||
git clone $GITADDR
|
||||
fi
|
||||
|
||||
unset GIT_DIR
|
||||
|
||||
cd $GIT_PROJECT_DIR && git pull
|
||||
|
||||
# func 2
|
||||
# cd $GIT_PROJECT_DIR && env -i git pull origin master
|
||||
|
||||
|
||||
#更新的目的地址
|
||||
WEB_PATH={$WEB_ROOT}/{$USERNAME}/{$PROJECT}
|
||||
|
||||
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
|
||||
fi
|
||||
|
||||
sysName=`uname`
|
||||
if [ $sysName == 'Darwin' ]; then
|
||||
USER=$(who | sed -n "2,1p" |awk '{print $1}')
|
||||
chown -R $USER:staff $WEB_PATH
|
||||
else
|
||||
chown -R www:www $WEB_PATH
|
||||
fi
|
||||
@@ -1,38 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
echo `date`
|
||||
|
||||
GITADDR="{$GITROOTURL}/{$USERNAME}/{$PROJECT}"
|
||||
GIT_SDIR="{$CODE_DIR}"
|
||||
|
||||
GIT_USER_DIR="${GIT_SDIR}/{$USERNAME}"
|
||||
GIT_PROJECT_DIR="${GIT_USER_DIR}/{$PROJECT}"
|
||||
|
||||
# echo $GIT_PROJECT_DIR
|
||||
if [ ! -d $GIT_PROJECT_DIR ];then
|
||||
mkdir -p $GIT_USER_DIR && cd $GIT_USER_DIR
|
||||
git clone $GITADDR
|
||||
fi
|
||||
|
||||
unset GIT_DIR
|
||||
git config pull.rebase false
|
||||
|
||||
cd $GIT_PROJECT_DIR && git pull
|
||||
|
||||
# func 2
|
||||
# cd $GIT_PROJECT_DIR && env -i git pull origin master
|
||||
|
||||
|
||||
|
||||
WEB_PATH={$WEB_ROOT}/{$USERNAME}/{$PROJECT}
|
||||
mkdir -p $WEB_PATH
|
||||
|
||||
rsync -vauP --delete --exclude=".*" $GIT_PROJECT_DIR/ $WEB_PATH
|
||||
|
||||
sysName=`uname`
|
||||
if [ $sysName == 'Darwin' ]; then
|
||||
USER=$(who | sed -n "2,1p" |awk '{print $1}')
|
||||
chown -R $USER:staff $WEB_PATH
|
||||
else
|
||||
chown -R www:www $WEB_PATH
|
||||
fi
|
||||
@@ -1,3 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
sh -x {$PATH}/commit 2>{$PATH}/sh.log
|
||||
|
Before Width: | Height: | Size: 26 KiB |
@@ -1,24 +0,0 @@
|
||||
<div class="bt-form">
|
||||
<div class="bt-w-main">
|
||||
<div class="bt-w-menu">
|
||||
<p class="bgw" onclick="pluginService('gitea');">服务</p>
|
||||
<p onclick="pluginInitD('gitea');">自启动</p>
|
||||
<!-- <p onclick="pluginConfig('gitea',null, 'init_conf');">启动配置</p> -->
|
||||
<p onclick="gogsEdit();">手动编辑</p>
|
||||
<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="giteaRead();">使用说明</p>
|
||||
</div>
|
||||
<div class="bt-w-con pd15">
|
||||
<div class="soft-man-con"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script type="text/javascript">
|
||||
$.getScript( "/plugins/file?name=gitea&f=js/gitea.js",function(){
|
||||
pluginService('gitea');
|
||||
});
|
||||
</script>
|
||||
@@ -1,767 +0,0 @@
|
||||
# coding: utf-8
|
||||
|
||||
|
||||
import time
|
||||
import os
|
||||
import sys
|
||||
import re
|
||||
|
||||
|
||||
sys.path.append(os.getcwd() + "/class/core")
|
||||
import mw
|
||||
|
||||
|
||||
# cmd = 'ls /usr/local/lib/ | grep python | cut -d \\ -f 1 | awk \'END {print}\''
|
||||
# info = mw.execShell(cmd)
|
||||
# p = "/usr/local/lib/" + info[0].strip() + "/site-packages"
|
||||
# sys.path.append(p)
|
||||
|
||||
|
||||
app_debug = False
|
||||
if mw.isAppleSystem():
|
||||
app_debug = True
|
||||
|
||||
|
||||
def getPluginName():
|
||||
return 'gitea'
|
||||
|
||||
|
||||
def getPluginDir():
|
||||
return mw.getPluginDir() + '/' + getPluginName()
|
||||
|
||||
|
||||
def getServerDir():
|
||||
return mw.getServerDir() + '/' + getPluginName()
|
||||
|
||||
|
||||
def getInitDFile():
|
||||
if app_debug:
|
||||
return '/tmp/' + getPluginName()
|
||||
return '/etc/init.d/' + getPluginName()
|
||||
|
||||
|
||||
def getArgs():
|
||||
args = sys.argv[2:]
|
||||
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(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 getInitdConfTpl():
|
||||
path = getPluginDir() + "/init.d/gitea.tpl"
|
||||
return path
|
||||
|
||||
|
||||
def getInitdConf():
|
||||
path = getServerDir() + "/init.d/gitea"
|
||||
return path
|
||||
|
||||
|
||||
def getConf():
|
||||
path = getServerDir() + "/custom/conf/app.ini"
|
||||
|
||||
if not os.path.exists(path):
|
||||
return mw.returnJson(False, "请先安装初始化!<br/>默认地址:http://" + mw.getLocalIp() + ":3000")
|
||||
return path
|
||||
|
||||
|
||||
def getConfTpl():
|
||||
path = getPluginDir() + "/conf/app.ini"
|
||||
return path
|
||||
|
||||
|
||||
def status():
|
||||
data = mw.execShell(
|
||||
"ps -ef|grep " + getPluginName() + " |grep -v grep | grep -v python | awk '{print $2}'")
|
||||
if data[0] == '':
|
||||
return 'stop'
|
||||
return 'start'
|
||||
|
||||
|
||||
def getHomeDir():
|
||||
if mw.isAppleSystem():
|
||||
user = mw.execShell(
|
||||
"who | sed -n '2, 1p' |awk '{print $1}'")[0].strip()
|
||||
return '/Users/' + user
|
||||
else:
|
||||
return 'www'
|
||||
|
||||
|
||||
def getRunUser():
|
||||
if mw.isAppleSystem():
|
||||
user = mw.execShell(
|
||||
"who | sed -n '2, 1p' |awk '{print $1}'")[0].strip()
|
||||
return user
|
||||
else:
|
||||
return 'www'
|
||||
|
||||
__SR = '''#!/bin/bash
|
||||
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
|
||||
export PATH
|
||||
export USER=%s
|
||||
export HOME=%s && ''' % ( getRunUser(), getHomeDir())
|
||||
|
||||
|
||||
def contentReplace(content):
|
||||
|
||||
service_path = mw.getServerDir()
|
||||
content = content.replace('{$ROOT_PATH}', mw.getRootDir())
|
||||
content = content.replace('{$SERVER_PATH}', service_path)
|
||||
content = content.replace('{$RUN_USER}', getRunUser())
|
||||
content = content.replace('{$HOME_DIR}', getHomeDir())
|
||||
|
||||
return content
|
||||
|
||||
|
||||
def initDreplace():
|
||||
|
||||
file_tpl = getInitdConfTpl()
|
||||
service_path = mw.getServerDir()
|
||||
|
||||
git_dir = mw.getServerDir() + '/git'
|
||||
if not os.path.exists(git_dir):
|
||||
mw.execShell('mkdir -p ' + git_dir)
|
||||
|
||||
initD_path = getServerDir() + '/init.d'
|
||||
if not os.path.exists(initD_path):
|
||||
os.mkdir(initD_path)
|
||||
file_bin = initD_path + '/' + getPluginName()
|
||||
|
||||
if not os.path.exists(file_bin):
|
||||
content = mw.readFile(file_tpl)
|
||||
content = contentReplace(content)
|
||||
mw.writeFile(file_bin, content)
|
||||
mw.execShell('chmod +x ' + file_bin)
|
||||
|
||||
# systemd
|
||||
systemDir = mw.systemdCfgDir()
|
||||
systemService = systemDir + '/gitea.service'
|
||||
systemServiceTpl = getPluginDir() + '/init.d/gitea.service.tpl'
|
||||
if os.path.exists(systemDir) and not os.path.exists(systemService):
|
||||
service_path = mw.getServerDir()
|
||||
se_content = mw.readFile(systemServiceTpl)
|
||||
se_content = se_content.replace('{$SERVER_PATH}', service_path)
|
||||
mw.writeFile(systemService, se_content)
|
||||
mw.execShell('systemctl daemon-reload')
|
||||
|
||||
log_path = getServerDir() + '/log'
|
||||
if not os.path.exists(log_path):
|
||||
os.mkdir(log_path)
|
||||
|
||||
return file_bin
|
||||
|
||||
|
||||
def getRootUrl():
|
||||
content = mw.readFile(getConf())
|
||||
rep = 'ROOT_URL\s*=\s*(.*)'
|
||||
tmp = re.search(rep, content)
|
||||
if tmp:
|
||||
return tmp.groups()[0]
|
||||
|
||||
rep = 'EXTERNAL_URL\s*=\s*(.*)'
|
||||
tmp = re.search(rep, content)
|
||||
if tmp:
|
||||
return tmp.groups()[0]
|
||||
return ''
|
||||
|
||||
|
||||
def getSshPort():
|
||||
content = mw.readFile(getConf())
|
||||
rep = 'SSH_PORT\s*=\s*(.*)'
|
||||
tmp = re.search(rep, content)
|
||||
if not tmp:
|
||||
return ''
|
||||
return tmp.groups()[0]
|
||||
|
||||
|
||||
def getHttpPort():
|
||||
content = mw.readFile(getConf())
|
||||
rep = 'HTTP_PORT\s*=\s*(.*)'
|
||||
tmp = re.search(rep, content)
|
||||
if not tmp:
|
||||
return ''
|
||||
return tmp.groups()[0]
|
||||
|
||||
|
||||
def getRootPath():
|
||||
content = mw.readFile(getConf())
|
||||
rep = 'ROOT\s*=\s*(.*)'
|
||||
tmp = re.search(rep, content)
|
||||
if not tmp:
|
||||
return ''
|
||||
return tmp.groups()[0]
|
||||
|
||||
|
||||
def getDbConfValue():
|
||||
conf = getConf()
|
||||
if not os.path.exists(conf):
|
||||
return {}
|
||||
|
||||
content = mw.readFile(conf)
|
||||
rep_scope = "\[database\](.*?)\["
|
||||
tmp = re.findall(rep_scope, content, re.S)
|
||||
|
||||
rep = '(\w*)\s*=\s*(.*)'
|
||||
tmp = re.findall(rep, tmp[0])
|
||||
r = {}
|
||||
for x in range(len(tmp)):
|
||||
k = tmp[x][0]
|
||||
v = tmp[x][1]
|
||||
r[k] = v
|
||||
return r
|
||||
|
||||
|
||||
def pMysqlDb(conf):
|
||||
host = conf['HOST'].split(':')
|
||||
# pymysql
|
||||
db = mw.getMyORM()
|
||||
# MySQLdb |
|
||||
# db = mw.getMyORMDb()
|
||||
|
||||
db.setPort(int(host[1]))
|
||||
db.setUser(conf['USER'])
|
||||
|
||||
if 'PASSWD' in conf:
|
||||
db.setPwd(conf['PASSWD'])
|
||||
else:
|
||||
db.setPwd(conf['PASSWORD'])
|
||||
|
||||
db.setDbName(conf['NAME'])
|
||||
# db.setSocket(getSocketFile())
|
||||
db.setCharset("utf8")
|
||||
return db
|
||||
|
||||
|
||||
def pSqliteDb(conf):
|
||||
# print(conf)
|
||||
import db
|
||||
psDb = db.Sql()
|
||||
|
||||
# 默认
|
||||
gsdir = getServerDir() + '/data'
|
||||
dbname = 'gitea'
|
||||
if conf['PATH'][0] == '/':
|
||||
# 绝对路径
|
||||
pass
|
||||
else:
|
||||
path = conf['PATH'].split('/')
|
||||
gsdir = getServerDir() + '/' + path[0]
|
||||
dbname = path[1].split('.')[0]
|
||||
|
||||
# print(gsdir, dbname)
|
||||
psDb.dbPos(gsdir, dbname)
|
||||
return psDb
|
||||
|
||||
|
||||
def getGiteaDbType(conf):
|
||||
|
||||
if 'DB_TYPE' in conf:
|
||||
return conf['DB_TYPE']
|
||||
|
||||
if 'TYPE' in conf:
|
||||
return conf['TYPE']
|
||||
|
||||
return 'NONE'
|
||||
|
||||
|
||||
def pQuery(sql):
|
||||
conf = getDbConfValue()
|
||||
gtype = getGiteaDbType(conf)
|
||||
if gtype == 'sqlite3':
|
||||
db = pSqliteDb(conf)
|
||||
data = db.query(sql, []).fetchall()
|
||||
return data
|
||||
elif gtype == 'mysql':
|
||||
db = pMysqlDb(conf)
|
||||
return db.query(sql)
|
||||
|
||||
print("仅支持mysql|sqlite3配置")
|
||||
exit(0)
|
||||
|
||||
|
||||
def isSqlError(mysqlMsg):
|
||||
# 检测数据库执行错误
|
||||
_mysqlMsg = str(mysqlMsg)
|
||||
# print _mysqlMsg
|
||||
if "MySQLdb" in _mysqlMsg:
|
||||
return mw.returnData(False, 'MySQLdb组件缺失! <br>进入SSH命令行输入: pip install mysql-python')
|
||||
if "2002," in _mysqlMsg:
|
||||
return mw.returnData(False, '数据库连接失败,请检查数据库服务是否启动!')
|
||||
if "using password:" in _mysqlMsg:
|
||||
return mw.returnData(False, '数据库管理密码错误!')
|
||||
if "Connection refused" in _mysqlMsg:
|
||||
return mw.returnData(False, '数据库连接失败,请检查数据库服务是否启动!')
|
||||
if "1133," in _mysqlMsg:
|
||||
return mw.returnData(False, '数据库用户不存在!')
|
||||
if "1007," in _mysqlMsg:
|
||||
return mw.returnData(False, '数据库已经存在!')
|
||||
if "1044," in _mysqlMsg:
|
||||
return mw.returnData(False, mysqlMsg[1])
|
||||
if "2003," in _mysqlMsg:
|
||||
return mw.returnData(False, "Can't connect to MySQL server on '127.0.0.1' (61)")
|
||||
return mw.returnData(True, 'OK')
|
||||
|
||||
|
||||
def appOp(method):
|
||||
file = initDreplace()
|
||||
|
||||
if not mw.isAppleSystem():
|
||||
data = mw.execShell('systemctl ' + method + ' ' + getPluginName())
|
||||
if data[1] == '':
|
||||
return 'ok'
|
||||
return 'fail'
|
||||
|
||||
data = mw.execShell(__SR + file + ' ' + method)
|
||||
if data[1] == '':
|
||||
return 'ok'
|
||||
return data[0]
|
||||
|
||||
|
||||
def start():
|
||||
return appOp('start')
|
||||
|
||||
|
||||
def stop():
|
||||
return appOp('stop')
|
||||
|
||||
|
||||
def restart():
|
||||
return appOp('restart')
|
||||
|
||||
|
||||
def reload():
|
||||
return appOp('reload')
|
||||
|
||||
|
||||
def initdStatus():
|
||||
if mw.isAppleSystem():
|
||||
return "Apple Computer does not support"
|
||||
|
||||
shell_cmd = 'systemctl status gitea | 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 gitea')
|
||||
return 'ok'
|
||||
|
||||
|
||||
def initdUinstall():
|
||||
if mw.isAppleSystem():
|
||||
return "Apple Computer does not support"
|
||||
|
||||
mw.execShell('systemctl disable gitea')
|
||||
return 'ok'
|
||||
|
||||
|
||||
def runLog():
|
||||
log_path = getServerDir() + '/log/gitea.log'
|
||||
return log_path
|
||||
|
||||
|
||||
def postReceiveLog():
|
||||
log_path = getServerDir() + '/log/hooks/post-receive.log'
|
||||
return log_path
|
||||
|
||||
|
||||
def getGogsConf():
|
||||
conf = getConf()
|
||||
if not os.path.exists(conf):
|
||||
return mw.returnJson(False, "请先安装初始化!<br/>默认地址:http://" + mw.getLocalIp() + ":3000")
|
||||
|
||||
gets = [
|
||||
{'name': 'DOMAIN', 'type': -1, 'ps': '服务器域名'},
|
||||
{'name': 'ROOT_URL', 'type': -1, 'ps': '公开的完整URL路径'},
|
||||
{'name': 'HTTP_ADDR', 'type': -1, 'ps': '应用HTTP监听地址'},
|
||||
{'name': 'HTTP_PORT', 'type': -1, 'ps': '应用 HTTP 监听端口号'},
|
||||
|
||||
{'name': 'START_SSH_SERVER', 'type': 2, 'ps': '启动内置SSH服务器'},
|
||||
{'name': 'SSH_PORT', 'type': -1, 'ps': 'SSH 端口号'},
|
||||
|
||||
{'name': 'REQUIRE_SIGNIN_VIEW', 'type': 2, 'ps': '强制登录浏览'},
|
||||
{'name': 'ENABLE_CAPTCHA', 'type': 2, 'ps': '启用验证码服务'},
|
||||
{'name': 'DISABLE_REGISTRATION', 'type': 2, 'ps': '禁止注册,只能由管理员创建帐号'},
|
||||
{'name': 'ENABLE_NOTIFY_MAIL', 'type': 2, 'ps': '是否开启邮件通知'},
|
||||
|
||||
{'name': 'FORCE_PRIVATE', 'type': 2, 'ps': '强制要求所有新建的仓库都是私有'},
|
||||
|
||||
{'name': 'SHOW_FOOTER_BRANDING', 'type': 2, 'ps': 'Gogs推广信息'},
|
||||
{'name': 'SHOW_FOOTER_VERSION', 'type': 2, 'ps': 'Gogs版本信息'},
|
||||
{'name': 'SHOW_FOOTER_TEMPLATE_LOAD_TIME', 'type': 2, 'ps': 'Gogs模板加载时间'},
|
||||
]
|
||||
conf = mw.readFile(conf)
|
||||
result = []
|
||||
|
||||
for g in gets:
|
||||
rep = g['name'] + '\s*=\s*(.*)'
|
||||
tmp = re.search(rep, conf)
|
||||
if not tmp:
|
||||
continue
|
||||
g['value'] = tmp.groups()[0]
|
||||
result.append(g)
|
||||
return mw.returnJson(True, 'OK', result)
|
||||
|
||||
|
||||
def submitGogsConf():
|
||||
gets = ['DOMAIN',
|
||||
'ROOT_URL',
|
||||
'HTTP_ADDR',
|
||||
'HTTP_PORT',
|
||||
'START_SSH_SERVER',
|
||||
'SSH_PORT',
|
||||
'REQUIRE_SIGNIN_VIEW',
|
||||
'FORCE_PRIVATE',
|
||||
'ENABLE_CAPTCHA',
|
||||
'DISABLE_REGISTRATION',
|
||||
'ENABLE_NOTIFY_MAIL',
|
||||
'SHOW_FOOTER_BRANDING',
|
||||
'SHOW_FOOTER_VERSION',
|
||||
'SHOW_FOOTER_TEMPLATE_LOAD_TIME']
|
||||
args = getArgs()
|
||||
filename = getConf()
|
||||
conf = mw.readFile(filename)
|
||||
for g in gets:
|
||||
if g in args:
|
||||
rep = g + '\s*=\s*(.*)'
|
||||
val = g + ' = ' + args[g]
|
||||
conf = re.sub(rep, val, conf)
|
||||
mw.writeFile(filename, conf)
|
||||
restart()
|
||||
return mw.returnJson(True, '设置成功')
|
||||
|
||||
|
||||
def userList():
|
||||
|
||||
conf = getConf()
|
||||
if not os.path.exists(conf):
|
||||
return mw.returnJson(False, "请先安装初始化!<br/>默认地址:http://" + mw.getLocalIp() + ":3000")
|
||||
|
||||
conf = getDbConfValue()
|
||||
gtype = getGiteaDbType(conf)
|
||||
if gtype != 'mysql':
|
||||
return mw.returnJson(False, "仅支持mysql数据操作!")
|
||||
|
||||
import math
|
||||
args = getArgs()
|
||||
|
||||
data = checkArgs(args, ['page', 'page_size'])
|
||||
if not data[0]:
|
||||
return data[1]
|
||||
|
||||
page = int(args['page'])
|
||||
page_size = int(args['page_size'])
|
||||
tojs = args['tojs']
|
||||
search = ''
|
||||
if 'search' in args:
|
||||
search = args['search']
|
||||
|
||||
data = {}
|
||||
|
||||
data['root_url'] = getRootUrl()
|
||||
|
||||
start = (page - 1) * page_size
|
||||
list_count = pQuery('select count(id) as num from user')
|
||||
|
||||
count = list_count[0]["num"]
|
||||
list_data = pQuery(
|
||||
'select id,name,email from user order by id desc limit ' + str(start) + ',' + str(page_size))
|
||||
data['list'] = mw.getPage(
|
||||
{'count': count, 'p': page, 'row': page_size, 'tojs': tojs})
|
||||
data['page'] = page
|
||||
data['page_size'] = page_size
|
||||
data['page_count'] = int(math.ceil(count / page_size))
|
||||
data['data'] = list_data
|
||||
return mw.returnJson(True, 'OK', data)
|
||||
|
||||
|
||||
def getAllUserProject(user, search=''):
|
||||
path = getRootPath() + '/' + user
|
||||
dlist = []
|
||||
if os.path.exists(path):
|
||||
for filename in os.listdir(path):
|
||||
tmp = {}
|
||||
filePath = path + '/' + filename
|
||||
if os.path.isdir(filePath):
|
||||
if search == '':
|
||||
tmp['name'] = filename.replace('.git', '')
|
||||
dlist.append(tmp)
|
||||
else:
|
||||
if filename.find(search) != -1:
|
||||
tmp['name'] = filename.replace('.git', '')
|
||||
dlist.append(tmp)
|
||||
return dlist
|
||||
|
||||
|
||||
def checkProjectListIsHasScript(user, data):
|
||||
path = getRootPath() + '/' + user
|
||||
for x in range(len(data)):
|
||||
name = data[x]['name'] + '.git'
|
||||
path_tmp = path + '/' + name + '/hooks/post-receive.d/post-receive'
|
||||
if os.path.exists(path_tmp):
|
||||
data[x]['has_hook'] = True
|
||||
else:
|
||||
data[x]['has_hook'] = False
|
||||
return data
|
||||
|
||||
|
||||
def userProjectList():
|
||||
import math
|
||||
args = getArgs()
|
||||
# print args
|
||||
|
||||
page = 1
|
||||
page_size = 5
|
||||
search = ''
|
||||
|
||||
if not 'name' in args:
|
||||
return mw.returnJson(False, '缺少参数name')
|
||||
if 'page' in args:
|
||||
page = int(args['page'])
|
||||
|
||||
if 'page_size' in args:
|
||||
page_size = int(args['page_size'])
|
||||
|
||||
if 'search' in args:
|
||||
search = args['search']
|
||||
|
||||
data = {}
|
||||
|
||||
ulist = getAllUserProject(args['name'])
|
||||
dlist_sum = len(ulist)
|
||||
|
||||
start = (page - 1) * page_size
|
||||
ret_data = ulist[start:start + page_size]
|
||||
ret_data = checkProjectListIsHasScript(args['name'], ret_data)
|
||||
|
||||
data['root_url'] = getRootUrl()
|
||||
data['data'] = ret_data
|
||||
data['args'] = args
|
||||
data['list'] = mw.getPage(
|
||||
{'count': dlist_sum, 'p': page, 'row': page_size, 'tojs': 'userProjectList'})
|
||||
|
||||
return mw.returnJson(True, 'OK', data)
|
||||
|
||||
|
||||
def projectScriptEdit():
|
||||
args = getArgs()
|
||||
|
||||
if not 'user' in args:
|
||||
return mw.returnJson(True, 'username missing')
|
||||
|
||||
if not 'name' in args:
|
||||
return mw.returnJson(True, 'project name missing')
|
||||
|
||||
user = args['user']
|
||||
name = args['name'] + '.git'
|
||||
post_receive = getRootPath() + '/' + user + '/' + name + \
|
||||
'/custom_hooks/commit'
|
||||
if os.path.exists(post_receive):
|
||||
return mw.returnJson(True, 'OK', {'path': post_receive})
|
||||
else:
|
||||
return mw.returnJson(False, 'file does not exist')
|
||||
|
||||
|
||||
def projectScriptLoad():
|
||||
args = getArgs()
|
||||
if not 'user' in args:
|
||||
return mw.returnJson(True, 'username missing')
|
||||
|
||||
if not 'name' in args:
|
||||
return mw.returnJson(True, 'project name missing')
|
||||
|
||||
user = args['user']
|
||||
name = args['name'] + '.git'
|
||||
|
||||
path = getRootPath() + '/' + user + '/' + name
|
||||
post_receive_tpl = getPluginDir() + '/hook/post-receive.tpl'
|
||||
post_receive = path + '/hooks/post-receive.d/post-receive'
|
||||
|
||||
if not os.path.exists(path + '/custom_hooks'):
|
||||
mw.execShell('mkdir -p ' + path + '/custom_hooks')
|
||||
mw.execShell('chown -R www:www ' + path + '/custom_hooks')
|
||||
|
||||
pct_content = mw.readFile(post_receive_tpl)
|
||||
pct_content = pct_content.replace('{$PATH}', path + '/custom_hooks')
|
||||
mw.writeFile(post_receive, pct_content)
|
||||
mw.execShell('chmod 777 ' + post_receive)
|
||||
mw.execShell('chown -R www:www ' + post_receive)
|
||||
|
||||
commit_tpl = getPluginDir() + '/hook/commit.tpl'
|
||||
commit = path + '/custom_hooks/commit'
|
||||
|
||||
codeDir = mw.getRootDir() + '/git'
|
||||
|
||||
cc_content = mw.readFile(commit_tpl)
|
||||
|
||||
gitPath = getRootPath()
|
||||
cc_content = cc_content.replace('{$GITROOTURL}', gitPath)
|
||||
cc_content = cc_content.replace('{$CODE_DIR}', codeDir)
|
||||
cc_content = cc_content.replace('{$USERNAME}', user)
|
||||
cc_content = cc_content.replace('{$PROJECT}', args['name'])
|
||||
cc_content = cc_content.replace('{$WEB_ROOT}', mw.getWwwDir())
|
||||
mw.writeFile(commit, cc_content)
|
||||
mw.execShell('chmod 777 ' + commit)
|
||||
mw.execShell('chown -R www:www ' + commit)
|
||||
|
||||
return 'ok'
|
||||
|
||||
|
||||
def projectScriptUnload():
|
||||
args = getArgs()
|
||||
data = checkArgs(args, ['user', 'name'])
|
||||
if not data[0]:
|
||||
return data[1]
|
||||
|
||||
user = args['user']
|
||||
name = args['name'] + '.git'
|
||||
|
||||
post_receive = getRootPath() + '/' + user + '/' + name + \
|
||||
'/hooks/post-receive.d/post-receive'
|
||||
mw.execShell('rm -f ' + post_receive)
|
||||
|
||||
commit = getRootPath() + '/' + user + '/' + name + \
|
||||
'/custom_hooks/commit'
|
||||
mw.execShell('rm -f ' + commit)
|
||||
return 'ok'
|
||||
|
||||
|
||||
def projectScriptDebug():
|
||||
args = getArgs()
|
||||
data = checkArgs(args, ['user', 'name'])
|
||||
if not data[0]:
|
||||
return data[1]
|
||||
|
||||
user = args['user']
|
||||
name = args['name'] + '.git'
|
||||
commit_log = getRootPath() + '/' + user + '/' + name + \
|
||||
'/custom_hooks/sh.log'
|
||||
|
||||
data = {}
|
||||
if os.path.exists(commit_log):
|
||||
data['status'] = True
|
||||
data['path'] = commit_log
|
||||
else:
|
||||
data['status'] = False
|
||||
data['msg'] = '没有日志文件'
|
||||
|
||||
return mw.getJson(data)
|
||||
|
||||
|
||||
def gogsEdit():
|
||||
data = {}
|
||||
data['post_receive'] = getPluginDir() + '/hook/post-receive.tpl'
|
||||
data['commit'] = getPluginDir() + '/hook/commit.tpl'
|
||||
return mw.getJson(data)
|
||||
|
||||
|
||||
def getRsaPublic():
|
||||
path = getHomeDir()
|
||||
path += '/.ssh/id_rsa.pub'
|
||||
|
||||
content = mw.readFile(path)
|
||||
|
||||
data = {}
|
||||
data['mw'] = content
|
||||
return mw.getJson(data)
|
||||
|
||||
|
||||
def getTotalStatistics():
|
||||
st = status()
|
||||
data = {}
|
||||
if st.strip() == 'start':
|
||||
list_count = pQuery('select count(id) as num from repository')
|
||||
count = list_count[0]["num"]
|
||||
data['status'] = True
|
||||
data['count'] = count
|
||||
data['ver'] = mw.readFile(getServerDir() + '/version.pl').strip()
|
||||
return mw.returnJson(True, 'ok', data)
|
||||
|
||||
data['status'] = False
|
||||
data['count'] = 0
|
||||
return mw.returnJson(False, 'fail', data)
|
||||
|
||||
|
||||
def uninstallPreInspection():
|
||||
repo_dir = getServerDir() + "/data/gitea-repositories"
|
||||
if not os.path.exists(repo_dir):
|
||||
return 'ok'
|
||||
dir_list = os.listdir(repo_dir)
|
||||
if len(dir_list) > 0:
|
||||
return "有项目数据!请手动删除Gitea<br/> rm -rf {}".format(getServerDir())
|
||||
return 'ok'
|
||||
|
||||
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 == 'uninstall_pre_inspection':
|
||||
print(uninstallPreInspection())
|
||||
elif func == 'run_log':
|
||||
print(runLog())
|
||||
elif func == 'post_receive_log':
|
||||
print(postReceiveLog())
|
||||
elif func == 'conf':
|
||||
print(getConf())
|
||||
elif func == 'init_conf':
|
||||
print(getInitdConf())
|
||||
elif func == 'get_gogs_conf':
|
||||
print(getGogsConf())
|
||||
elif func == 'submit_gogs_conf':
|
||||
print(submitGogsConf())
|
||||
elif func == 'user_list':
|
||||
print(userList())
|
||||
elif func == 'user_project_list':
|
||||
print(userProjectList())
|
||||
elif func == 'project_script_edit':
|
||||
print(projectScriptEdit())
|
||||
elif func == 'project_script_load':
|
||||
print(projectScriptLoad())
|
||||
elif func == 'project_script_unload':
|
||||
print(projectScriptUnload())
|
||||
elif func == 'project_script_debug':
|
||||
print(projectScriptDebug())
|
||||
elif func == 'gogs_edit':
|
||||
print(gogsEdit())
|
||||
elif func == 'get_rsa_public':
|
||||
print(getRsaPublic())
|
||||
elif func == 'get_total_statistics':
|
||||
print(getTotalStatistics())
|
||||
else:
|
||||
print('fail')
|
||||
@@ -1,18 +0,0 @@
|
||||
{
|
||||
"ps": "Gitea 是一个开源社区驱动的轻量级代码托管解决方案。",
|
||||
"name": "gitea",
|
||||
"title": "Gitea",
|
||||
"versions": ["1.17.2"],
|
||||
"tip": "soft",
|
||||
"install_pre_inspection":false,
|
||||
"uninstall_pre_inspection":true,
|
||||
"checks": "server/gitea",
|
||||
"path":"server/gitea",
|
||||
"author": "gitea",
|
||||
"date": "2022-10-03",
|
||||
"home": "https://dl.gitea.io/",
|
||||
"type": "Git服务器",
|
||||
"shell": "install.sh",
|
||||
"pid": "3",
|
||||
"sort": 7
|
||||
}
|
||||
@@ -1,20 +0,0 @@
|
||||
[Unit]
|
||||
Description=Gitea (Git with a cup of tea)
|
||||
After=syslog.target
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
RestartSec=2s
|
||||
Type=simple
|
||||
User=www
|
||||
Group=www
|
||||
WorkingDirectory={$SERVER_PATH}/gitea
|
||||
ExecStart={$SERVER_PATH}/gitea/gitea web
|
||||
Restart=always
|
||||
Environment=USER=www HOME=/home/www GITEA_WORK_DIR={$SERVER_PATH}/gitea
|
||||
RemainAfterExit=yes
|
||||
#AmbientCapabilities=CAP_NET_BIND_SERVICE
|
||||
#CapabilityBoundingSet=CAP_NET_BIND_SERVICE
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
@@ -1,110 +0,0 @@
|
||||
#!/bin/sh
|
||||
#
|
||||
# /etc/rc.d/init.d/Gitea
|
||||
#
|
||||
# Runs the Gogs
|
||||
#
|
||||
#
|
||||
# chkconfig: - 85 15
|
||||
#
|
||||
|
||||
### BEGIN INIT INFO
|
||||
# Provides: Gitea
|
||||
# Required-Start: $remote_fs $syslog
|
||||
# Required-Stop: $remote_fs $syslog
|
||||
# Should-Start: mysql postgresql
|
||||
# Should-Stop: mysql postgresql
|
||||
# Default-Start: 2 3 4 5
|
||||
# Default-Stop: 0 1 6
|
||||
# Short-Description: Start Gitea at boot time.
|
||||
# Description: Control Gitea.
|
||||
### END INIT INFO
|
||||
|
||||
# Source function library.
|
||||
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
|
||||
|
||||
# Default values
|
||||
export HOME={$HOME_DIR}
|
||||
export USER={$RUN_USER}
|
||||
NAME=gitea
|
||||
GOGS_HOME={$SERVER_PATH}/gitea
|
||||
GOGS_PATH=${GOGS_HOME}/$NAME
|
||||
GOGS_USER={$RUN_USER}
|
||||
SERVICENAME="gitea"
|
||||
LOCKFILE=/tmp/gitea.lock
|
||||
LOGPATH=${GOGS_HOME}/log
|
||||
LOGFILE=${LOGPATH}/gitea.log
|
||||
RETVAL=0
|
||||
|
||||
|
||||
[ -r /etc/sysconfig/$NAME ] && . /etc/sysconfig/$NAME
|
||||
DAEMON_OPTS="--check $NAME"
|
||||
[ ! -z "$GOGS_USER" ] && DAEMON_OPTS="$DAEMON_OPTS --user=${GOGS_USER}"
|
||||
|
||||
|
||||
status(){
|
||||
isStart=`ps -ef|grep 'gitea web' |grep -v grep|awk '{print $2}'`
|
||||
if [ "$isStart" == '' ];then
|
||||
echo -e "${SERVICENAME} not running"
|
||||
else
|
||||
echo -e "${SERVICENAME}(pid $(echo $isStart)) already running"
|
||||
fi
|
||||
}
|
||||
|
||||
start() {
|
||||
isStart=`ps -ef|grep 'gitea web' |grep -v grep|awk '{print $2}'`
|
||||
if [ "$isStart" != '' ];then
|
||||
echo "${SERVICENAME}(pid $(echo $isStart)) already running"
|
||||
return $RETVAL
|
||||
fi
|
||||
|
||||
cd ${GOGS_HOME}
|
||||
echo -e "Starting ${SERVICENAME}: \c"
|
||||
${GOGS_PATH} web > ${LOGFILE} 2>&1 &
|
||||
RETVAL=$?
|
||||
[ $RETVAL = 0 ] && touch ${LOCKFILE} && echo -e "\033[32mdone\033[0m"
|
||||
return $RETVAL
|
||||
}
|
||||
|
||||
stop() {
|
||||
|
||||
pids=`ps -ef|grep 'gitea web' |grep -v grep|awk '{print $2}'`
|
||||
arr=($pids)
|
||||
echo -e "Stopping gitea... \c"
|
||||
for p in ${arr[@]}
|
||||
do
|
||||
kill -9 $p
|
||||
done
|
||||
echo -e "\033[32mdone\033[0m"
|
||||
}
|
||||
|
||||
case "$1" in
|
||||
start)
|
||||
start
|
||||
;;
|
||||
stop)
|
||||
stop
|
||||
;;
|
||||
status)
|
||||
status
|
||||
;;
|
||||
restart)
|
||||
stop
|
||||
start
|
||||
;;
|
||||
reload)
|
||||
stop
|
||||
start
|
||||
;;
|
||||
*)
|
||||
echo "Usage: ${NAME} {start|stop|status|restart}"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
exit $RETVAL
|
||||
@@ -1,128 +0,0 @@
|
||||
#!/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
|
||||
if [ -f ${rootPath}/bin/activate ];then
|
||||
source ${rootPath}/bin/activate
|
||||
fi
|
||||
|
||||
|
||||
URL_DOWNLOAD=https://dl.gitea.io/
|
||||
|
||||
|
||||
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}'`
|
||||
|
||||
|
||||
getBit(){
|
||||
echo `getconf LONG_BIT`
|
||||
}
|
||||
|
||||
Install_Rsync(){
|
||||
if [ "$OSNAME" == "debian" ] || [ "$OSNAME" == "ubuntu" ];then
|
||||
apt install -y rsync
|
||||
elif [[ "$OSNAME" == "arch" ]]; then
|
||||
echo y | pacman -Sy rsync
|
||||
elif [[ "$OSNAME" == "macos" ]]; then
|
||||
# brew install rsync
|
||||
# brew install lsyncd
|
||||
echo "ok"
|
||||
else
|
||||
yum install -y rsync
|
||||
fi
|
||||
}
|
||||
|
||||
|
||||
Install_App()
|
||||
{
|
||||
Install_Rsync
|
||||
|
||||
mkdir -p $serverPath/source/gitea
|
||||
|
||||
if id www &> /dev/null ;then
|
||||
echo "www uid is `id -u www`"
|
||||
echo "www shell is `grep "^www:" /etc/passwd |cut -d':' -f7 `"
|
||||
else
|
||||
groupadd www
|
||||
useradd -g www www
|
||||
fi
|
||||
|
||||
if [ "macos" != "$OSNAME" ];then
|
||||
if [ ! -d /home/www ];then
|
||||
mkdir -p /home/www
|
||||
chown -R www:www /home/www
|
||||
fi
|
||||
fi
|
||||
|
||||
echo '正在安装脚本文件...' > $install_tmp
|
||||
version=$1
|
||||
|
||||
|
||||
git config --global push.default simple
|
||||
|
||||
if [ "macos" == "$OSNAME" ];then
|
||||
file=gitea-${version}-darwin-10.12-amd64
|
||||
else
|
||||
file=gitea-${version}-linux-amd64
|
||||
fi
|
||||
|
||||
file_xz="${file}.xz"
|
||||
echo "wget -O $serverPath/source/gitea/$file_xz ${URL_DOWNLOAD}/gitea/${version}/${file_xz}"
|
||||
if [ ! -f $serverPath/source/gitea/$file_xz ];then
|
||||
wget --no-check-certificate -O $serverPath/source/gitea/$file_xz ${URL_DOWNLOAD}/gitea/${version}/${file_xz}
|
||||
fi
|
||||
|
||||
cd $serverPath/source/gitea && xz -k -d $file_xz
|
||||
if [ -f $file ];then
|
||||
mkdir -p $serverPath/gitea
|
||||
mv $serverPath/source/gitea/$file $serverPath/gitea/gitea
|
||||
chmod +x $serverPath/gitea/gitea
|
||||
|
||||
chown -R www:www $serverPath/gitea
|
||||
fi
|
||||
|
||||
|
||||
if [ -d $serverPath/gitea ];then
|
||||
echo $version > $serverPath/gitea/version.pl
|
||||
|
||||
cd ${rootPath} && python3 plugins/gitea/index.py start
|
||||
cd ${rootPath} && python3 plugins/gitea/index.py initd_install
|
||||
fi
|
||||
|
||||
echo 'install success' > $install_tmp
|
||||
}
|
||||
|
||||
Uninstall_App()
|
||||
{
|
||||
|
||||
if [ -f /usr/lib/systemd/system/gitea.service ];then
|
||||
systemctl stop gitea
|
||||
systemctl disable gitea
|
||||
rm -rf /usr/lib/systemd/system/gitea.service
|
||||
systemctl daemon-reload
|
||||
fi
|
||||
|
||||
if [ -f $serverPath/gitea/initd/gitea ];then
|
||||
$serverPath/gitea/initd/gitea stop
|
||||
fi
|
||||
|
||||
rm -rf $serverPath/gitea
|
||||
echo 'uninstall success' > $install_tmp
|
||||
}
|
||||
|
||||
|
||||
action=$1
|
||||
version=$2
|
||||
if [ "${1}" == 'install' ];then
|
||||
Install_App $version
|
||||
else
|
||||
Uninstall_App $version
|
||||
fi
|
||||
@@ -1,369 +0,0 @@
|
||||
|
||||
function gogsPost(method,args,callback, title){
|
||||
|
||||
var _args = null;
|
||||
if (typeof(args) == 'string'){
|
||||
_args = JSON.stringify(toArrayObject(args));
|
||||
} else {
|
||||
_args = JSON.stringify(args);
|
||||
}
|
||||
|
||||
var _title = '正在获取...';
|
||||
if (typeof(title) != 'undefined'){
|
||||
_title = title;
|
||||
}
|
||||
|
||||
var loadT = layer.msg(_title, { icon: 16, time: 0, shade: 0.3 });
|
||||
$.post('/plugins/run', {name:'gitea', 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 gogsSetConfig(){
|
||||
gogsPost('get_gogs_conf', '', function(data){
|
||||
var rrdata = $.parseJSON(data.data);
|
||||
if (!rrdata.status){
|
||||
layer.msg(rrdata.msg,{icon:0,time:2000,shade: [0.3, '#000']});
|
||||
return;
|
||||
}
|
||||
var rdata = rrdata.data;
|
||||
var mlist = '';
|
||||
for (var i = 0; i < rdata.length; i++) {
|
||||
var w = '140';
|
||||
if (rdata[i].name == 'error_reporting') w = '250';
|
||||
var ibody = '<input style="width: ' + w + 'px;" class="bt-input-text mr5" name="' + rdata[i].name + '" value="' + rdata[i].value + '" type="text" >';
|
||||
switch (rdata[i].type) {
|
||||
case 0:
|
||||
var selected_1 = (rdata[i].value == 1) ? 'selected' : '';
|
||||
var selected_0 = (rdata[i].value == 0) ? 'selected' : '';
|
||||
ibody = '<select class="bt-input-text mr5" name="' + rdata[i].name + '" style="width: ' + w + 'px;">\
|
||||
<option value="1" ' + selected_1 + '>开启</option>\
|
||||
<option value="0" ' + selected_0 + '>关闭</option>\
|
||||
</select>';
|
||||
break;
|
||||
case 1:
|
||||
var selected_1 = (rdata[i].value == 'On') ? 'selected' : '';
|
||||
var selected_0 = (rdata[i].value == 'Off') ? 'selected' : '';
|
||||
ibody = '<select class="bt-input-text mr5" name="' + rdata[i].name + '" style="width: ' + w + 'px;">\
|
||||
<option value="On" ' + selected_1 + '>开启</option>\
|
||||
<option value="Off" ' + selected_0 + '>关闭</option></select>'
|
||||
break;
|
||||
case 2:
|
||||
var selected_1 = (rdata[i].value == 'true') ? 'selected' : '';
|
||||
var selected_0 = (rdata[i].value == 'false') ? 'selected' : '';
|
||||
ibody = '<select class="bt-input-text mr5" name="' + rdata[i].name + '" style="width: ' + w + 'px;">\
|
||||
<option value="true" ' + selected_1 + '>开启</option>\
|
||||
<option value="false" ' + selected_0 + '>关闭</option></select>'
|
||||
break;
|
||||
}
|
||||
mlist += '<p><span>' + rdata[i].name + '</span>' + ibody + ', <font>' + rdata[i].ps + '</font></p>'
|
||||
}
|
||||
var html = '<style>.conf_p p{margin-bottom: 2px}</style><div class="conf_p" style="margin-bottom:0">\
|
||||
' + mlist + '\
|
||||
<div style="margin-top:10px; padding-right:15px" class="text-right">\
|
||||
<button class="btn btn-success btn-sm mr5" onclick="gogsSetConfig()">刷新</button>\
|
||||
<button class="btn btn-success btn-sm" onclick="submitGogsConf()">保存</button></div>\
|
||||
</div>';
|
||||
$(".soft-man-con").html(html);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
//提交PHP配置
|
||||
function submitGogsConf() {
|
||||
var data = {
|
||||
DOMAIN: $("input[name='DOMAIN']").val(),
|
||||
ROOT_URL: $("input[name='ROOT_URL']").val(),
|
||||
HTTP_ADDR: $("select[name='HTTP_ADDR']").val(),
|
||||
HTTP_PORT: $("input[name='HTTP_PORT']").val(),
|
||||
START_SSH_SERVER: $("select[name='START_SSH_SERVER']").val() || 'false',
|
||||
SSH_PORT: $("input[name='SSH_PORT']").val(),
|
||||
REQUIRE_SIGNIN_VIEW: $("select[name='REQUIRE_SIGNIN_VIEW']").val() || 'false',
|
||||
ENABLE_CAPTCHA: $("select[name='ENABLE_CAPTCHA']").val() || 'true',
|
||||
DISABLE_REGISTRATION: $("select[name='DISABLE_REGISTRATION']").val() || 'false',
|
||||
ENABLE_NOTIFY_MAIL: $("select[name='ENABLE_NOTIFY_MAIL']").val() || 'false',
|
||||
FORCE_PRIVATE: $("select[name='FORCE_PRIVATE']").val() || 'false',
|
||||
SHOW_FOOTER_BRANDING: $("select[name='SHOW_FOOTER_BRANDING']").val() || 'false',
|
||||
SHOW_FOOTER_VERSION: $("select[name='SHOW_FOOTER_VERSION']").val() || 'false',
|
||||
SHOW_FOOTER_TEMPLATE_LOAD_TIME: $("select[name='SHOW_FOOTER_TEMPLATE_LOAD_TIME']").val() || 'false',
|
||||
};
|
||||
|
||||
gogsPost('submit_gogs_conf', data, function(ret_data){
|
||||
var rdata = $.parseJSON(ret_data.data);
|
||||
layer.msg(rdata.msg, { icon: rdata.status ? 1 : 2 });
|
||||
gogsSetConfig();
|
||||
});
|
||||
}
|
||||
|
||||
function gogsEdit(){
|
||||
|
||||
gogsPost('gogs_edit',{} , function(data){
|
||||
// console.log(data);
|
||||
var rdata = $.parseJSON(data.data);
|
||||
var edit = '<p class="status">通用的手动编辑:</p>';
|
||||
edit +='<div class="sfm-opt">\
|
||||
<button class="btn btn-default btn-sm" onclick="onlineEditFile(0,\''+rdata['post_receive']+'\');">post-receive.tpl</button>\
|
||||
<button class="btn btn-default btn-sm" onclick="onlineEditFile(0,\''+rdata['commit']+'\');">commit.tpl</button>\
|
||||
</div>';
|
||||
$(".soft-man-con").html(edit);
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
function giteaUserList(page, search) {
|
||||
|
||||
var _data = {};
|
||||
if (typeof(page) =='undefined'){
|
||||
var page = 1;
|
||||
}
|
||||
|
||||
_data['page'] = page;
|
||||
_data['page_size'] = 10;
|
||||
if(typeof(search) != 'undefined'){
|
||||
_data['search'] = search;
|
||||
}
|
||||
_data['tojs'] = 'giteaUserList';
|
||||
|
||||
gogsPost('user_list', _data, function(data){
|
||||
|
||||
var rdata = $.parseJSON(data.data);
|
||||
if (!rdata.status){
|
||||
layer.msg(rdata.msg,{icon:0,time:2000,shade: [0.3, '#000']});
|
||||
return;
|
||||
}
|
||||
content = '<div class="finduser"><input class="bt-input-text mr5 outline_no" type="text" placeholder="查找用户名" id="find_user" style="height: 28px; border-radius: 3px;width: 435px;">';
|
||||
content += '<button class="btn btn-success btn-sm" onclick="userFind();">查找</button></div>';
|
||||
|
||||
content += '<div class="divtable" style="margin-top:5px;"><table class="table table-hover" width="100%" cellspacing="0" cellpadding="0" border="0">';
|
||||
content += '<thead><tr>';
|
||||
content += '<th>序号</th>';
|
||||
content += '<th>用户或组织</th>';
|
||||
content += '<th>邮件地址</th>';
|
||||
content += '<th>操作(<a href="'+rdata['data']['root_url']+'" class="btlink" target="_blank">WEB管理</a>)</th>';
|
||||
content += '</tr></thead>';
|
||||
|
||||
content += '<tbody>';
|
||||
|
||||
ulist = rdata['data']['data'];
|
||||
for (i in ulist){
|
||||
email = ulist[i]["email"] == '' ? '无' : ulist[i]["email"];
|
||||
content += '<tr><td>'+ulist[i]["id"]+'</td>'+
|
||||
'<td>'+ulist[i]["name"]+'</td>'+
|
||||
'<td>'+email+'</td>'+
|
||||
'<td><a class="btlink" onclick="userProjectList(\''+ulist[i]["name"]+'\')">项目管理</a></td>'+
|
||||
'</tr>';
|
||||
}
|
||||
|
||||
content += '</tbody>';
|
||||
content += '</table></div>';
|
||||
|
||||
var page = '<div class="dataTables_paginate paging_bootstrap pagination" style="margin-top:0px;"><ul id="softPage" class="page"><div>';
|
||||
page += rdata['data']['list'];
|
||||
page += '</div></ul></div>';
|
||||
|
||||
content += page;
|
||||
|
||||
$(".soft-man-con").html(content);
|
||||
});
|
||||
}
|
||||
|
||||
function userProjectList(user, search){
|
||||
var loadOpen = layer.open({
|
||||
type: 1,
|
||||
title: '用户('+user+')项目列表',
|
||||
area: '500px',
|
||||
content:"<div class='bt-form pd20 c6'>\
|
||||
<div>\
|
||||
<div id='gitea_table' class='divtable' style='margin-top:5px;'>\
|
||||
<table class='table table-hover'>\
|
||||
<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>\
|
||||
</div>\
|
||||
</div>",
|
||||
success:function(){
|
||||
userProjectListPost(user,search);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function userProjectListPost(user, search){
|
||||
var req = {};
|
||||
if (!isNaN(user)){
|
||||
req['page'] = user;
|
||||
req['name'] = user = getCookie('gogsUserSelected');
|
||||
} else {
|
||||
req['page'] = 1;
|
||||
req['name'] = user;
|
||||
setCookie('gogsUserSelected', user);
|
||||
}
|
||||
|
||||
req['page_size'] = 5;
|
||||
req['search'] = '';
|
||||
if(typeof(search) != 'undefined'){
|
||||
req['search'] = search;
|
||||
}
|
||||
|
||||
gogsPost('user_project_list', req, function(data){
|
||||
var rdata = [];
|
||||
try {
|
||||
rdata = $.parseJSON(data.data);
|
||||
} catch(e){}
|
||||
|
||||
if (!rdata['status']){
|
||||
layer.msg(rdata['msg'], { icon: 2 });
|
||||
return;
|
||||
}
|
||||
|
||||
var list = '';
|
||||
// console.log(rdata);
|
||||
var project_list = rdata['data']['data'];
|
||||
for (i in project_list) {
|
||||
var name = project_list[i]['name'];
|
||||
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>\
|
||||
</td>\
|
||||
</tr>';
|
||||
}
|
||||
|
||||
$('#gitea_table tbody').html(list);
|
||||
|
||||
var page = rdata['data']['list'];
|
||||
$('#gitea_table .gitea_page').html(page);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
function projectScript(user, name,has_hook){
|
||||
// console.log(user,name,has_hook);
|
||||
var html = '';
|
||||
if (has_hook){
|
||||
html += '<button class="btn btn-default btn-sm hook_edit">手动编辑</button>';
|
||||
html += '<button class="btn btn-default btn-sm hook_log">调试日志</button>';
|
||||
html += '<button class="btn btn-default btn-sm hook_load">重新加载</button>';
|
||||
html += '<button class="btn btn-default btn-sm hook_unload">卸载脚本</button>';
|
||||
} else {
|
||||
html += '<button class="btn btn-default btn-sm hook_load">加载脚本</button>';
|
||||
}
|
||||
|
||||
var loadOpen = layer.open({
|
||||
type: 1,
|
||||
title: '['+user+']['+name+']脚本设置',
|
||||
area: '240px',
|
||||
content:'<div class="change-default pd20">'+html+'</div>',
|
||||
success:function(layero,index) {
|
||||
|
||||
$('.hook_edit').click(function(){
|
||||
projectScriptEdit(user,name,index);
|
||||
});
|
||||
|
||||
$('.hook_log').click(function(){
|
||||
projectScriptDebug(user,name,index);
|
||||
});
|
||||
|
||||
$('.hook_load').click(function(){
|
||||
projectScriptLoad(user,name,index);
|
||||
});
|
||||
|
||||
$('.hook_unload').click(function(){
|
||||
projectScriptUnload(user,name,index);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function projectScriptEdit(user,name,index){
|
||||
gogsPost('project_script_edit', {'user':user,'name':name}, function(data){
|
||||
var rdata = $.parseJSON(data.data);
|
||||
if (rdata['status']){
|
||||
onlineEditFile(0, rdata['data']['path']);
|
||||
} else {
|
||||
layer.msg(rdata.msg,{icon:1,time:2000,shade: [0.3, '#000']});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function projectScriptLoad(user,name,index){
|
||||
gogsPost('project_script_load', {'user':user,'name':name}, function(data){
|
||||
if (data.data != 'ok'){
|
||||
layer.msg(data.data,{icon:0,time:2000,shade: [0.3, '#000']});
|
||||
return;
|
||||
}
|
||||
|
||||
showMsg('加载成功!',function(){
|
||||
layer.close(index);
|
||||
userProjectListPost(1);
|
||||
},{icon:1,time:2000,shade: [0.3, '#000']},2000);
|
||||
});
|
||||
}
|
||||
|
||||
function projectScriptUnload(user,name,index){
|
||||
gogsPost('project_script_unload', {'user':user,'name':name}, function(data){
|
||||
if (data.data != 'ok'){
|
||||
layer.msg(data.data,{icon:0,time:2000,shade: [0.3, '#000']});
|
||||
return;
|
||||
}
|
||||
|
||||
showMsg('卸载成功!',function(){
|
||||
layer.close(index);
|
||||
userProjectListPost(1);
|
||||
},{icon:1,time:2000,shade: [0.3, '#000']},2000);
|
||||
});
|
||||
}
|
||||
|
||||
function projectScriptDebug(user,name,index){
|
||||
gogsPost('project_script_debug', {'user':user,'name':name}, function(data){
|
||||
var rdata = $.parseJSON(data.data);
|
||||
if (rdata['status']){
|
||||
onlineEditFile(0, rdata['path']);
|
||||
} else {
|
||||
showMsg(rdata.msg,function(){
|
||||
},{icon:1,time:2000,shade: [0.3, '#000']},2000);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function getRsaPublic(){
|
||||
gogsPost('get_rsa_public', {}, function(data){
|
||||
var rdata = $.parseJSON(data.data);
|
||||
var con = '<div class="tab-con">\
|
||||
<div class="myKeyCon ptb15">\
|
||||
<textarea style="margin:0px;width:580px;height:110px;outline:none;" spellcheck="false">'+rdata.mw+'</textarea>\
|
||||
</div>\
|
||||
<ul class="help-info-text c7 pull-left"></ul>\
|
||||
</div>'
|
||||
layer.open({
|
||||
type: 1,
|
||||
area: "600px",
|
||||
title: '本机公钥',
|
||||
closeBtn: 2,
|
||||
shift: 5,
|
||||
shadeClose: false,
|
||||
content:con
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function giteaRead(){
|
||||
|
||||
var readme = '<ul class="help-info-text c7">';
|
||||
readme += '<li>默认使用MySQL,第一个启动加载各种配置,并修改成正确的数据库配置</li>';
|
||||
readme += '<li>邮件端口使用456,gogs仅支持使用STARTTLS的SMTP协议</li>';
|
||||
readme += '<li>如果使用项目中脚本本地同步,<a target="_blank" href="https://github.com/midoks/mdserver-web/wiki/%E6%8F%92%E4%BB%B6%E7%AE%A1%E7%90%86%5Bgogs%E4%BD%BF%E7%94%A8%E8%AF%B4%E6%98%8E%5D#%E5%90%AF%E5%8A%A8gogs%E5%90%8E%E5%A6%82%E6%9E%9C%E8%A6%81%E4%BD%BF%E7%94%A8hook%E8%84%9A%E6%9C%AC%E5%90%8C%E6%AD%A5%E4%BB%A3%E7%A0%81%E9%9C%80%E8%A6%81%E5%BC%80%E5%90%AFssh%E7%AB%AF%E5%8F%A3">点击查看</></li>';
|
||||
readme += '<li><a href="#" onclick="getRsaPublic();">点击查看本机公钥</></li>';
|
||||
readme += '</ul>';
|
||||
|
||||
$('.soft-man-con').html(readme);
|
||||
}
|
||||
@@ -1,63 +0,0 @@
|
||||
APP_NAME = Gogs
|
||||
RUN_USER = {$RUN_USER}
|
||||
RUN_MODE = prod
|
||||
|
||||
[database]
|
||||
DB_TYPE = mysql
|
||||
HOST = 127.0.0.1:3306
|
||||
NAME = gogs
|
||||
USER = gogs
|
||||
PASSWD = gogs
|
||||
SSL_MODE = disable
|
||||
PATH = data/gogs.db
|
||||
|
||||
[repository]
|
||||
ROOT = {$ROOT_PATH}/gogs-repositories
|
||||
FORCE_PRIVATE = true
|
||||
|
||||
[server]
|
||||
DOMAIN = 127.0.0.1
|
||||
HTTP_PORT = 3000
|
||||
ROOT_URL = http://127.0.0.1:3000/
|
||||
DISABLE_SSH = true
|
||||
SSH_PORT = 2000
|
||||
START_SSH_SERVER = false
|
||||
OFFLINE_MODE = false
|
||||
LANDING_PAGE = home
|
||||
|
||||
[mailer]
|
||||
ENABLED = false
|
||||
HOST = smtp.163.com:465
|
||||
USER = midoks@163.com
|
||||
PASSWD = ***
|
||||
USE_PLAIN_TEXT = true
|
||||
|
||||
[service]
|
||||
REGISTER_EMAIL_CONFIRM = false
|
||||
ENABLE_NOTIFY_MAIL = false
|
||||
DISABLE_REGISTRATION = true
|
||||
ENABLE_CAPTCHA = true
|
||||
REQUIRE_SIGNIN_VIEW = true
|
||||
|
||||
[picture]
|
||||
DISABLE_GRAVATAR = false
|
||||
ENABLE_FEDERATED_AVATAR = false
|
||||
|
||||
[session]
|
||||
PROVIDER = file
|
||||
|
||||
[log]
|
||||
MODE = console, file
|
||||
LEVEL = Info
|
||||
ROOT_PATH = {$SERVER_PATH}/gogs/log
|
||||
|
||||
[security]
|
||||
INSTALL_LOCK = true
|
||||
SECRET_KEY = jmGSJXDBH5Ng4wt
|
||||
|
||||
|
||||
[other]
|
||||
SHOW_FOOTER_BRANDING = false
|
||||
SHOW_FOOTER_VERSION = false
|
||||
SHOW_FOOTER_TEMPLATE_LOAD_TIME = false
|
||||
|
||||
@@ -1,39 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
echo `date`
|
||||
|
||||
GITADDR="{$GITROOTURL}/{$USERNAME}/{$PROJECT}.git"
|
||||
GIT_SDIR="{$CODE_DIR}"
|
||||
|
||||
GIT_USER_DIR="${GIT_SDIR}/{$USERNAME}"
|
||||
GIT_PROJECT_DIR="${GIT_USER_DIR}/{$PROJECT}"
|
||||
|
||||
|
||||
git config --global credential.helper store
|
||||
|
||||
# echo $GIT_PROJECT_DIR
|
||||
if [ ! -d $GIT_PROJECT_DIR ];then
|
||||
mkdir -p $GIT_USER_DIR && cd $GIT_USER_DIR
|
||||
git clone $GITADDR
|
||||
fi
|
||||
|
||||
unset GIT_DIR
|
||||
cd $GIT_PROJECT_DIR && git pull
|
||||
|
||||
# func 2
|
||||
# cd $GIT_PROJECT_DIR && env -i git pull origin master
|
||||
|
||||
|
||||
#更新的目的地址
|
||||
WEB_PATH={$WEB_ROOT}/{$USERNAME}/{$PROJECT}
|
||||
mkdir -p $WEB_PATH
|
||||
|
||||
rsync -vauP --delete --exclude=".*" $GIT_PROJECT_DIR/ $WEB_PATH
|
||||
|
||||
sysName=`uname`
|
||||
if [ $sysName == 'Darwin' ]; then
|
||||
USER=$(who | sed -n "2,1p" |awk '{print $1}')
|
||||
chown -R $USER:staff $WEB_PATH
|
||||
else
|
||||
chown -R www:www $WEB_PATH
|
||||
fi
|
||||
@@ -1,36 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
echo `date`
|
||||
|
||||
GITADDR="{$GITROOTURL}/{$USERNAME}/{$PROJECT}"
|
||||
GIT_SDIR="{$CODE_DIR}"
|
||||
|
||||
GIT_USER_DIR="${GIT_SDIR}/{$USERNAME}"
|
||||
GIT_PROJECT_DIR="${GIT_USER_DIR}/{$PROJECT}"
|
||||
|
||||
# echo $GIT_PROJECT_DIR
|
||||
if [ ! -d $GIT_PROJECT_DIR ];then
|
||||
mkdir -p $GIT_USER_DIR && cd $GIT_USER_DIR
|
||||
git clone $GITADDR
|
||||
fi
|
||||
|
||||
unset GIT_DIR
|
||||
cd $GIT_PROJECT_DIR && git pull
|
||||
|
||||
# func 2
|
||||
# cd $GIT_PROJECT_DIR && env -i git pull origin master
|
||||
|
||||
|
||||
|
||||
WEB_PATH={$WEB_ROOT}/{$USERNAME}/{$PROJECT}
|
||||
mkdir -p $WEB_PATH
|
||||
|
||||
rsync -vauP --delete --exclude=".*" $GIT_PROJECT_DIR/ $WEB_PATH
|
||||
|
||||
sysName=`uname`
|
||||
if [ $sysName == 'Darwin' ]; then
|
||||
USER=$(who | sed -n "2,1p" |awk '{print $1}')
|
||||
chown -R $USER:staff $WEB_PATH
|
||||
else
|
||||
chown -R www:www $WEB_PATH
|
||||
fi
|
||||
@@ -1,3 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
sh -x {$PATH}/commit 2>{$PATH}/sh.log
|
||||
|
Before Width: | Height: | Size: 1.7 KiB |
@@ -1,23 +0,0 @@
|
||||
<div class="bt-form">
|
||||
<div class="bt-w-main">
|
||||
<div class="bt-w-menu">
|
||||
<p class="bgw" onclick="pluginService('gogs');">服务</p>
|
||||
<p onclick="pluginInitD('gogs');">自启动</p>
|
||||
<!-- <p onclick="pluginConfig('gogs',null, 'init_conf');">启动配置</p> -->
|
||||
<p onclick="gogsEdit();">手动编辑</p>
|
||||
<p onclick="pluginConfig('gogs',null, 'conf');">配置文件</p>
|
||||
<p onclick="gogsSetConfig();">配置修改</p>
|
||||
<p onclick="gogsUserList();">用户列表</p>
|
||||
<p onclick="pluginLogs('gogs',null,'run_log');">运行日志</p>
|
||||
<p onclick="pluginLogs('gogs',null,'post_receive_log');" title="提交post-receive日志">提交日志</p>
|
||||
<p onclick="gogsRead();">使用说明</p>
|
||||
</div>
|
||||
<div class="bt-w-con pd15">
|
||||
<div class="soft-man-con"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script type="text/javascript">
|
||||
$.getScript( "/plugins/file?name=gogs&f=js/gogs.js");
|
||||
pluginService('gogs');
|
||||
</script>
|
||||
@@ -1,754 +0,0 @@
|
||||
# coding: utf-8
|
||||
|
||||
|
||||
import time
|
||||
import os
|
||||
import sys
|
||||
import re
|
||||
|
||||
|
||||
sys.path.append(os.getcwd() + "/class/core")
|
||||
import mw
|
||||
|
||||
|
||||
# cmd = 'ls /usr/local/lib/ | grep python | cut -d \\ -f 1 | awk \'END {print}\''
|
||||
# info = mw.execShell(cmd)
|
||||
# p = "/usr/local/lib/" + info[0].strip() + "/site-packages"
|
||||
# sys.path.append(p)
|
||||
|
||||
import psutil
|
||||
|
||||
|
||||
app_debug = False
|
||||
if mw.isAppleSystem():
|
||||
app_debug = True
|
||||
|
||||
|
||||
def getPluginName():
|
||||
return 'gogs'
|
||||
|
||||
|
||||
def getPluginDir():
|
||||
return mw.getPluginDir() + '/' + getPluginName()
|
||||
|
||||
|
||||
def getServerDir():
|
||||
return mw.getServerDir() + '/' + getPluginName()
|
||||
|
||||
|
||||
def getInitDFile():
|
||||
if app_debug:
|
||||
return '/tmp/' + getPluginName()
|
||||
return '/etc/init.d/' + getPluginName()
|
||||
|
||||
|
||||
def getArgs():
|
||||
args = sys.argv[2:]
|
||||
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(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 getInitdConfTpl():
|
||||
path = getPluginDir() + "/init.d/gogs.tpl"
|
||||
return path
|
||||
|
||||
|
||||
def getInitdConf():
|
||||
path = getServerDir() + "/init.d/gogs"
|
||||
return path
|
||||
|
||||
|
||||
def getConf():
|
||||
path = getServerDir() + "/custom/conf/app.ini"
|
||||
return path
|
||||
|
||||
|
||||
def getConfTpl():
|
||||
path = getPluginDir() + "/conf/app.ini"
|
||||
return path
|
||||
|
||||
|
||||
def status():
|
||||
data = mw.execShell(
|
||||
"ps -ef|grep " + getPluginName() + " |grep -v grep | grep -v python | awk '{print $2}'")
|
||||
if data[0] == '':
|
||||
return 'stop'
|
||||
return 'start'
|
||||
|
||||
|
||||
def getHomeDir():
|
||||
if mw.isAppleSystem():
|
||||
user = mw.execShell(
|
||||
"who | sed -n '2, 1p' |awk '{print $1}'")[0].strip()
|
||||
return '/Users/' + user
|
||||
else:
|
||||
return '/root'
|
||||
|
||||
|
||||
def getRunUser():
|
||||
if mw.isAppleSystem():
|
||||
user = mw.execShell(
|
||||
"who | sed -n '2, 1p' |awk '{print $1}'")[0].strip()
|
||||
return user
|
||||
else:
|
||||
return 'root'
|
||||
|
||||
__SR = '''#!/bin/bash
|
||||
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
|
||||
export PATH
|
||||
export USER=%s
|
||||
export HOME=%s && ''' % ( getRunUser(), getHomeDir())
|
||||
|
||||
|
||||
def contentReplace(content):
|
||||
|
||||
service_path = mw.getServerDir()
|
||||
content = content.replace('{$ROOT_PATH}', mw.getRootDir())
|
||||
content = content.replace('{$SERVER_PATH}', service_path)
|
||||
content = content.replace('{$RUN_USER}', getRunUser())
|
||||
content = content.replace('{$HOME_DIR}', getHomeDir())
|
||||
|
||||
return content
|
||||
|
||||
|
||||
def initDreplace():
|
||||
|
||||
file_tpl = getInitdConfTpl()
|
||||
service_path = mw.getServerDir()
|
||||
|
||||
initD_path = getServerDir() + '/init.d'
|
||||
if not os.path.exists(initD_path):
|
||||
os.mkdir(initD_path)
|
||||
file_bin = initD_path + '/' + getPluginName()
|
||||
|
||||
if not os.path.exists(file_bin):
|
||||
content = mw.readFile(file_tpl)
|
||||
content = contentReplace(content)
|
||||
mw.writeFile(file_bin, content)
|
||||
mw.execShell('chmod +x ' + file_bin)
|
||||
|
||||
# conf_bin = getConf()
|
||||
# if not os.path.exists(conf_bin):
|
||||
# mw.execShell('mkdir -p ' + getServerDir() + '/custom/conf')
|
||||
# conf_tpl = getConfTpl()
|
||||
# content = mw.readFile(conf_tpl)
|
||||
# content = contentReplace(content)
|
||||
# mw.writeFile(conf_bin, content)
|
||||
|
||||
# systemd
|
||||
systemDir = mw.systemdCfgDir()
|
||||
systemService = systemDir + '/gogs.service'
|
||||
systemServiceTpl = getPluginDir() + '/init.d/gogs.service.tpl'
|
||||
if os.path.exists(systemDir) and not os.path.exists(systemService):
|
||||
service_path = mw.getServerDir()
|
||||
se_content = mw.readFile(systemServiceTpl)
|
||||
se_content = se_content.replace('{$SERVER_PATH}', service_path)
|
||||
mw.writeFile(systemService, se_content)
|
||||
mw.execShell('systemctl daemon-reload')
|
||||
|
||||
log_path = getServerDir() + '/log'
|
||||
if not os.path.exists(log_path):
|
||||
os.mkdir(log_path)
|
||||
|
||||
return file_bin
|
||||
|
||||
|
||||
def getRootUrl():
|
||||
content = mw.readFile(getConf())
|
||||
rep = 'ROOT_URL\s*=\s*(.*)'
|
||||
tmp = re.search(rep, content)
|
||||
if tmp:
|
||||
return tmp.groups()[0]
|
||||
|
||||
rep = 'EXTERNAL_URL\s*=\s*(.*)'
|
||||
tmp = re.search(rep, content)
|
||||
if tmp:
|
||||
return tmp.groups()[0]
|
||||
return ''
|
||||
|
||||
|
||||
def getSshPort():
|
||||
content = mw.readFile(getConf())
|
||||
rep = 'SSH_PORT\s*=\s*(.*)'
|
||||
tmp = re.search(rep, content)
|
||||
if not tmp:
|
||||
return ''
|
||||
return tmp.groups()[0]
|
||||
|
||||
|
||||
def getHttpPort():
|
||||
content = mw.readFile(getConf())
|
||||
rep = 'HTTP_PORT\s*=\s*(.*)'
|
||||
tmp = re.search(rep, content)
|
||||
if not tmp:
|
||||
return ''
|
||||
return tmp.groups()[0]
|
||||
|
||||
|
||||
def getRootPath():
|
||||
content = mw.readFile(getConf())
|
||||
rep = 'ROOT\s*=\s*(.*)'
|
||||
tmp = re.search(rep, content)
|
||||
if not tmp:
|
||||
return ''
|
||||
return tmp.groups()[0]
|
||||
|
||||
|
||||
def getDbConfValue():
|
||||
conf = getConf()
|
||||
if not os.path.exists(conf):
|
||||
return {}
|
||||
|
||||
content = mw.readFile(conf)
|
||||
rep_scope = "\[database\](.*?)\["
|
||||
tmp = re.findall(rep_scope, content, re.S)
|
||||
|
||||
rep = '(\w*)\s*=\s*(.*)'
|
||||
tmp = re.findall(rep, tmp[0])
|
||||
r = {}
|
||||
for x in range(len(tmp)):
|
||||
k = tmp[x][0]
|
||||
v = tmp[x][1]
|
||||
r[k] = v
|
||||
return r
|
||||
|
||||
|
||||
def pMysqlDb(conf):
|
||||
host = conf['HOST'].split(':')
|
||||
# pymysql
|
||||
db = mw.getMyORM()
|
||||
# MySQLdb |
|
||||
# db = mw.getMyORMDb()
|
||||
|
||||
db.setPort(int(host[1]))
|
||||
db.setUser(conf['USER'])
|
||||
|
||||
if 'PASSWD' in conf:
|
||||
db.setPwd(conf['PASSWD'])
|
||||
else:
|
||||
db.setPwd(conf['PASSWORD'])
|
||||
|
||||
db.setDbName(conf['NAME'])
|
||||
# db.setSocket(getSocketFile())
|
||||
db.setCharset("utf8")
|
||||
return db
|
||||
|
||||
|
||||
def pSqliteDb(conf):
|
||||
# print(conf)
|
||||
import db
|
||||
psDb = db.Sql()
|
||||
|
||||
# 默认
|
||||
gsdir = getServerDir() + '/data'
|
||||
dbname = 'gogs'
|
||||
if conf['PATH'][0] == '/':
|
||||
# 绝对路径
|
||||
pass
|
||||
else:
|
||||
path = conf['PATH'].split('/')
|
||||
gsdir = getServerDir() + '/' + path[0]
|
||||
dbname = path[1].split('.')[0]
|
||||
|
||||
# print(gsdir, dbname)
|
||||
psDb.dbPos(gsdir, dbname)
|
||||
return psDb
|
||||
|
||||
|
||||
def getGogsDbType(conf):
|
||||
|
||||
if 'DB_TYPE' in conf:
|
||||
return conf['DB_TYPE']
|
||||
|
||||
if 'TYPE' in conf:
|
||||
return conf['TYPE']
|
||||
|
||||
return 'NONE'
|
||||
|
||||
|
||||
def pQuery(sql):
|
||||
conf = getDbConfValue()
|
||||
gtype = getGogsDbType(conf)
|
||||
if gtype == 'sqlite3':
|
||||
db = pSqliteDb(conf)
|
||||
data = db.query(sql, []).fetchall()
|
||||
return data
|
||||
elif gtype == 'mysql':
|
||||
db = pMysqlDb(conf)
|
||||
return db.query(sql)
|
||||
|
||||
print("仅支持mysql|sqlite3配置")
|
||||
exit(0)
|
||||
|
||||
|
||||
def isSqlError(mysqlMsg):
|
||||
# 检测数据库执行错误
|
||||
_mysqlMsg = str(mysqlMsg)
|
||||
# print _mysqlMsg
|
||||
if "MySQLdb" in _mysqlMsg:
|
||||
return mw.returnData(False, 'MySQLdb组件缺失! <br>进入SSH命令行输入: pip install mysql-python')
|
||||
if "2002," in _mysqlMsg:
|
||||
return mw.returnData(False, '数据库连接失败,请检查数据库服务是否启动!')
|
||||
if "using password:" in _mysqlMsg:
|
||||
return mw.returnData(False, '数据库管理密码错误!')
|
||||
if "Connection refused" in _mysqlMsg:
|
||||
return mw.returnData(False, '数据库连接失败,请检查数据库服务是否启动!')
|
||||
if "1133," in _mysqlMsg:
|
||||
return mw.returnData(False, '数据库用户不存在!')
|
||||
if "1007," in _mysqlMsg:
|
||||
return mw.returnData(False, '数据库已经存在!')
|
||||
if "1044," in _mysqlMsg:
|
||||
return mw.returnData(False, mysqlMsg[1])
|
||||
if "2003," in _mysqlMsg:
|
||||
return mw.returnData(False, "Can't connect to MySQL server on '127.0.0.1' (61)")
|
||||
return mw.returnData(True, 'OK')
|
||||
|
||||
|
||||
def gogsOp(method):
|
||||
file = initDreplace()
|
||||
|
||||
if not mw.isAppleSystem():
|
||||
data = mw.execShell('systemctl ' + method + ' gogs')
|
||||
if data[1] == '':
|
||||
return 'ok'
|
||||
return 'fail'
|
||||
|
||||
data = mw.execShell(__SR + file + ' ' + method)
|
||||
if data[1] == '':
|
||||
return 'ok'
|
||||
return data[0]
|
||||
|
||||
|
||||
def start():
|
||||
return gogsOp('start')
|
||||
|
||||
|
||||
def stop():
|
||||
return gogsOp('stop')
|
||||
|
||||
|
||||
def restart():
|
||||
return gogsOp('restart')
|
||||
|
||||
|
||||
def reload():
|
||||
return gogsOp('reload')
|
||||
|
||||
|
||||
def initdStatus():
|
||||
if mw.isAppleSystem():
|
||||
return "Apple Computer does not support"
|
||||
|
||||
shell_cmd = 'systemctl status gogs | 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 gogs')
|
||||
return 'ok'
|
||||
|
||||
|
||||
def initdUinstall():
|
||||
if mw.isAppleSystem():
|
||||
return "Apple Computer does not support"
|
||||
|
||||
mw.execShell('systemctl disable gogs')
|
||||
return 'ok'
|
||||
|
||||
|
||||
def runLog():
|
||||
log_path = getServerDir() + '/log/gogs.log'
|
||||
return log_path
|
||||
|
||||
|
||||
def postReceiveLog():
|
||||
log_path = getServerDir() + '/log/hooks/post-receive.log'
|
||||
return log_path
|
||||
|
||||
|
||||
def getGogsConf():
|
||||
conf = getConf()
|
||||
if not os.path.exists(conf):
|
||||
return mw.returnJson(False, "请先安装初始化!<br/>默认地址:http://" + mw.getLocalIp() + ":3000")
|
||||
|
||||
gets = [
|
||||
{'name': 'DOMAIN', 'type': -1, 'ps': '服务器域名'},
|
||||
{'name': 'ROOT_URL', 'type': -1, 'ps': '公开的完整URL路径'},
|
||||
{'name': 'HTTP_ADDR', 'type': -1, 'ps': '应用HTTP监听地址'},
|
||||
{'name': 'HTTP_PORT', 'type': -1, 'ps': '应用 HTTP 监听端口号'},
|
||||
|
||||
{'name': 'START_SSH_SERVER', 'type': 2, 'ps': '启动内置SSH服务器'},
|
||||
{'name': 'SSH_PORT', 'type': -1, 'ps': 'SSH 端口号'},
|
||||
|
||||
{'name': 'REQUIRE_SIGNIN_VIEW', 'type': 2, 'ps': '强制登录浏览'},
|
||||
{'name': 'ENABLE_CAPTCHA', 'type': 2, 'ps': '启用验证码服务'},
|
||||
{'name': 'DISABLE_REGISTRATION', 'type': 2, 'ps': '禁止注册,只能由管理员创建帐号'},
|
||||
{'name': 'ENABLE_NOTIFY_MAIL', 'type': 2, 'ps': '是否开启邮件通知'},
|
||||
|
||||
{'name': 'FORCE_PRIVATE', 'type': 2, 'ps': '强制要求所有新建的仓库都是私有'},
|
||||
|
||||
{'name': 'SHOW_FOOTER_BRANDING', 'type': 2, 'ps': 'Gogs推广信息'},
|
||||
{'name': 'SHOW_FOOTER_VERSION', 'type': 2, 'ps': 'Gogs版本信息'},
|
||||
{'name': 'SHOW_FOOTER_TEMPLATE_LOAD_TIME', 'type': 2, 'ps': 'Gogs模板加载时间'},
|
||||
]
|
||||
conf = mw.readFile(conf)
|
||||
result = []
|
||||
|
||||
for g in gets:
|
||||
rep = g['name'] + '\s*=\s*(.*)'
|
||||
tmp = re.search(rep, conf)
|
||||
if not tmp:
|
||||
continue
|
||||
g['value'] = tmp.groups()[0]
|
||||
result.append(g)
|
||||
return mw.returnJson(True, 'OK', result)
|
||||
|
||||
|
||||
def submitGogsConf():
|
||||
gets = ['DOMAIN',
|
||||
'ROOT_URL',
|
||||
'HTTP_ADDR',
|
||||
'HTTP_PORT',
|
||||
'START_SSH_SERVER',
|
||||
'SSH_PORT',
|
||||
'REQUIRE_SIGNIN_VIEW',
|
||||
'FORCE_PRIVATE',
|
||||
'ENABLE_CAPTCHA',
|
||||
'DISABLE_REGISTRATION',
|
||||
'ENABLE_NOTIFY_MAIL',
|
||||
'SHOW_FOOTER_BRANDING',
|
||||
'SHOW_FOOTER_VERSION',
|
||||
'SHOW_FOOTER_TEMPLATE_LOAD_TIME']
|
||||
args = getArgs()
|
||||
filename = getConf()
|
||||
conf = mw.readFile(filename)
|
||||
for g in gets:
|
||||
if g in args:
|
||||
rep = g + '\s*=\s*(.*)'
|
||||
val = g + ' = ' + args[g]
|
||||
conf = re.sub(rep, val, conf)
|
||||
mw.writeFile(filename, conf)
|
||||
reload()
|
||||
return mw.returnJson(True, '设置成功')
|
||||
|
||||
|
||||
def userList():
|
||||
|
||||
conf = getConf()
|
||||
if not os.path.exists(conf):
|
||||
return mw.returnJson(False, "请先安装初始化!<br/>默认地址:http://" + mw.getLocalIp() + ":3000")
|
||||
|
||||
conf = getDbConfValue()
|
||||
gtype = getGogsDbType(conf)
|
||||
if gtype != 'mysql':
|
||||
return mw.returnJson(False, "仅支持mysql数据操作!")
|
||||
|
||||
import math
|
||||
args = getArgs()
|
||||
|
||||
data = checkArgs(args, ['page', 'page_size'])
|
||||
if not data[0]:
|
||||
return data[1]
|
||||
|
||||
page = int(args['page'])
|
||||
page_size = int(args['page_size'])
|
||||
search = ''
|
||||
if 'search' in args:
|
||||
search = args['search']
|
||||
|
||||
data = {}
|
||||
|
||||
data['root_url'] = getRootUrl()
|
||||
|
||||
start = (page - 1) * page_size
|
||||
list_count = pQuery('select count(id) as num from user')
|
||||
count = list_count[0]["num"]
|
||||
list_data = pQuery(
|
||||
'select id,name,email from user order by id desc limit ' + str(start) + ',' + str(page_size))
|
||||
data['list'] = mw.getPage({'count': count, 'p': page,
|
||||
'row': page_size, 'tojs': 'gogsUserList'})
|
||||
data['page'] = page
|
||||
data['page_size'] = page_size
|
||||
data['page_count'] = int(math.ceil(count / page_size))
|
||||
data['data'] = list_data
|
||||
return mw.returnJson(True, 'OK', data)
|
||||
|
||||
|
||||
def getAllUserProject(user, search=''):
|
||||
path = getRootPath() + '/' + user
|
||||
dlist = []
|
||||
if os.path.exists(path):
|
||||
for filename in os.listdir(path):
|
||||
tmp = {}
|
||||
filePath = path + '/' + filename
|
||||
if os.path.isdir(filePath):
|
||||
if search == '':
|
||||
tmp['name'] = filename.replace('.git', '')
|
||||
dlist.append(tmp)
|
||||
else:
|
||||
if filename.find(search) != -1:
|
||||
tmp['name'] = filename.replace('.git', '')
|
||||
dlist.append(tmp)
|
||||
return dlist
|
||||
|
||||
|
||||
def checkProjectListIsHasScript(user, data):
|
||||
path = getRootPath() + '/' + user
|
||||
for x in range(len(data)):
|
||||
name = data[x]['name'] + '.git'
|
||||
path_tmp = path + '/' + name + '/custom_hooks/post-receive'
|
||||
if os.path.exists(path_tmp):
|
||||
data[x]['has_hook'] = True
|
||||
else:
|
||||
data[x]['has_hook'] = False
|
||||
return data
|
||||
|
||||
|
||||
def userProjectList():
|
||||
import math
|
||||
args = getArgs()
|
||||
# print args
|
||||
|
||||
page = 1
|
||||
page_size = 5
|
||||
search = ''
|
||||
|
||||
if not 'name' in args:
|
||||
return mw.returnJson(False, '缺少参数name')
|
||||
if 'page' in args:
|
||||
page = int(args['page'])
|
||||
|
||||
if 'page_size' in args:
|
||||
page_size = int(args['page_size'])
|
||||
|
||||
if 'search' in args:
|
||||
search = args['search']
|
||||
|
||||
data = {}
|
||||
|
||||
ulist = getAllUserProject(args['name'])
|
||||
dlist_sum = len(ulist)
|
||||
|
||||
start = (page - 1) * page_size
|
||||
ret_data = ulist[start:start + page_size]
|
||||
ret_data = checkProjectListIsHasScript(args['name'], ret_data)
|
||||
|
||||
data['root_url'] = getRootUrl()
|
||||
data['data'] = ret_data
|
||||
data['args'] = args
|
||||
data['list'] = mw.getPage(
|
||||
{'count': dlist_sum, 'p': page, 'row': page_size, 'tojs': 'userProjectList'})
|
||||
|
||||
return mw.returnJson(True, 'OK', data)
|
||||
|
||||
|
||||
def projectScriptEdit():
|
||||
args = getArgs()
|
||||
|
||||
if not 'user' in args:
|
||||
return mw.returnJson(True, 'username missing')
|
||||
|
||||
if not 'name' in args:
|
||||
return mw.returnJson(True, 'project name missing')
|
||||
|
||||
user = args['user']
|
||||
name = args['name'] + '.git'
|
||||
post_receive = getRootPath() + '/' + user + '/' + name + \
|
||||
'/custom_hooks/commit'
|
||||
if os.path.exists(post_receive):
|
||||
return mw.returnJson(True, 'OK', {'path': post_receive})
|
||||
else:
|
||||
return mw.returnJson(False, 'file does not exist')
|
||||
|
||||
|
||||
def projectScriptLoad():
|
||||
args = getArgs()
|
||||
if not 'user' in args:
|
||||
return mw.returnJson(True, 'username missing')
|
||||
|
||||
if not 'name' in args:
|
||||
return mw.returnJson(True, 'project name missing')
|
||||
|
||||
user = args['user']
|
||||
name = args['name'] + '.git'
|
||||
|
||||
path = getRootPath() + '/' + user + '/' + name
|
||||
post_receive_tpl = getPluginDir() + '/hook/post-receive.tpl'
|
||||
post_receive = path + '/custom_hooks/post-receive'
|
||||
|
||||
if not os.path.exists(path + '/custom_hooks'):
|
||||
mw.execShell('mkdir -p ' + path + '/custom_hooks')
|
||||
|
||||
pct_content = mw.readFile(post_receive_tpl)
|
||||
pct_content = pct_content.replace('{$PATH}', path + '/custom_hooks')
|
||||
mw.writeFile(post_receive, pct_content)
|
||||
mw.execShell('chmod 777 ' + post_receive)
|
||||
|
||||
commit_tpl = getPluginDir() + '/hook/commit.tpl'
|
||||
commit = path + '/custom_hooks/commit'
|
||||
|
||||
codeDir = mw.getRootDir() + '/git'
|
||||
|
||||
cc_content = mw.readFile(commit_tpl)
|
||||
|
||||
gitPath = getRootPath()
|
||||
cc_content = cc_content.replace('{$GITROOTURL}', gitPath)
|
||||
cc_content = cc_content.replace('{$CODE_DIR}', codeDir)
|
||||
cc_content = cc_content.replace('{$USERNAME}', user)
|
||||
cc_content = cc_content.replace('{$PROJECT}', args['name'])
|
||||
cc_content = cc_content.replace('{$WEB_ROOT}', mw.getWwwDir())
|
||||
mw.writeFile(commit, cc_content)
|
||||
mw.execShell('chmod 777 ' + commit)
|
||||
|
||||
return 'ok'
|
||||
|
||||
|
||||
def projectScriptUnload():
|
||||
args = getArgs()
|
||||
data = checkArgs(args, ['user', 'name'])
|
||||
if not data[0]:
|
||||
return data[1]
|
||||
|
||||
user = args['user']
|
||||
name = args['name'] + '.git'
|
||||
|
||||
post_receive = getRootPath() + '/' + user + '/' + name + \
|
||||
'/custom_hooks/post-receive'
|
||||
mw.execShell('rm -f ' + post_receive)
|
||||
|
||||
commit = getRootPath() + '/' + user + '/' + name + \
|
||||
'/custom_hooks/commit'
|
||||
mw.execShell('rm -f ' + commit)
|
||||
return 'ok'
|
||||
|
||||
|
||||
def projectScriptDebug():
|
||||
args = getArgs()
|
||||
data = checkArgs(args, ['user', 'name'])
|
||||
if not data[0]:
|
||||
return data[1]
|
||||
|
||||
user = args['user']
|
||||
name = args['name'] + '.git'
|
||||
commit_log = getRootPath() + '/' + user + '/' + name + \
|
||||
'/custom_hooks/sh.log'
|
||||
|
||||
data = {}
|
||||
if os.path.exists(commit_log):
|
||||
data['status'] = True
|
||||
data['path'] = commit_log
|
||||
else:
|
||||
data['status'] = False
|
||||
data['msg'] = '没有日志文件'
|
||||
|
||||
return mw.getJson(data)
|
||||
|
||||
|
||||
def gogsEdit():
|
||||
data = {}
|
||||
data['post_receive'] = getPluginDir() + '/hook/post-receive.tpl'
|
||||
data['commit'] = getPluginDir() + '/hook/commit.tpl'
|
||||
return mw.getJson(data)
|
||||
|
||||
|
||||
def getRsaPublic():
|
||||
path = getHomeDir()
|
||||
path += '/.ssh/id_rsa.pub'
|
||||
|
||||
content = mw.readFile(path)
|
||||
|
||||
data = {}
|
||||
data['mw'] = content
|
||||
return mw.getJson(data)
|
||||
|
||||
|
||||
def getTotalStatistics():
|
||||
st = status()
|
||||
data = {}
|
||||
if st.strip() == 'start':
|
||||
list_count = pQuery('select count(id) as num from repository')
|
||||
count = list_count[0]["num"]
|
||||
data['status'] = True
|
||||
data['count'] = count
|
||||
data['ver'] = mw.readFile(getServerDir() + '/version.pl').strip()
|
||||
return mw.returnJson(True, 'ok', data)
|
||||
|
||||
data['status'] = False
|
||||
data['count'] = 0
|
||||
return mw.returnJson(False, 'fail', data)
|
||||
|
||||
|
||||
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 == 'run_log':
|
||||
print(runLog())
|
||||
elif func == 'post_receive_log':
|
||||
print(postReceiveLog())
|
||||
elif func == 'conf':
|
||||
print(getConf())
|
||||
elif func == 'init_conf':
|
||||
print(getInitdConf())
|
||||
elif func == 'get_gogs_conf':
|
||||
print(getGogsConf())
|
||||
elif func == 'submit_gogs_conf':
|
||||
print(submitGogsConf())
|
||||
elif func == 'user_list':
|
||||
print(userList())
|
||||
elif func == 'user_project_list':
|
||||
print(userProjectList())
|
||||
elif func == 'project_script_edit':
|
||||
print(projectScriptEdit())
|
||||
elif func == 'project_script_load':
|
||||
print(projectScriptLoad())
|
||||
elif func == 'project_script_unload':
|
||||
print(projectScriptUnload())
|
||||
elif func == 'project_script_debug':
|
||||
print(projectScriptDebug())
|
||||
elif func == 'gogs_edit':
|
||||
print(gogsEdit())
|
||||
elif func == 'get_rsa_public':
|
||||
print(getRsaPublic())
|
||||
elif func == 'get_total_statistics':
|
||||
print(getTotalStatistics())
|
||||
else:
|
||||
print('fail')
|
||||
@@ -1,16 +0,0 @@
|
||||
{
|
||||
"ps": "Gogs是一款极易搭建的自助Git服务",
|
||||
"name": "gogs",
|
||||
"title": "Gogs",
|
||||
"versions": ["0.11.86","0.12.9"],
|
||||
"tip": "soft",
|
||||
"checks": "server/gogs",
|
||||
"path":"server/gogs",
|
||||
"author": "midoks",
|
||||
"date": "2018-04-01",
|
||||
"home": "https://gogs.io",
|
||||
"type": "Git服务器",
|
||||
"shell": "install.sh",
|
||||
"pid": "3",
|
||||
"sort": 7
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
[Unit]
|
||||
Description=Gogs
|
||||
After=network.target
|
||||
|
||||
[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=git
|
||||
Group=git
|
||||
WorkingDirectory={$SERVER_PATH}/gogs
|
||||
ExecStart={$SERVER_PATH}/gogs/gogs web
|
||||
ExecReload=/bin/kill -USR2 $MAINPID
|
||||
Restart=always
|
||||
Environment=USER=git HOME=/home/git
|
||||
|
||||
# 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
|
||||
@@ -1,12 +0,0 @@
|
||||
[Unit]
|
||||
Description=Gogs
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
Type=forking
|
||||
ExecStart={$SERVER_PATH}/gogs/init.d/gogs start
|
||||
ExecStop={$SERVER_PATH}/gogs/init.d/gogs stop
|
||||
RemainAfterExit=yes
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
@@ -1,110 +0,0 @@
|
||||
#!/bin/sh
|
||||
#
|
||||
# /etc/rc.d/init.d/gogs
|
||||
#
|
||||
# Runs the Gogs
|
||||
#
|
||||
#
|
||||
# chkconfig: - 85 15
|
||||
#
|
||||
|
||||
### BEGIN INIT INFO
|
||||
# Provides: gogs
|
||||
# Required-Start: $remote_fs $syslog
|
||||
# Required-Stop: $remote_fs $syslog
|
||||
# Should-Start: mysql postgresql
|
||||
# Should-Stop: mysql postgresql
|
||||
# Default-Start: 2 3 4 5
|
||||
# Default-Stop: 0 1 6
|
||||
# Short-Description: Start gogs at boot time.
|
||||
# Description: Control gogs.
|
||||
### END INIT INFO
|
||||
|
||||
# Source function library.
|
||||
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
|
||||
|
||||
# Default values
|
||||
export HOME={$HOME_DIR}
|
||||
export USER={$RUN_USER}
|
||||
NAME=gogs
|
||||
GOGS_HOME={$SERVER_PATH}/gogs
|
||||
GOGS_PATH=${GOGS_HOME}/$NAME
|
||||
GOGS_USER={$RUN_USER}
|
||||
SERVICENAME="Gogs"
|
||||
LOCKFILE=/tmp/gogs.lock
|
||||
LOGPATH=${GOGS_HOME}/log
|
||||
LOGFILE=${LOGPATH}/gogs.log
|
||||
RETVAL=0
|
||||
|
||||
|
||||
[ -r /etc/sysconfig/$NAME ] && . /etc/sysconfig/$NAME
|
||||
DAEMON_OPTS="--check $NAME"
|
||||
[ ! -z "$GOGS_USER" ] && DAEMON_OPTS="$DAEMON_OPTS --user=${GOGS_USER}"
|
||||
|
||||
|
||||
status(){
|
||||
isStart=`ps -ef|grep 'gogs web' |grep -v grep|awk '{print $2}'`
|
||||
if [ "$isStart" == '' ];then
|
||||
echo -e "${SERVICENAME} not running"
|
||||
else
|
||||
echo -e "${SERVICENAME}(pid $(echo $isStart)) already running"
|
||||
fi
|
||||
}
|
||||
|
||||
start() {
|
||||
isStart=`ps -ef|grep 'gogs web' |grep -v grep|awk '{print $2}'`
|
||||
if [ "$isStart" != '' ];then
|
||||
echo "${SERVICENAME}(pid $(echo $isStart)) already running"
|
||||
return $RETVAL
|
||||
fi
|
||||
|
||||
cd ${GOGS_HOME}
|
||||
echo -e "Starting ${SERVICENAME}: \c"
|
||||
${GOGS_PATH} web > ${LOGFILE} 2>&1 &
|
||||
RETVAL=$?
|
||||
[ $RETVAL = 0 ] && touch ${LOCKFILE} && echo -e "\033[32mdone\033[0m"
|
||||
return $RETVAL
|
||||
}
|
||||
|
||||
stop() {
|
||||
|
||||
pids=`ps -ef|grep 'gogs web' |grep -v grep|awk '{print $2}'`
|
||||
arr=($pids)
|
||||
echo -e "Stopping gogs... \c"
|
||||
for p in ${arr[@]}
|
||||
do
|
||||
kill -9 $p
|
||||
done
|
||||
echo -e "\033[32mdone\033[0m"
|
||||
}
|
||||
|
||||
case "$1" in
|
||||
start)
|
||||
start
|
||||
;;
|
||||
stop)
|
||||
stop
|
||||
;;
|
||||
status)
|
||||
status
|
||||
;;
|
||||
restart)
|
||||
stop
|
||||
start
|
||||
;;
|
||||
reload)
|
||||
stop
|
||||
start
|
||||
;;
|
||||
*)
|
||||
echo "Usage: ${NAME} {start|stop|status|restart}"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
exit $RETVAL
|
||||
@@ -1,105 +0,0 @@
|
||||
#!/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
|
||||
|
||||
|
||||
GOGS_DOWNLOAD='https://dl.gogs.io'
|
||||
|
||||
getOs(){
|
||||
os=`uname`
|
||||
if [ "Darwin" == "$os" ];then
|
||||
echo 'darwin'
|
||||
else
|
||||
echo 'linux'
|
||||
fi
|
||||
return 0
|
||||
}
|
||||
|
||||
getBit(){
|
||||
echo `getconf LONG_BIT`
|
||||
}
|
||||
|
||||
|
||||
Install_gogs()
|
||||
{
|
||||
|
||||
mkdir -p $serverPath/source/gogs
|
||||
|
||||
echo '正在安装脚本文件...' > $install_tmp
|
||||
version=$1
|
||||
os=`getOs`
|
||||
|
||||
# if id git &> /dev/null ;then
|
||||
# echo "git uid is `id -u git`"
|
||||
# echo "git shell is `grep "^git:" /etc/passwd |cut -d':' -f7 `"
|
||||
# else
|
||||
# groupadd git
|
||||
# useradd -g git git
|
||||
# fi
|
||||
|
||||
git config --global push.default simple
|
||||
|
||||
if [ "darwin" == "$os" ];then
|
||||
file=gogs_${version}_darwin_amd64.zip
|
||||
else
|
||||
file=gogs_${version}_linux_amd64.zip
|
||||
fi
|
||||
|
||||
if [ ! -f $serverPath/source/gogs/$file ];then
|
||||
wget --no-check-certificate -O $serverPath/source/gogs/$file ${GOGS_DOWNLOAD}/${version}/${file}
|
||||
fi
|
||||
|
||||
cd $serverPath/source/gogs && unzip -o $file -d gogs_${version}
|
||||
mv $serverPath/source/gogs/gogs_${version}/gogs/ $serverPath/gogs
|
||||
|
||||
|
||||
if [ -d $serverPath/gogs ];then
|
||||
echo $version > $serverPath/gogs/version.pl
|
||||
|
||||
cd ${rootPath} && python3 ${rootPath}/plugins/gogs/index.py start
|
||||
cd ${rootPath} && python3 ${rootPath}/plugins/gogs/index.py initd_install
|
||||
fi
|
||||
# if id -u gogs > /dev/null 2>&1; then
|
||||
# echo "gogs user exists"
|
||||
# else
|
||||
# useradd gogs
|
||||
# cp /etc/sudoers{,.`date +"%Y-%m-%d_%H-%M-%S"`}
|
||||
# echo "gogs ALL=(ALL) NOPASSWD: ALL" >> /etc/sudoers
|
||||
# fi
|
||||
|
||||
echo 'install success' > $install_tmp
|
||||
}
|
||||
|
||||
Uninstall_gogs()
|
||||
{
|
||||
|
||||
if [ -f /usr/lib/systemd/system/gogs.service ];then
|
||||
systemctl stop gogs
|
||||
systemctl disable gogs
|
||||
rm -rf /usr/lib/systemd/system/gogs.service
|
||||
systemctl daemon-reload
|
||||
fi
|
||||
|
||||
if [ -f $serverPath/gogs/initd/gogs ];then
|
||||
$serverPath/gogs/initd/gogs stop
|
||||
fi
|
||||
|
||||
rm -rf $serverPath/gogs
|
||||
echo 'uninstall success' > $install_tmp
|
||||
}
|
||||
|
||||
|
||||
action=$1
|
||||
version=$2
|
||||
if [ "${1}" == 'install' ];then
|
||||
Install_gogs $version
|
||||
else
|
||||
Uninstall_gogs $version
|
||||
fi
|
||||
@@ -1,352 +0,0 @@
|
||||
|
||||
function str2Obj(str){
|
||||
var data = {};
|
||||
kv = str.split('&');
|
||||
for(i in kv){
|
||||
v = kv[i].split('=');
|
||||
data[v[0]] = v[1];
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
function gogsPost(method,args,callback, title){
|
||||
|
||||
var _args = null;
|
||||
if (typeof(args) == 'string'){
|
||||
_args = JSON.stringify(str2Obj(args));
|
||||
} else {
|
||||
_args = JSON.stringify(args);
|
||||
}
|
||||
|
||||
var _title = '正在获取...';
|
||||
if (typeof(title) != 'undefined'){
|
||||
_title = title;
|
||||
}
|
||||
|
||||
var loadT = layer.msg(_title, { icon: 16, time: 0, shade: 0.3 });
|
||||
$.post('/plugins/run', {name:'gogs', 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 gogsSetConfig(){
|
||||
gogsPost('get_gogs_conf', '', function(data){
|
||||
var rrdata = $.parseJSON(data.data);
|
||||
if (!rrdata.status){
|
||||
layer.msg(rrdata.msg,{icon:0,time:2000,shade: [0.3, '#000']});
|
||||
return;
|
||||
}
|
||||
var rdata = rrdata.data;
|
||||
var mlist = '';
|
||||
for (var i = 0; i < rdata.length; i++) {
|
||||
var w = '140';
|
||||
if (rdata[i].name == 'error_reporting') w = '250';
|
||||
var ibody = '<input style="width: ' + w + 'px;" class="bt-input-text mr5" name="' + rdata[i].name + '" value="' + rdata[i].value + '" type="text" >';
|
||||
switch (rdata[i].type) {
|
||||
case 0:
|
||||
var selected_1 = (rdata[i].value == 1) ? 'selected' : '';
|
||||
var selected_0 = (rdata[i].value == 0) ? 'selected' : '';
|
||||
ibody = '<select class="bt-input-text mr5" name="' + rdata[i].name + '" style="width: ' + w + 'px;">\
|
||||
<option value="1" ' + selected_1 + '>开启</option>\
|
||||
<option value="0" ' + selected_0 + '>关闭</option>\
|
||||
</select>';
|
||||
break;
|
||||
case 1:
|
||||
var selected_1 = (rdata[i].value == 'On') ? 'selected' : '';
|
||||
var selected_0 = (rdata[i].value == 'Off') ? 'selected' : '';
|
||||
ibody = '<select class="bt-input-text mr5" name="' + rdata[i].name + '" style="width: ' + w + 'px;">\
|
||||
<option value="On" ' + selected_1 + '>开启</option>\
|
||||
<option value="Off" ' + selected_0 + '>关闭</option></select>'
|
||||
break;
|
||||
case 2:
|
||||
var selected_1 = (rdata[i].value == 'true') ? 'selected' : '';
|
||||
var selected_0 = (rdata[i].value == 'false') ? 'selected' : '';
|
||||
ibody = '<select class="bt-input-text mr5" name="' + rdata[i].name + '" style="width: ' + w + 'px;">\
|
||||
<option value="true" ' + selected_1 + '>开启</option>\
|
||||
<option value="false" ' + selected_0 + '>关闭</option></select>'
|
||||
break;
|
||||
}
|
||||
mlist += '<p><span>' + rdata[i].name + '</span>' + ibody + ', <font>' + rdata[i].ps + '</font></p>'
|
||||
}
|
||||
var html = '<style>.conf_p p{margin-bottom: 2px}</style><div class="conf_p" style="margin-bottom:0">\
|
||||
' + mlist + '\
|
||||
<div style="margin-top:10px; padding-right:15px" class="text-right">\
|
||||
<button class="btn btn-success btn-sm mr5" onclick="gogsSetConfig()">刷新</button>\
|
||||
<button class="btn btn-success btn-sm" onclick="submitGogsConf()">保存</button></div>\
|
||||
</div>';
|
||||
$(".soft-man-con").html(html);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
//提交PHP配置
|
||||
function submitGogsConf() {
|
||||
var data = {
|
||||
DOMAIN: $("input[name='DOMAIN']").val(),
|
||||
ROOT_URL: $("input[name='ROOT_URL']").val(),
|
||||
HTTP_ADDR: $("select[name='HTTP_ADDR']").val(),
|
||||
HTTP_PORT: $("input[name='HTTP_PORT']").val(),
|
||||
START_SSH_SERVER: $("select[name='START_SSH_SERVER']").val() || 'false',
|
||||
SSH_PORT: $("input[name='SSH_PORT']").val(),
|
||||
REQUIRE_SIGNIN_VIEW: $("select[name='REQUIRE_SIGNIN_VIEW']").val() || 'false',
|
||||
ENABLE_CAPTCHA: $("select[name='ENABLE_CAPTCHA']").val() || 'true',
|
||||
DISABLE_REGISTRATION: $("select[name='DISABLE_REGISTRATION']").val() || 'false',
|
||||
ENABLE_NOTIFY_MAIL: $("select[name='ENABLE_NOTIFY_MAIL']").val() || 'false',
|
||||
FORCE_PRIVATE: $("select[name='FORCE_PRIVATE']").val() || 'false',
|
||||
SHOW_FOOTER_BRANDING: $("select[name='SHOW_FOOTER_BRANDING']").val() || 'false',
|
||||
SHOW_FOOTER_VERSION: $("select[name='SHOW_FOOTER_VERSION']").val() || 'false',
|
||||
SHOW_FOOTER_TEMPLATE_LOAD_TIME: $("select[name='SHOW_FOOTER_TEMPLATE_LOAD_TIME']").val() || 'false',
|
||||
};
|
||||
|
||||
gogsPost('submit_gogs_conf', data, function(ret_data){
|
||||
var rdata = $.parseJSON(ret_data.data);
|
||||
layer.msg(rdata.msg, { icon: rdata.status ? 1 : 2 });
|
||||
gogsSetConfig();
|
||||
});
|
||||
}
|
||||
|
||||
function gogsEdit(){
|
||||
|
||||
gogsPost('gogs_edit',{} , function(data){
|
||||
// console.log(data);
|
||||
var rdata = $.parseJSON(data.data);
|
||||
var edit = '<p class="status">通用的手动编辑:</p>';
|
||||
edit +='<div class="sfm-opt">\
|
||||
<button class="btn btn-default btn-sm" onclick="onlineEditFile(0,\''+rdata['post_receive']+'\');">post-receive.tpl</button>\
|
||||
<button class="btn btn-default btn-sm" onclick="onlineEditFile(0,\''+rdata['commit']+'\');">commit.tpl</button>\
|
||||
</div>';
|
||||
$(".soft-man-con").html(edit);
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
function gogsUserList(page, search) {
|
||||
|
||||
var _data = {};
|
||||
if (typeof(page) =='undefined'){
|
||||
var page = 1;
|
||||
}
|
||||
|
||||
_data['page'] = page;
|
||||
_data['page_size'] = 10;
|
||||
if(typeof(search) != 'undefined'){
|
||||
_data['search'] = search;
|
||||
}
|
||||
|
||||
gogsPost('user_list', _data, function(data){
|
||||
|
||||
var rdata = $.parseJSON(data.data);
|
||||
if (!rdata.status){
|
||||
layer.msg(rdata.msg,{icon:0,time:2000,shade: [0.3, '#000']});
|
||||
return;
|
||||
}
|
||||
content = '<div class="finduser"><input class="bt-input-text mr5 outline_no" type="text" placeholder="查找用户名" id="find_user" style="height: 28px; border-radius: 3px;width: 435px;">';
|
||||
content += '<button class="btn btn-success btn-sm" onclick="userFind();">查找</button></div>';
|
||||
|
||||
content += '<div class="divtable" style="margin-top:5px;"><table class="table table-hover" width="100%" cellspacing="0" cellpadding="0" border="0">';
|
||||
content += '<thead><tr>';
|
||||
content += '<th>序号</th>';
|
||||
content += '<th>用户或组织</th>';
|
||||
content += '<th>邮件地址</th>';
|
||||
content += '<th>操作(<a href="'+rdata['data']['root_url']+'" class="btlink" target="_blank">WEB管理</a>)</th>';
|
||||
content += '</tr></thead>';
|
||||
|
||||
content += '<tbody>';
|
||||
|
||||
ulist = rdata['data']['data'];
|
||||
for (i in ulist){
|
||||
email = ulist[i]["email"] == '' ? '无' : ulist[i]["email"];
|
||||
content += '<tr><td>'+ulist[i]["id"]+'</td>'+
|
||||
'<td>'+ulist[i]["name"]+'</td>'+
|
||||
'<td>'+email+'</td>'+
|
||||
'<td><a class="btlink" onclick="userProjectList(\''+ulist[i]["name"]+'\')">项目管理</a></td>'+
|
||||
'</tr>';
|
||||
}
|
||||
|
||||
content += '</tbody>';
|
||||
content += '</table></div>';
|
||||
|
||||
var page = '<div class="dataTables_paginate paging_bootstrap pagination" style="margin-top:0px;"><ul id="softPage" class="page"><div>';
|
||||
page += rdata['data']['list'];
|
||||
page += '</div></ul></div>';
|
||||
|
||||
content += page;
|
||||
|
||||
$(".soft-man-con").html(content);
|
||||
});
|
||||
}
|
||||
|
||||
function userProjectList(user, search){
|
||||
var req = {};
|
||||
if (!isNaN(user)){
|
||||
req['page'] = user;
|
||||
req['name'] = user = getCookie('gogsUserSelected');
|
||||
} else {
|
||||
req['page'] = 1;
|
||||
req['name'] = user;
|
||||
setCookie('gogsUserSelected', user);
|
||||
}
|
||||
|
||||
req['page_size'] = 5;
|
||||
req['search'] = '';
|
||||
if(typeof(search) != 'undefined'){
|
||||
req['search'] = search;
|
||||
}
|
||||
|
||||
$('.layui-layer-close1').click();
|
||||
gogsPost('user_project_list', req, function(data){
|
||||
var rdata = [];
|
||||
try {
|
||||
rdata = $.parseJSON(data.data);
|
||||
} catch(e){}
|
||||
|
||||
if (!rdata['status']){
|
||||
layer.msg(rdata['msg'], { icon: 2 });
|
||||
return;
|
||||
}
|
||||
|
||||
var list = '';
|
||||
// console.log(rdata);
|
||||
var project_list = rdata['data']['data'];
|
||||
for (i in project_list) {
|
||||
var name = project_list[i]['name'];
|
||||
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>\
|
||||
</td>\
|
||||
</tr>';
|
||||
}
|
||||
|
||||
var page = '<div class="dataTables_paginate paging_bootstrap pagination" style="margin-top:0px;"><ul id="softPage" class="page"><div>';
|
||||
page += rdata['data']['list'];
|
||||
page += '</div></ul></div>';
|
||||
|
||||
var loadOpen = layer.open({
|
||||
type: 1,
|
||||
title: '用户('+user+')项目列表',
|
||||
area: '500px',
|
||||
content:"<div class='bt-form pd20 c6'>\
|
||||
<div>\
|
||||
<div class='divtable' style='margin-top:5px;'>\
|
||||
<table class='table table-hover'>\
|
||||
<thead><tr><th>项目</th><th>操作</th></tr></thead>\
|
||||
<tbody>" + list + "</tbody>\
|
||||
</table>" +
|
||||
page +
|
||||
"</div></div></div>"
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
function projectScript(user, name,has_hook){
|
||||
// console.log(user,name,has_hook);
|
||||
var html = '';
|
||||
if (has_hook){
|
||||
html += '<button onclick="projectScriptEdit(\''+user+'\',\''+name+'\')" class="btn btn-default btn-sm">手动编辑</button>';
|
||||
html += '<button onclick="projectScriptDebug(\''+user+'\',\''+name+'\')" class="btn btn-default btn-sm">调试日志</button>';
|
||||
html += '<button onclick="projectScriptLoad(\''+user+'\',\''+name+'\')" class="btn btn-default btn-sm">重新加载</button>';
|
||||
html += '<button onclick="projectScriptUnload(\''+user+'\',\''+name+'\')" class="btn btn-default btn-sm">卸载脚本</button>';
|
||||
} else {
|
||||
html += '<button onclick="projectScriptLoad(\''+user+'\',\''+name+'\')" class="btn btn-default btn-sm">加载脚本</button>';
|
||||
}
|
||||
|
||||
var loadOpen = layer.open({
|
||||
type: 1,
|
||||
title: '['+user+']['+name+']脚本设置',
|
||||
area: '240px',
|
||||
content:'<div class="change-default pd20">'+html+'</div>'
|
||||
});
|
||||
}
|
||||
|
||||
function projectScriptEdit(user,name){
|
||||
gogsPost('project_script_edit', {'user':user,'name':name}, function(data){
|
||||
var rdata = $.parseJSON(data.data);
|
||||
if (rdata['status']){
|
||||
onlineEditFile(0, rdata['data']['path']);
|
||||
} else {
|
||||
layer.msg(rdata.msg,{icon:1,time:2000,shade: [0.3, '#000']});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function projectScriptLoad(user,name){
|
||||
gogsPost('project_script_load', {'user':user,'name':name}, function(data){
|
||||
if (data.data != 'ok'){
|
||||
layer.msg(data.data,{icon:0,time:2000,shade: [0.3, '#000']});
|
||||
return;
|
||||
}
|
||||
|
||||
layer.msg('加载成功!',{icon:1,time:2000,shade: [0.3, '#000']});
|
||||
setTimeout(function(){
|
||||
userProjectList(1);
|
||||
}, 2000);
|
||||
});
|
||||
}
|
||||
|
||||
function projectScriptUnload(user,name){
|
||||
gogsPost('project_script_unload', {'user':user,'name':name}, function(data){
|
||||
if (data.data != 'ok'){
|
||||
layer.msg(data.data,{icon:0,time:2000,shade: [0.3, '#000']});
|
||||
return;
|
||||
}
|
||||
|
||||
layer.msg('卸载成功!',{icon:1,time:2000,shade: [0.3, '#000']});
|
||||
setTimeout(function(){
|
||||
userProjectList(1);
|
||||
}, 2000);
|
||||
});
|
||||
}
|
||||
|
||||
function projectScriptDebug(user,name){
|
||||
gogsPost('project_script_debug', {'user':user,'name':name}, function(data){
|
||||
var rdata = $.parseJSON(data.data);
|
||||
if (rdata['status']){
|
||||
onlineEditFile(0, rdata['path']);
|
||||
} else {
|
||||
layer.msg(rdata.msg,{icon:1,time:2000,shade: [0.3, '#000']});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function getRsaPublic(){
|
||||
gogsPost('get_rsa_public', {}, function(data){
|
||||
var rdata = $.parseJSON(data.data);
|
||||
var con = '<div class="tab-con">\
|
||||
<div class="myKeyCon ptb15">\
|
||||
<textarea style="margin:0px;width:580px;height:110px;outline:none;" spellcheck="false">'+rdata.mw+'</textarea>\
|
||||
</div>\
|
||||
<ul class="help-info-text c7 pull-left"></ul>\
|
||||
</div>'
|
||||
layer.open({
|
||||
type: 1,
|
||||
area: "600px",
|
||||
title: '本机公钥',
|
||||
closeBtn: 2,
|
||||
shift: 5,
|
||||
shadeClose: false,
|
||||
content:con
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function gogsRead(){
|
||||
|
||||
var readme = '<ul class="help-info-text c7">';
|
||||
readme += '<li>默认使用MySQL,第一个启动加载各种配置,并修改成正确的数据库配置</li>';
|
||||
readme += '<li>邮件端口使用456,gogs仅支持使用STARTTLS的SMTP协议</li>';
|
||||
readme += '<li>如果使用项目中脚本本地同步,<a target="_blank" href="https://github.com/midoks/mdserver-web/wiki/%E6%8F%92%E4%BB%B6%E7%AE%A1%E7%90%86%5Bgogs%E4%BD%BF%E7%94%A8%E8%AF%B4%E6%98%8E%5D#%E5%90%AF%E5%8A%A8gogs%E5%90%8E%E5%A6%82%E6%9E%9C%E8%A6%81%E4%BD%BF%E7%94%A8hook%E8%84%9A%E6%9C%AC%E5%90%8C%E6%AD%A5%E4%BB%A3%E7%A0%81%E9%9C%80%E8%A6%81%E5%BC%80%E5%90%AFssh%E7%AB%AF%E5%8F%A3">点击查看</></li>';
|
||||
readme += '<li><a href="#" onclick="getRsaPublic();">点击查看本机公钥</></li>';
|
||||
readme += '</ul>';
|
||||
|
||||
$('.soft-man-con').html(readme);
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
global
|
||||
daemon
|
||||
log 127.0.0.1 local0
|
||||
pidfile /tmp/haproxy.pid
|
||||
maxconn 4000
|
||||
user haproxy
|
||||
group haproxy
|
||||
|
||||
|
||||
defaults
|
||||
mode http
|
||||
log global
|
||||
option httplog
|
||||
timeout connect 10s
|
||||
timeout client 15s
|
||||
timeout server 15s
|
||||
|
||||
|
||||
listen stats
|
||||
mode http
|
||||
bind *:10800
|
||||
stats enable
|
||||
stats uri /haproxy
|
||||
stats realm Haproxy\ Statistics
|
||||
stats auth admin:admin
|
||||
|
||||
frontend http-in
|
||||
bind *:1080
|
||||
default_backend servers
|
||||
option forwardfor
|
||||
option httpclose
|
||||
|
||||
backend servers
|
||||
balance roundrobin
|
||||
server web1 0.0.0.0:9090 check
|
||||
|
||||
|
||||
|
Before Width: | Height: | Size: 3.5 KiB |
@@ -1,18 +0,0 @@
|
||||
<div class="bt-form">
|
||||
<div class="bt-w-main">
|
||||
<div class="bt-w-menu">
|
||||
<p class="bgw" onclick="pluginService('haproxy');">服务</p>
|
||||
<p onclick="pluginInitD('haproxy');">自启动</p>
|
||||
<p onclick="pluginConfigTpl('haproxy');">配置修改</p>
|
||||
<p onclick="pluginLogs('haproxy','','run_log',30);">运行日志</p>
|
||||
</div>
|
||||
<div class="bt-w-con pd15">
|
||||
<div class="soft-man-con"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script type="text/javascript">
|
||||
$.getScript( "/plugins/file?name=haproxy&f=js/haproxy.js", function(){
|
||||
pluginService('haproxy');
|
||||
});
|
||||
</script>
|
||||
@@ -1,253 +0,0 @@
|
||||
# coding:utf-8
|
||||
|
||||
import sys
|
||||
import io
|
||||
import os
|
||||
import time
|
||||
import re
|
||||
import string
|
||||
import subprocess
|
||||
|
||||
sys.path.append(os.getcwd() + "/class/core")
|
||||
import mw
|
||||
|
||||
app_debug = False
|
||||
if mw.isAppleSystem():
|
||||
app_debug = True
|
||||
|
||||
|
||||
def getPluginName():
|
||||
return 'haproxy'
|
||||
|
||||
|
||||
def getPluginDir():
|
||||
return mw.getPluginDir() + '/' + getPluginName()
|
||||
|
||||
|
||||
def getServerDir():
|
||||
return mw.getServerDir() + '/' + getPluginName()
|
||||
|
||||
|
||||
def getInitDFile():
|
||||
if app_debug:
|
||||
return '/tmp/' + getPluginName()
|
||||
return '/etc/init.d/' + getPluginName()
|
||||
|
||||
|
||||
def getConfTpl():
|
||||
path = getPluginDir() + "/conf/haproxy.conf"
|
||||
return path
|
||||
|
||||
|
||||
def getConf():
|
||||
path = getServerDir() + "/haproxy.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('}')
|
||||
t = t.split(':')
|
||||
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 configTpl():
|
||||
path = getPluginDir() + '/tpl'
|
||||
pathFile = os.listdir(path)
|
||||
tmp = []
|
||||
for one in pathFile:
|
||||
file = path + '/' + one
|
||||
tmp.append(file)
|
||||
return mw.getJson(tmp)
|
||||
|
||||
|
||||
def readConfigTpl():
|
||||
args = getArgs()
|
||||
data = checkArgs(args, ['file'])
|
||||
if not data[0]:
|
||||
return data[1]
|
||||
|
||||
content = mw.readFile(args['file'])
|
||||
content = contentReplace(content)
|
||||
return mw.returnJson(True, 'ok', content)
|
||||
|
||||
|
||||
def contentReplace(content):
|
||||
service_path = mw.getServerDir()
|
||||
content = content.replace('{$ROOT_PATH}', mw.getRootDir())
|
||||
content = content.replace('{$SERVER_PATH}', service_path)
|
||||
content = content.replace('{$SERVER_APP}', service_path + '/haproxy')
|
||||
return content
|
||||
|
||||
|
||||
def status():
|
||||
data = mw.execShell(
|
||||
"ps -ef|grep haproxy |grep -v grep | grep -v python | awk '{print $2}'")
|
||||
if data[0] == '':
|
||||
return 'stop'
|
||||
return 'start'
|
||||
|
||||
|
||||
def initDreplace():
|
||||
|
||||
file_tpl = getInitDTpl()
|
||||
service_path = os.path.dirname(os.getcwd())
|
||||
|
||||
initD_path = getServerDir() + '/init.d'
|
||||
if not os.path.exists(initD_path):
|
||||
os.mkdir(initD_path)
|
||||
file_bin = initD_path + '/' + getPluginName()
|
||||
|
||||
# initd replace
|
||||
if not os.path.exists(file_bin):
|
||||
content = mw.readFile(file_tpl)
|
||||
content = contentReplace(content)
|
||||
mw.writeFile(file_bin, content)
|
||||
mw.execShell('chmod +x ' + file_bin)
|
||||
|
||||
# config replace
|
||||
conf_bin = getConf()
|
||||
if not os.path.exists(conf_bin):
|
||||
conf_content = mw.readFile(getConfTpl())
|
||||
conf_content = contentReplace(conf_content)
|
||||
mw.writeFile(getServerDir() + '/haproxy.conf', conf_content)
|
||||
|
||||
# systemd
|
||||
systemDir = mw.systemdCfgDir()
|
||||
systemService = systemDir + '/haproxy.service'
|
||||
systemServiceTpl = getPluginDir() + '/init.d/haproxy.service.tpl'
|
||||
if os.path.exists(systemDir) and not os.path.exists(systemService):
|
||||
service_path = mw.getServerDir()
|
||||
se_content = mw.readFile(systemServiceTpl)
|
||||
se_content = se_content.replace('{$SERVER_PATH}', service_path)
|
||||
mw.writeFile(systemService, se_content)
|
||||
mw.execShell('systemctl daemon-reload')
|
||||
|
||||
return file_bin
|
||||
|
||||
|
||||
def haOp(method):
|
||||
file = initDreplace()
|
||||
|
||||
if not mw.isAppleSystem():
|
||||
data = mw.execShell('systemctl ' + method + ' haproxy')
|
||||
if data[1] == '':
|
||||
return 'ok'
|
||||
return 'fail'
|
||||
|
||||
data = mw.execShell(file + ' ' + method)
|
||||
if data[1] == '':
|
||||
return 'ok'
|
||||
return data[1]
|
||||
|
||||
|
||||
def start():
|
||||
return haOp('start')
|
||||
|
||||
|
||||
def stop():
|
||||
return haOp('stop')
|
||||
|
||||
|
||||
def restart():
|
||||
return haOp('restart')
|
||||
|
||||
|
||||
def reload():
|
||||
return haOp('reload')
|
||||
|
||||
|
||||
def initdStatus():
|
||||
if mw.isAppleSystem():
|
||||
return "Apple Computer does not support"
|
||||
|
||||
shell_cmd = 'systemctl status haproxy | 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 haproxy')
|
||||
return 'ok'
|
||||
|
||||
|
||||
def initdUinstall():
|
||||
if mw.isAppleSystem():
|
||||
return "Apple Computer does not support"
|
||||
|
||||
mw.execShell('systemctl disable haproxy')
|
||||
return 'ok'
|
||||
|
||||
|
||||
def runLog():
|
||||
path = getConf()
|
||||
content = mw.readFile(path)
|
||||
rep = 'log\s*=\s*(.*)'
|
||||
tmp = re.search(rep, content)
|
||||
return tmp.groups()[0]
|
||||
|
||||
|
||||
def getPort():
|
||||
path = getConf()
|
||||
content = mw.readFile(path)
|
||||
rep = 'listen\s*=\s*(.*)'
|
||||
tmp = re.search(rep, content)
|
||||
return tmp.groups()[0]
|
||||
|
||||
|
||||
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 == 'config_tpl':
|
||||
print(configTpl())
|
||||
elif func == 'read_config_tpl':
|
||||
print(readConfigTpl())
|
||||
elif func == 'run_log':
|
||||
print(runLog())
|
||||
elif func == 'query_log':
|
||||
print(queryLog())
|
||||
else:
|
||||
print('error')
|
||||
@@ -1,19 +0,0 @@
|
||||
{
|
||||
"sort": 7,
|
||||
"ps": "可靠、高性能的TCP/HTTP负载平衡器",
|
||||
"name": "haproxy",
|
||||
"title": "haproxy",
|
||||
"shell": "install.sh",
|
||||
"versions":["2.6"],
|
||||
"updates":["2.6"],
|
||||
"tip": "soft",
|
||||
"checks": "server/haproxy",
|
||||
"path": "server/haproxy",
|
||||
"display": 1,
|
||||
"author": "midoks",
|
||||
"date": "2022-08-26",
|
||||
"home": "https://www.haproxy.org/",
|
||||
"doc1": "https://www.haproxy.org/",
|
||||
"type": 0,
|
||||
"pid": "4"
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
[Unit]
|
||||
Description=he Reliable, High Performance TCP/HTTP Load Balancer
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
Type=forking
|
||||
ExecStartPre={$SERVER_PATH}/haproxy/sbin/haproxy -c -f {$SERVER_PATH}/haproxy/haproxy.conf
|
||||
ExecStart={$SERVER_PATH}/haproxy/sbin/haproxy -D -f {$SERVER_PATH}/haproxy/haproxy.conf
|
||||
ExecReload={$SERVER_PATH}/haproxy/sbin/haproxy -f {$SERVER_PATH}/haproxy/haproxy.conf -c -q
|
||||
Restart=on-failure
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
@@ -1,119 +0,0 @@
|
||||
#!/bin/sh
|
||||
#
|
||||
# chkconfig: - 85 15
|
||||
# description: HA-Proxy is a TCP/HTTP reverse proxy which is particularly suited \
|
||||
# for high availability environments.
|
||||
# processname: haproxy
|
||||
# config: /etc/haproxy/haproxy.cfg
|
||||
# pidfile: /var/run/haproxy.pid
|
||||
# Script Author: Simon Matter <simon.matter@invoca.ch>
|
||||
# Version: 2004060600
|
||||
# Source function library.
|
||||
|
||||
if [ -f /etc/init.d/functions ]; then
|
||||
. /etc/init.d/functions
|
||||
elif [ -f /etc/rc.d/init.d/functions ] ; then
|
||||
. /etc/rc.d/init.d/functions
|
||||
else
|
||||
echo ".."
|
||||
fi
|
||||
|
||||
# Source networking configuration.
|
||||
if [ -f /etc/sysconfig/network ];then
|
||||
. /etc/sysconfig/network
|
||||
fi
|
||||
|
||||
# Check that networking is up.
|
||||
# [ ${NETWORKING} = "no" ] && exit 0
|
||||
|
||||
HAPROXYDIR={$SERVER_PATH}/haproxy
|
||||
BASENAME=haproxy
|
||||
|
||||
# This is our service name
|
||||
|
||||
#BASENAME=`basename $0`
|
||||
#if [ -L $0 ]; then
|
||||
# BASENAME=`find $0 -name $BASENAME -printf %l`
|
||||
# BASENAME=`basename $BASENAME`
|
||||
#fi
|
||||
|
||||
[ -f $HAPROXYDIR/etc/$BASENAME.conf ] || exit 1
|
||||
|
||||
RETVAL=0
|
||||
|
||||
start() {
|
||||
$HAPROXYDIR/sbin/$BASENAME -c -q -f $HAPROXYDIR/etc/$BASENAME.conf
|
||||
|
||||
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "Errors found in configuration file, check it with '$BASENAME check'."
|
||||
return 1
|
||||
fi
|
||||
|
||||
echo -n "Starting $BASENAME: "
|
||||
daemon $HAPROXYDIR/sbin/$BASENAME -D -f $HAPROXYDIR/etc/$BASENAME.conf
|
||||
RETVAL=$?
|
||||
# echo
|
||||
# [ $RETVAL -eq 0 ] && touch /var/lock/subsys/$BASENAME
|
||||
return $RETVAL
|
||||
}
|
||||
|
||||
|
||||
stop() {
|
||||
|
||||
echo -n "Shutting down $BASENAME: "
|
||||
killproc $BASENAME -USR1
|
||||
|
||||
RETVAL=$?
|
||||
echo
|
||||
[ $RETVAL -eq 0 ] && rm -f /var/lock/subsys/$BASENAME
|
||||
[ $RETVAL -eq 0 ] && rm -f /var/run/$BASENAME.pid
|
||||
return $RETVAL
|
||||
}
|
||||
|
||||
restart() {
|
||||
$HAPROXYDIR/sbin/$BASENAME -c -q -f $HAPROXYDIR/etc/$BASENAME.conf
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "Errors found in configuration file, check it with '$BASENAME check'."
|
||||
return 1
|
||||
fi
|
||||
stop
|
||||
start
|
||||
}
|
||||
|
||||
reload() {
|
||||
$HAPROXYDIR/sbin/$BASENAME -c -q -f $HAPROXYDIR/etc/$BASENAME.conf
|
||||
if [ $? -ne 0 ]; then
|
||||
echo "Errors found in configuration file, check it with '$BASENAME check'."
|
||||
return 1
|
||||
fi
|
||||
$HAPROXYDIR/sbin/$BASENAME -D -f $HAPROXYDIR/$BASENAME.conf -p /var/run/$BASENAME.pid -sf $(cat /var/run/$BASENAME.pid)
|
||||
}
|
||||
|
||||
check() {
|
||||
$HAPROXYDIR/sbin/$BASENAME -c -q -V -f $HAPROXYDIR/etc/$BASENAME.conf
|
||||
}
|
||||
|
||||
rhstatus() {
|
||||
status $BASENAME
|
||||
}
|
||||
|
||||
condrestart() {
|
||||
[ -e /var/lock/subsys/$BASENAME ] && restart || :
|
||||
}
|
||||
|
||||
# See how we were called.
|
||||
case "$1" in
|
||||
start) start ;;
|
||||
stop) stop ;;
|
||||
restart) restart;;
|
||||
reload) reload;;
|
||||
condrestart) condrestart ;;
|
||||
status) rhstatus ;;
|
||||
check) check ;;
|
||||
*)
|
||||
echo $"Usage: $BASENAME {start|stop|restart|reload|condrestart|status|check}"
|
||||
exit 1
|
||||
esac
|
||||
|
||||
exit $?
|
||||
@@ -1,52 +0,0 @@
|
||||
#!/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
|
||||
|
||||
echo $action $type
|
||||
|
||||
if id haproxy &> /dev/null ;then
|
||||
echo "haproxy UID is `id -u haproxy`"
|
||||
echo "haproxy Shell is `grep "^haproxy:" /etc/passwd |cut -d':' -f7 `"
|
||||
else
|
||||
groupadd haproxy
|
||||
useradd -g haproxy haproxy
|
||||
fi
|
||||
|
||||
if [ "${2}" == "" ];then
|
||||
echo '缺少安装脚本...' > $install_tmp
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [ ! -d $curPath/versions/$2 ];then
|
||||
echo '缺少安装脚本2...' > $install_tmp
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [ "${action}" == "uninstall" ];then
|
||||
|
||||
if [ -f /usr/lib/systemd/system/haproxy.service ] || [ -f /lib/systemd/system/haproxy.service ];then
|
||||
systemctl stop haproxy
|
||||
systemctl disable haproxy
|
||||
rm -rf /usr/lib/systemd/system/haproxy.service
|
||||
rm -rf /lib/systemd/system/haproxy.service
|
||||
systemctl daemon-reload
|
||||
fi
|
||||
fi
|
||||
|
||||
sh -x $curPath/versions/$2/install.sh $1
|
||||
|
||||
if [ "${action}" == "install" ] && [ -d $serverPath/haproxy ];then
|
||||
#初始化
|
||||
cd ${rootPath} && python3 ${rootPath}/plugins/haproxy/index.py start ${type}
|
||||
cd ${rootPath} && python3 ${rootPath}/plugins/haproxy/index.py initd_install ${type}
|
||||
fi
|
||||
@@ -1,53 +0,0 @@
|
||||
function spPostMin(method, args, callback){
|
||||
|
||||
var req_data = {};
|
||||
req_data['name'] = 'haproxy';
|
||||
req_data['func'] = method;
|
||||
|
||||
if (typeof(args) != 'undefined' && args!=''){
|
||||
req_data['args'] = JSON.stringify(args);
|
||||
}
|
||||
|
||||
$.post('/plugins/run', req_data, 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 spPost(method, args, callback){
|
||||
var loadT = layer.msg('正在获取...', { icon: 16, time: 0, shade: 0.3 });
|
||||
spPostMin(method,args,function(data){
|
||||
layer.close(loadT);
|
||||
if(typeof(callback) == 'function'){
|
||||
callback(data);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
function secToTime(s) {
|
||||
var t;
|
||||
if(s > -1){
|
||||
var hour = Math.floor(s/3600);
|
||||
var min = Math.floor(s/60) % 60;
|
||||
var sec = s % 60;
|
||||
if(hour < 10) {
|
||||
t = '0'+ hour + ":";
|
||||
} else {
|
||||
t = hour + ":";
|
||||
}
|
||||
|
||||
if(min < 10){t += "0";}
|
||||
t += min + ":";
|
||||
if(sec < 10){t += "0";}
|
||||
t += sec.toFixed(2);
|
||||
}
|
||||
return t;
|
||||
}
|
||||
|
||||
@@ -1,70 +0,0 @@
|
||||
#!/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")
|
||||
sysName=`uname`
|
||||
|
||||
install_tmp=${rootPath}/tmp/mw_install.pl
|
||||
|
||||
|
||||
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}'`
|
||||
|
||||
|
||||
VERSION=2.6.4
|
||||
MIN_VERSION=2.6
|
||||
Install_App()
|
||||
{
|
||||
|
||||
echo '正在安装脚本文件...' > $install_tmp
|
||||
mkdir -p $serverPath/haproxy
|
||||
|
||||
APP_DIR=${serverPath}/source/haproxy
|
||||
mkdir -p $APP_DIR
|
||||
|
||||
if [ ! -f ${APP_DIR}/haproxy-${VERSION}.tar.gz ];then
|
||||
if [ $sysName == 'Darwin' ]; then
|
||||
wget --no-check-certificate -O ${APP_DIR}/haproxy-${VERSION}.tar.gz https://www.haproxy.org/download/${MIN_VERSION}/src/haproxy-${VERSION}.tar.gz
|
||||
else
|
||||
curl -sSLo ${APP_DIR}/haproxy-${VERSION}.tar.gz https://www.haproxy.org/download/${MIN_VERSION}/src/haproxy-${VERSION}.tar.gz
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ ! -f ${APP_DIR}/haproxy-${VERSION}.tar.gz ];then
|
||||
curl -sSLo ${APP_DIR}/haproxy-${VERSION}.tar.gz https://www.haproxy.org/download/${MIN_VERSION}/src/haproxy-${VERSION}.tar.gz
|
||||
fi
|
||||
|
||||
|
||||
cd ${APP_DIR} && tar -zxvf haproxy-${VERSION}.tar.gz
|
||||
|
||||
if [ "$OSNAME" == "macos" ];then
|
||||
cd ${APP_DIR}/haproxy-${VERSION} && make TARGET=osx && make install PREFIX=$serverPath/haproxy
|
||||
else
|
||||
cd ${APP_DIR}/haproxy-${VERSION} && make TARGET=linux-glibc && make install PREFIX=$serverPath/haproxy
|
||||
fi
|
||||
|
||||
echo $MIN_VERSION > $serverPath/haproxy/version.pl
|
||||
echo 'Install_HA' > $install_tmp
|
||||
}
|
||||
|
||||
Uninstall_App()
|
||||
{
|
||||
if [ -f $serverPath/haproxy/initd/haproxy ];then
|
||||
$serverPath/haproxy/initd/haproxy stop
|
||||
fi
|
||||
|
||||
rm -rf $serverPath/haproxy
|
||||
echo "Uninstall_HA" > $install_tmp
|
||||
}
|
||||
|
||||
action=$1
|
||||
if [ "${1}" == 'install' ];then
|
||||
Install_App
|
||||
else
|
||||
Uninstall_App
|
||||
fi
|
||||
@@ -1,201 +0,0 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
|
Before Width: | Height: | Size: 4.1 KiB |
@@ -1,20 +0,0 @@
|
||||
<div class="bt-form">
|
||||
<div class="bt-w-main">
|
||||
<div class="bt-w-menu">
|
||||
<p class="bgw" onclick="pluginService('imail');">服务</p>
|
||||
<p onclick="pluginInitD('imail');">自启动</p>
|
||||
<p onclick="pluginConfig('imail',null, 'conf');">配置文件</p>
|
||||
<p onclick="pluginLogs('imail',null,'run_log', 50);">运行日志</p>
|
||||
</div>
|
||||
<div class="bt-w-con pd15">
|
||||
<div class="soft-man-con"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script type="text/javascript">
|
||||
$.getScript( "/plugins/file?name=imail&f=js/imail.js", function(){
|
||||
mail.init();
|
||||
pluginService('imail');
|
||||
});
|
||||
</script>
|
||||
@@ -1,218 +0,0 @@
|
||||
# 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/imail'
|
||||
__SR = ''
|
||||
|
||||
def __init__(self):
|
||||
self.__setupPath = self.getServerDir()
|
||||
|
||||
self.__SR = '''#!/bin/bash
|
||||
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
|
||||
export PATH
|
||||
export USER=%s
|
||||
export HOME=%s && ''' % ( self.getRunUser(), self.getHomeDir())
|
||||
|
||||
def getArgs(self):
|
||||
args = sys.argv[3:]
|
||||
tmp = {}
|
||||
args_len = len(args)
|
||||
|
||||
if args_len == 1:
|
||||
t = args[0].strip('{').strip('}')
|
||||
t = t.split(':')
|
||||
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 __release_port(self, port):
|
||||
from collections import namedtuple
|
||||
try:
|
||||
import firewall_api
|
||||
firewall_api.firewall_api().addAcceptPortArgs(port, 'IMail-Server', 'port')
|
||||
return port
|
||||
except Exception as e:
|
||||
return "Release failed {}".format(e)
|
||||
|
||||
def openPort(self):
|
||||
for i in ["25", "110", "143", "465", "995", "993", "587"]:
|
||||
self.__release_port(i)
|
||||
return True
|
||||
|
||||
def getPluginName(self):
|
||||
return 'imail'
|
||||
|
||||
def getPluginDir(self):
|
||||
return mw.getPluginDir() + '/' + self.getPluginName()
|
||||
|
||||
def getServerDir(self):
|
||||
return mw.getServerDir() + '/' + self.getPluginName()
|
||||
|
||||
def getInitdConfTpl(self):
|
||||
path = self.getPluginDir() + "/init.d/imail.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):
|
||||
data = mw.execShell(
|
||||
"ps -ef|grep " + self.getPluginName() + " |grep -v grep | grep -v python | awk '{print $2}'")
|
||||
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 + '/imail.service'
|
||||
systemServiceTpl = self.getPluginDir() + '/init.d/imail.service.tpl'
|
||||
if os.path.exists(systemDir) and not os.path.exists(systemService):
|
||||
service_path = mw.getServerDir()
|
||||
se_content = mw.readFile(systemServiceTpl)
|
||||
se_content = se_content.replace('{$SERVER_PATH}', service_path)
|
||||
mw.writeFile(systemService, se_content)
|
||||
mw.execShell('systemctl daemon-reload')
|
||||
|
||||
log_path = self.getServerDir() + '/logs'
|
||||
if not os.path.exists(log_path):
|
||||
os.mkdir(log_path)
|
||||
|
||||
return file_bin
|
||||
|
||||
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(self.__SR + 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 initd_status(self):
|
||||
if mw.isAppleSystem():
|
||||
return "Apple Computer does not support"
|
||||
|
||||
cmd = 'systemctl status imail | grep loaded | grep "enabled;"'
|
||||
data = mw.execShell(cmd)
|
||||
if data[0] == '':
|
||||
return 'fail'
|
||||
return 'ok'
|
||||
|
||||
def initd_install(self):
|
||||
if mw.isAppleSystem():
|
||||
return "Apple Computer does not support"
|
||||
|
||||
mw.execShell('systemctl enable imail')
|
||||
return 'ok'
|
||||
|
||||
def initd_uinstall(self):
|
||||
if mw.isAppleSystem():
|
||||
return "Apple Computer does not support"
|
||||
|
||||
mw.execShell('systemctl disable imail')
|
||||
return 'ok'
|
||||
|
||||
def conf(self):
|
||||
conf_path = self.getServerDir() + '/custom/conf/app.conf'
|
||||
if not os.path.exists(conf_path):
|
||||
return mw.returnJson(False, "请先安装初始化!<br/>默认地址:http://" + mw.getLocalIp() + ":1080")
|
||||
|
||||
return self.getServerDir() + '/custom/conf/app.conf'
|
||||
|
||||
def run_log(self):
|
||||
ilog = self.getServerDir() + '/logs/imail.log'
|
||||
if not os.path.exists(ilog):
|
||||
return mw.returnJson(False, "请先安装初始化!<br/>默认地址:http://" + mw.getLocalIp() + ":1080")
|
||||
|
||||
return self.getServerDir() + '/logs/imail.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))
|
||||
@@ -1,18 +0,0 @@
|
||||
{
|
||||
"sort": 7,
|
||||
"ps": "简单邮件服务[DEV]",
|
||||
"name": "imail",
|
||||
"title": "邮件服务",
|
||||
"shell": "install.sh",
|
||||
"versions":["0.0.19"],
|
||||
"updates":["0.0.19"],
|
||||
"tip": "soft",
|
||||
"checks": "server/imail",
|
||||
"path": "server/imail",
|
||||
"display": 1,
|
||||
"author": "midoks",
|
||||
"date": "2022-09-26",
|
||||
"home": "https://github.com/midoks/mdserver-web",
|
||||
"type": 0,
|
||||
"pid": "5"
|
||||
}
|
||||
@@ -1,12 +0,0 @@
|
||||
[Unit]
|
||||
Description=Imail Simple Mail Server
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
Type=forking
|
||||
ExecStart={$SERVER_PATH}/imail/init.d/imail start
|
||||
ExecStop={$SERVER_PATH}/imail/init.d/imail stop
|
||||
RemainAfterExit=yes
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
@@ -1,89 +0,0 @@
|
||||
#!/bin/bash
|
||||
# chkconfig: 2345 55 25
|
||||
# description: Imail Service
|
||||
|
||||
### BEGIN INIT INFO
|
||||
# Provides: bt
|
||||
# Required-Start: $all
|
||||
# Required-Stop: $all
|
||||
# Default-Start: 2 3 4 5
|
||||
# Default-Stop: 0 1 6
|
||||
# Short-Description: starts Imail
|
||||
# Description: starts the Imail
|
||||
### END INIT INFO
|
||||
|
||||
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
|
||||
|
||||
# Source function library.
|
||||
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
|
||||
|
||||
app_path={$SERVER_PATH}/imail
|
||||
SERVICENAME="imail"
|
||||
|
||||
im_start(){
|
||||
isStart=`ps -ef|grep 'imail service' |grep -v grep|awk '{print $2}'`
|
||||
if [ "$isStart" == '' ];then
|
||||
echo -e "Starting imail... \c"
|
||||
cd $app_path && ${app_path}/imail service &
|
||||
isStart=""
|
||||
while [[ "$isStart" == "" ]];
|
||||
do
|
||||
echo -e ".\c"
|
||||
sleep 0.5
|
||||
isStart=$(lsof -n -P -i:25|grep LISTEN|grep -v grep|awk '{print $2}'|xargs)
|
||||
let n+=1
|
||||
if [ $n -gt 15 ];then
|
||||
break;
|
||||
fi
|
||||
done
|
||||
if [ "$isStart" == '' ];then
|
||||
echo -e "\033[31mfailed\033[0m"
|
||||
echo '------------------------------------------------------'
|
||||
tail -n 20 ${app_path}/logs/run_away.log
|
||||
echo '------------------------------------------------------'
|
||||
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
|
||||
}
|
||||
|
||||
im_stop(){
|
||||
pids=`ps -ef|grep 'imail service' |grep -v grep|awk '{print $2}'`
|
||||
arr=($pids)
|
||||
echo -e "Stopping ${SERVICENAME}... \c"
|
||||
for p in ${arr[@]}
|
||||
do
|
||||
kill -9 $p
|
||||
done
|
||||
echo -e "\033[32mdone\033[0m"
|
||||
}
|
||||
|
||||
im_status(){
|
||||
isStart=`ps -ef|grep 'imail service' |grep -v grep|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') im_start;;
|
||||
'stop') im_stop;;
|
||||
'status') im_status;;
|
||||
'reload')
|
||||
im_stop
|
||||
im_start;;
|
||||
'restart')
|
||||
im_stop
|
||||
im_start;;
|
||||
esac
|
||||
@@ -1,25 +0,0 @@
|
||||
#!/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
|
||||