mirror of
https://github.com/midoks/mdserver-web
synced 2026-11-06 02:55:59 +08:00
Compare commits
5422 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
abc1d04e5e | ||
| fec638007c | |||
| 80f7a0de38 | |||
| a3bb9ac80a | |||
| 3dcc6e3349 | |||
| 60870a4e5d | |||
| ea635bc960 | |||
| 749c8443ee | |||
| 052120c25e | |||
| a2b3b86c43 | |||
| 6d57e3dd40 | |||
| a67689d890 | |||
| b6bffedf06 | |||
| e07eaf4226 | |||
| dd9e9d4899 | |||
| cd1d80915a | |||
| 40f64ab3e8 | |||
| 69ba921ec4 | |||
| 255fbcd688 | |||
| 24e12b70c8 | |||
| 0feee48ea8 | |||
| 5cd8c5ae2c | |||
| 28a75eaedc | |||
| 50d0794f4a | |||
| 755e53f4d7 | |||
| 1876027bd2 | |||
| 26b06f9bb2 | |||
| 8f67d35260 | |||
| 7fa78c78e2 | |||
| ae7276300f | |||
| 420816ff4e | |||
| df34a7ef9b | |||
| e1b199c8d5 | |||
|
|
e6060847ef | ||
| 7ff1530684 | |||
| cd0ad7488e | |||
| 6a0f81154a | |||
|
|
096c736c28 | ||
| 7d01a487dc | |||
| ba95ef1537 | |||
| 01035018c4 | |||
| 8aa12b979a | |||
| 98dc9e5bd8 | |||
| 4ebadc28c9 | |||
| 02115226fa | |||
| f661aea83c | |||
| cfd3abf97b | |||
| b5d1c8cfcf | |||
| 98156cc1cb | |||
| 33a0dc6014 | |||
| 95a6f86e5d | |||
| 74ac55b01d | |||
| 518cc78670 | |||
| 43727decf0 | |||
| 599b3a7f4a | |||
| bf6c18c57a | |||
| 34838bb238 | |||
| 1a356e516c | |||
| 15e273aa6f | |||
|
|
4c3d3bf41d | ||
| f782bacc64 | |||
| 7e62ab62f9 | |||
| aaa8644348 | |||
| 53797c7f56 | |||
| c9302f406e | |||
| 7ad881c8ad | |||
| 015e57f96d | |||
| 84b94a172e | |||
| c1380945ef | |||
| 2d80df003e | |||
| 568cdaa0ad | |||
| d11e4807df | |||
|
|
f6bc08cec1 | ||
| d723afb96e | |||
|
|
6c2aa089f3 | ||
| 35e1ff2e96 | |||
| bee9e89ed5 | |||
| d0b622906f | |||
| 56f7edcb55 | |||
| 4acbc0363f | |||
| 79013db685 | |||
|
|
eb5b5e5ce8 | ||
| b9e6469635 | |||
| 56ab927f8c | |||
| 3d74320e76 | |||
| 0371eaa37a | |||
| 713dbfcda5 | |||
| 93ef2848ed | |||
| 8750cf0f74 | |||
| af32a1e54d | |||
| 2a7c63029a | |||
| f8fe7bea19 | |||
| ef2814bafe | |||
| 220fef5f4d | |||
| 2835c111d0 | |||
| 6d0dcf3112 | |||
| 28307ce8e8 | |||
| 282fbed37d | |||
| 101b6d06bc | |||
| 8de4c74696 | |||
| ba62972820 | |||
| ec512f50af | |||
| 3261f54649 | |||
| ec38ba9e21 | |||
| 9e1dc94a08 | |||
| 40a76ac81e | |||
| 721245b9ad | |||
| cb4ae9b74f | |||
|
|
5524ecf4e0 | ||
| dbb2718892 | |||
| 88fc903d80 | |||
|
|
242c30f7cb | ||
| c944b7c585 | |||
| dc2e2695be | |||
| f5b86ea189 | |||
| b6c2f7f3a1 | |||
| 81003f75b7 | |||
| d6dc330083 | |||
| fd0cbe10fc | |||
| 4ed77e82be | |||
|
|
de0e89f056 | ||
| 29da5146f6 | |||
| e951089b45 | |||
| 909d65ad83 | |||
| 27cac55b8f | |||
| 0ee6ae3713 | |||
| fb97160cfb | |||
| 96b00526fe | |||
| 909fe6d0f1 | |||
| f2e9f4c5e8 | |||
| 3b629b1296 | |||
| 5cee415030 | |||
|
|
141e52d42a | ||
| 1b21f3c814 | |||
| 4ddc2fef8e | |||
| 18b259a5a5 | |||
| 2e2288a691 | |||
| d03ae1db7b | |||
| 418355a571 | |||
| 340b53aa62 | |||
| dd0628a7fb | |||
| 3e5b48d2ad | |||
|
|
815148a722 | ||
| 47aadfd877 | |||
| 42bfd9a934 | |||
| 55df1b7613 | |||
| 6bc3fc5707 | |||
| 19771181dd | |||
| f0b65332bd | |||
| 7dd8a1eb4f | |||
| f584c18d7e | |||
| b023044473 | |||
| dfa7ffdb53 | |||
| a5a6eb7e5f | |||
| eac88f1d26 | |||
| 518f693948 | |||
| 43684e9b20 | |||
| 59a7bb5584 | |||
| f623fd9dcf | |||
| 9db36d45af | |||
| ff5670797a | |||
| 54ad7e5fd0 | |||
| 2970aa4b8c | |||
| d2e5751542 | |||
| ba5f6f4958 | |||
| b220552f92 | |||
| d02981cee5 | |||
| 96d5246415 | |||
| ea284049c0 | |||
| 5c44bb10d1 | |||
| c7c834fb75 | |||
| 2ade5e490c | |||
| 6c61d3f3c0 | |||
| ab19878a80 | |||
| c27abfde3c | |||
| 411b21cdec | |||
| 26e21e631d | |||
|
|
653068f2c2 | ||
| 188c63826f | |||
| 1820fc724f | |||
| 645519b983 | |||
| f97e015bbc | |||
| 9d47a385f5 | |||
| ca06c68d24 | |||
| 841873418c | |||
| 05a6f093ab | |||
| a916104d33 | |||
| 4609c360ac | |||
| b3acf3647f | |||
| 400c9d1276 | |||
|
|
59bd655bb1 | ||
| 8227253b74 | |||
| 4e5604dc27 | |||
| 2e238e6982 | |||
| c5df0f256c | |||
| 6bd73131ce | |||
| 309422a41c | |||
| 6aa7d9dc20 | |||
| 565e1cc882 | |||
|
|
5c1e292da9 | ||
| 6db1ee133d | |||
|
|
383cec2ee5 | ||
| dfa2163407 | |||
| ed9a9b7860 | |||
| 5af49dc081 | |||
| d4f425abf8 | |||
| 1867ee01f1 | |||
| 81fe83e0a1 | |||
| f266ec86c0 | |||
| e5d6357fae | |||
| 7967b36088 | |||
| d7471fe882 | |||
| b4b67c8f48 | |||
| 63ae895f12 | |||
| 5dd77ee747 | |||
| 6071dfa5f1 | |||
| 11d8d813d2 | |||
| f337a60dc1 | |||
| 33f66ab3b1 | |||
| eb71732d20 | |||
| fbc9340960 | |||
| a2f14f4121 | |||
| ce8c7474ce | |||
| 85d9f1409a | |||
| c29dfee004 | |||
| 618f938462 | |||
| 4e7927f9bc | |||
| 212f29438d | |||
| 3b6d9c3310 | |||
| c01cb01d71 | |||
| 712863b766 | |||
| 601a80950f | |||
| 9f46ceac33 | |||
| ad9b9be7fe | |||
| 2be6ac36ef | |||
| fdb8ca205f | |||
| f1024c165a | |||
| 9efc85f10d | |||
| dd592b40ee | |||
| 9103646150 | |||
| cae2680862 | |||
| e3099a946b | |||
| 3f1da50025 | |||
| fbd70eb4f9 | |||
| ef0d6d5265 | |||
| 1e6932cd01 | |||
| d173586d49 | |||
| 717269dadc | |||
| 21c4f93e02 | |||
| c36a03bfb3 | |||
| a71927917c | |||
| 2f5d5835c5 | |||
| e95b42df77 | |||
| 5c6dbbbb0c | |||
| a151516eab | |||
| 3a590c7feb | |||
| e9f0e1962e | |||
| c4fda1362d | |||
| f31343b2a5 | |||
| 90b414d373 | |||
| 3eab70db4c | |||
| e0810bb8d6 | |||
| 96a32d5c8f | |||
| d45cfb5fe8 | |||
| 08753ae96d | |||
| 465e55848f | |||
| 4492a97155 | |||
| ba46d568bf | |||
| d87d5d5349 | |||
| 76ea3e3375 | |||
| ef47145135 | |||
| 43a9ee7368 | |||
| 6646e0c46c | |||
| 535dbb2e37 | |||
| 035953c7f5 | |||
| 6a9ce81d46 | |||
| f2a6824632 | |||
| 594976373e | |||
| 45704f1c31 | |||
| ce57885343 | |||
| 4d143ef959 | |||
| 9a0f9a759c | |||
| 6341f00f46 | |||
| f517568251 | |||
| 1ff6d0c2cb | |||
| 5e28039573 | |||
|
|
d7757bee0d | ||
| 6b18511708 | |||
| 4a3e0791d3 | |||
| 7c25d3f6c3 | |||
| 8ef38674d7 | |||
| e84cbf1d76 | |||
| 14f622d252 | |||
| 8bb2bfacdc | |||
| 191c4b1331 | |||
| 7bed2b977e | |||
| b6c09b2b37 | |||
|
|
1bd5251dc0 | ||
| ecf1b6b600 | |||
| 22cca82967 | |||
| cd66e42b1a | |||
| d887fa1b50 | |||
| 55870ca08a | |||
| 2ce1d49386 | |||
| a0226ccbcd | |||
| de7c14ec62 | |||
| 0c2cfd6d9a | |||
| 4ca78b9a91 | |||
| 8219c2d2ed | |||
| 9f22ce949c | |||
| d76c21c54b | |||
| 30c5cee81a | |||
| e2d0ac6d89 | |||
| 3578a65d6e | |||
| c8331b800b | |||
| 1e7dc18c21 | |||
| 046da1868e | |||
| ef7068ebd2 | |||
| 0525667032 | |||
| 6198ca02da | |||
| 4d4fd90fc6 | |||
| acfc4d710b | |||
| 4fd12f4d73 | |||
| 9a8bb9f94b | |||
| 170f758bc9 | |||
| 8b7d563fac | |||
| 6f9e0cdd8f | |||
| 0a3af7a0c0 | |||
| c90a2facac | |||
| 92d56bddab | |||
| a3bab2da47 | |||
| d51a3ac1f7 | |||
| 04774bddc3 | |||
| 770f1532da | |||
| d09872c2f3 | |||
| e77369cd25 | |||
| 69897a208c | |||
| 1f5c5ac66e | |||
| 3a7f8f09cb | |||
| 77d2b9e323 | |||
| d56c271cc4 | |||
| b4aecf5d0c | |||
| e55e23c845 | |||
| e2977dbbc4 | |||
| 8ad94ffeff | |||
| c94ff04dcf | |||
| 65f10ca22f | |||
| a1127b3bc0 | |||
| 81c16d1a3d | |||
| b1b4c82457 | |||
| 515b22b103 | |||
| c200165057 | |||
| 64c30893d5 | |||
| 98b8dce5fd | |||
| 0d9b7b6edf | |||
| b72bb1fa87 | |||
| d85131266c | |||
| 22da275f5b | |||
| f6d65b9e33 | |||
| ebc07becd0 | |||
| 024f2c2369 | |||
| 2b93fb684b | |||
| b2daf6bf65 | |||
| 3ee0f553e9 | |||
| f206da8278 | |||
| edbaeb37f5 | |||
| 46847833d0 | |||
| 4303a62b1b | |||
| d6d5babf81 | |||
| 8e5b10bda3 | |||
| 8e54ee987c | |||
| 929b30ce73 | |||
| c085fa7066 | |||
| 783e71f189 | |||
| ae1ffd313c | |||
| 122c0b5f64 | |||
| 21e496f22e | |||
| d92b3039e2 | |||
| ea57812191 | |||
| d72238df45 | |||
| 55aab8d86d | |||
| f8d4d9e4cf | |||
| 90afa6446c | |||
| 5e468c548a | |||
| 48f4521ef6 | |||
| ccd48cecfb | |||
| 0757638b2e | |||
| 68cb547d5f | |||
| 1f7c494d82 | |||
|
|
8e58783964 | ||
| bfdc0639b6 | |||
| 337a7d87e4 | |||
| 38c062220c | |||
| f639acec86 | |||
| 02ba38a01a | |||
| ea8487b14c | |||
|
|
f0b613865b | ||
| b142f37045 | |||
| 45eeb3c045 | |||
| ab99d972cd | |||
| 08562a12a2 | |||
| d7e62bbfa1 | |||
| 5829c48995 | |||
| cd133fc278 | |||
| 8a2a4d1a84 | |||
|
|
a225b98432 | ||
| 1acd50b3f3 | |||
| 5279fe0ba7 | |||
| a73f7d86a9 | |||
| 5fe7e4b43d | |||
| d50d666fb2 | |||
| 86229586f1 | |||
| 2947dea3dd | |||
| 264534ff2c | |||
| 82655592d7 | |||
|
|
85f90e8ce6 | ||
| 450ae37f05 | |||
| 07d0eff75b | |||
| 8697bea0d3 | |||
| a06839fe20 | |||
| 6542113a21 | |||
| 63d04b8c75 | |||
| 71b409a970 | |||
| e0bcc720cc | |||
| 8bcabd055b | |||
| 94597476ac | |||
| a5172d4b5a | |||
| ee79ea55db | |||
| 2230c2b632 | |||
| 9b231554ef | |||
| ccc499359b | |||
| c5ba0dd8a2 | |||
| 3811979f7e | |||
| fb17b7e3a1 | |||
| 6ea79b8b28 | |||
| 037e360e3d | |||
| 3e4a995d5a | |||
| 9d7056184a | |||
| 8aad54b2eb | |||
| 9639dc0e19 | |||
|
|
1f816c4c09 | ||
| d88c3d812e | |||
| de3f060e8b | |||
| 2c740726e2 | |||
| 01fcadf7c9 | |||
| 21378014c4 | |||
| 765e3d7d1a | |||
| 2bd77e5830 | |||
| b5c61af33c | |||
| ac7df862c1 | |||
| ae30e8c740 | |||
| db4cd40e35 | |||
| 592cdc3e17 | |||
|
|
9a84276abf | ||
| 1c70d6381b | |||
| 556da39be8 | |||
| 59c8d02c7d | |||
| 3b0b53d1b5 | |||
| 996cbfb279 | |||
| e47ad14cb1 | |||
| 8921647180 | |||
|
|
2d7933e715 | ||
| cffe463779 | |||
| 2a0e7c090d | |||
| eab8ed9551 | |||
|
|
e9c3b5d135 | ||
| 59076d4750 | |||
| 8aa84d22a8 | |||
| cfb26034b1 | |||
| 953980ee47 | |||
| 994198517a | |||
| 98728b2eb8 | |||
| 6498606e69 | |||
|
|
d17ba4d785 | ||
| e4eeed3559 | |||
| b100204b37 | |||
| 82ff3e3d0a | |||
| 0b2223bce0 | |||
| a43b723b09 | |||
| 96551f49ca | |||
| 8e32d37985 | |||
| d1da238ddc | |||
| 7260a2be21 | |||
| 7562d50e58 | |||
| e77ae8763b | |||
| a0e51c75fe | |||
| 6021a5e6e2 | |||
| e50e222955 | |||
| 531dd665c9 | |||
| a79c0a1a9b | |||
| 9ef144c304 | |||
| 5402af8df7 | |||
| 99802125dc | |||
| 705647cd3b | |||
| 0ea2eeab7f | |||
| 727cde1352 | |||
| 6ef6358cbb | |||
| 6acb303ac9 | |||
| a80e5492b8 | |||
| 4358b1a520 | |||
| ad1a7c22a6 | |||
| 2f3af23e6d | |||
| 89a26d294d | |||
| f83faeacac | |||
| fb7c27c205 | |||
| 3f958c7083 | |||
| 60b6689318 | |||
| ba94e81367 | |||
| 89c8c68b88 | |||
| e78ae7e4de | |||
| f87cadd3ce | |||
| 1631e149ea | |||
| d84f585bff | |||
| 7e9091b341 | |||
| 7020d3e198 | |||
| 9d8e0ded8f | |||
| 090e8fa160 | |||
| 83746675e7 | |||
| 74d73d76cd | |||
| 6bfedde66d | |||
| 554bee4040 | |||
| a1005adf04 | |||
| cbed47d825 | |||
| abb7da896b | |||
| e62a69ac45 | |||
| 769285736b | |||
| 8d2eaa94bd | |||
| a31143e0e8 | |||
| 584551f764 | |||
| 8b60a65bbd | |||
| c13df7c25b | |||
| ad30ba776c | |||
| de3451993f | |||
| fde9b49f39 | |||
| cff0e9442c | |||
| 5cb57d89de | |||
| fadcbaa1bb | |||
| 911554f6df | |||
| 77b525d955 | |||
| b872a2bb53 | |||
| 818444d4f4 | |||
| 63e16e05aa | |||
| 06743894a3 | |||
| 2d665235c8 | |||
| 72311a2801 | |||
| d7ba290ed3 | |||
| e0d2984bae | |||
| ecdbe8d014 | |||
| 7d57e320b8 | |||
| cf16b729f6 | |||
| 366c5c91aa | |||
| 173cf2985e | |||
| 0bdba73f25 | |||
| 24d25e69ad | |||
| d3e6297503 | |||
| 76b5f15115 | |||
| cfa92b637a | |||
| f07e75e508 | |||
| 4220300731 | |||
|
|
43b464866f | ||
| 7bf5dbb4f3 | |||
| 9fe8453898 | |||
| b421cf261f | |||
| 75d28763ee | |||
| 25533fa8bc | |||
| 04294e6cbe | |||
| f0550ba6b3 | |||
| 88d0733d95 | |||
| 74633e155a | |||
| 7d8d7a8c6f | |||
|
|
32956578b2 | ||
| 4efdf7606c | |||
| a61644861b | |||
| e60570e380 | |||
| ad160cb23e | |||
| 73f58ced9a | |||
| 36ca82c1b4 | |||
| acd46f4724 | |||
| 370ff99134 | |||
| aa317ee2f5 | |||
| 44600b5657 | |||
| ea45316a32 | |||
| 6cd13022f1 | |||
| 91ff2f7550 | |||
| 483b5b090d | |||
| 6087931c58 | |||
| 9014fd8911 | |||
| 76f7f51d80 | |||
| 0c4902e01e | |||
| 175fb089cd | |||
| 7b8a4a38b3 | |||
| ebf920ed12 | |||
| bb8591f5e5 | |||
| b8573e1f1f | |||
| dacc1e5ad8 | |||
| 8cc64e32dd | |||
| 1d294cc3b1 | |||
| f9da2e4391 | |||
| 3b78079cd2 | |||
| 07a0a1be48 | |||
| 837f95e571 | |||
|
|
909d818caa | ||
|
|
355ceebbd7 | ||
| 6ae1370709 | |||
|
|
93b5d8b8b4 | ||
|
|
be461a15a9 | ||
| c239f35382 | |||
| 49d57af719 | |||
|
|
19fc0fdc51 | ||
| c1fd4fa24c | |||
| 7239b22759 | |||
| f5fa94ae95 | |||
|
|
b679ea035d | ||
| 49bbca8faa | |||
| 8b86cd5e63 | |||
| 698e3ffd81 | |||
|
|
c2284dcee5 | ||
| b68c9edc3e | |||
| 650365997a | |||
| 3279404595 | |||
| d32a94dccf | |||
| 80d846f247 | |||
| eadcbe336d | |||
| 474d0f075f | |||
| 2d6a41e19f | |||
| 1f24944417 | |||
| 4ab3409e54 | |||
| c3ad146303 | |||
| 745cd304fb | |||
| e6efefd1fd | |||
| 1350404c58 | |||
| 9cdd2274fa | |||
| cc204ad721 | |||
| 8eb1614b31 | |||
| 9c2e9627ec | |||
| 87123c526a | |||
| 7c3ebc6e35 | |||
| 6c5bf63424 | |||
| 13fd47e7d4 | |||
| fc1321584a | |||
| bab3ae16d3 | |||
| ab8498e519 | |||
| 0e480c7ad8 | |||
| 7ed8558ff0 | |||
| 4ed2f0caa9 | |||
| f275eabe1b | |||
| fe13ab1e5f | |||
| 6ed045cf94 | |||
| ef333ff142 | |||
| dac2f5240f | |||
| cebfc2fc91 | |||
| 1da865c1b2 | |||
| 2abdaaa7ce | |||
| c62142cf6b | |||
| 7c1820b6cd | |||
| a24af95d24 | |||
| 1326c282c8 | |||
| b7e84749f1 | |||
| 8a65158540 | |||
| a576c521a2 | |||
| 736a387a71 | |||
| 090205c372 | |||
| fda0bddc8f | |||
| 292b18468d | |||
| 47aab245fc | |||
| 39bd601825 | |||
| b744adeb13 | |||
| 7b70f9fc99 | |||
|
|
0723f751b3 | ||
| 1fd8617a12 | |||
|
|
2fec8fcef4 | ||
|
|
51a48afa71 | ||
|
|
76919faca7 | ||
|
|
9bcbeaf1a5 | ||
| d0de121721 | |||
| 957e2db3d0 | |||
| fb0be5549d | |||
| 8a9132d557 | |||
| 04d10435c2 | |||
| 92543e10fa | |||
| cd427dcbf3 | |||
|
|
ddb5e0285b | ||
| 75610a4559 | |||
| b8f7a45f61 | |||
|
|
37d5ce719e | ||
| ef9053aa7e | |||
| 018fcb7afb | |||
| 5cc9a67152 | |||
| 54895f1d0a | |||
| 91dfecf882 | |||
| 2f09e227d1 | |||
| 5d0463836e | |||
|
|
fb6499cb35 | ||
| 0bbfb58e67 | |||
|
|
fcd780a7d3 | ||
| 4f4d1e7580 | |||
| 6e2a8e0a12 | |||
| 817b4b36c7 | |||
|
|
0ba89cbb9e | ||
| 4e3e7864be | |||
| 6f51eaa298 | |||
| 39bd87e868 | |||
| 18443aab60 | |||
| b88ea7aa3c | |||
| 6a6f6e6062 | |||
| 5839dbcd6b | |||
| b46f45569e | |||
| 5cdc5af8cd | |||
| 4f38a9f3ff | |||
| 9c83695187 | |||
| f9be534101 | |||
| 58ab48ac15 | |||
| f34e93192d | |||
| 31106f2b13 | |||
| 468cc54cf5 | |||
| 728a38c55c | |||
|
|
b2b23b0e3b | ||
| 167dd11548 | |||
| 03fdbf2604 | |||
| edbdec8370 | |||
| 8f1c9f9471 | |||
| d35d16943d | |||
| 3ac4270dee | |||
| 90777e08ff | |||
| b47d1262ac | |||
| e28081a7c5 | |||
| 5f7d6c1488 | |||
| 59385e11be | |||
|
|
a668d88791 | ||
| e62bd7c5a6 | |||
| 1ad3d2f187 | |||
| bf06af14ed | |||
|
|
805c96f260 | ||
| 8ccba2f6d5 | |||
| 2e9f01d3ee | |||
| d1dcdd4f25 | |||
| 60958d1403 | |||
| 5530b14f36 | |||
| 64c9942ee1 | |||
| bc1437dac4 | |||
| 1d9576e838 | |||
| 8b275a5df4 | |||
| 40d9334dcf | |||
| 98c0242819 | |||
| a2fb03f9b8 | |||
| 98815ac5c3 | |||
| 073dd20c96 | |||
| 23af9085a9 | |||
| bcaec6b6ee | |||
| 4063bea922 | |||
| 9f864b3407 | |||
| 01ac598e39 | |||
| 77a0d3af36 | |||
| 8d34bbea47 | |||
| be18e98001 | |||
| 754e0f98f9 | |||
| 07f893b6a1 | |||
|
|
c1497ec44e | ||
| 55d4784fe2 | |||
| f26e8bc520 | |||
| 7b5f1a4253 | |||
| 1058fb7e1f | |||
| 0f1b95cdfc | |||
| 8ea16fcd85 | |||
| b4b9496980 | |||
| 8dfd26361f | |||
| cb5bd32b86 | |||
| 2584330d40 | |||
| ebe6333ac9 | |||
| 534892280e | |||
| df05cf69cf | |||
| 2f782cf6a2 | |||
| 6f483c17e6 | |||
| e64610a2dc | |||
|
|
7673a16779 | ||
| d73fe4b29c | |||
| 2c61be084b | |||
| fe5f8d1997 | |||
| 2653291ded | |||
| 417f91a2e8 | |||
|
|
08625e85bb | ||
| c554c19a12 | |||
| d7d08f2f12 | |||
|
|
bafe45a2f9 | ||
| a11d24723d | |||
| 3b858476f1 | |||
| 5560b68ac1 | |||
| 8bfd381363 | |||
| c6cfe2a22c | |||
| 4ed274dedc | |||
|
|
283c815de7 | ||
| c72c1bc889 | |||
| 6e61a535ca | |||
| c1f69ea1c7 | |||
| 984599c56f | |||
| 038bb31ec3 | |||
| 1ac541d16e | |||
| ecaa0e99c5 | |||
| 666ebeef0d | |||
| b39a936fa4 | |||
| d434dce568 | |||
| 6acb390133 | |||
| 3c6f3c50c4 | |||
| c52e707421 | |||
| 5fb998c221 | |||
| a0989ea833 | |||
| 5e565c06a0 | |||
| 69b96aa5fa | |||
| bce9de1494 | |||
| 3de7928be7 | |||
| 41630ef01a | |||
| bf29cb1a4d | |||
| 8f79a4ab86 | |||
| 0ad933c5ad | |||
| 5ed9470d83 | |||
| cac468f454 | |||
| f0f9dcf4de | |||
| 3e0e84a4f6 | |||
| ef8080b420 | |||
| b5fab1fb10 | |||
| c3f019d4ff | |||
| 701f172276 | |||
| 9e8c736cb4 | |||
| c8589d750c | |||
| 5bb20a01de | |||
| 6e4bf9da3e | |||
| c21ddb5c63 | |||
| 431295cec9 | |||
| 180cb84b2c | |||
| 66b5f322d9 | |||
| f00dc248bd | |||
| dbba5db9f7 | |||
| 3a8b799df6 | |||
| 0b99a4ae74 | |||
| 99b77d347a | |||
| 8235a88ecc | |||
| 564ba30f91 | |||
| aa8535d513 | |||
| a02ef2575b | |||
| a0be16c0fa | |||
| 548d87e339 | |||
| c704aa0817 | |||
| c6a060fde5 | |||
| 762e88cc44 | |||
| b7f671d1d5 | |||
| 48706d01f9 | |||
| fb53354956 | |||
| 5995147054 | |||
| fed9f458d1 | |||
| 7987564401 | |||
| 90ae21fb8b | |||
| 56a78d174d | |||
| 37da947b90 | |||
| f23e272027 | |||
| 10afc0a2d5 | |||
| f59c8ea533 | |||
| 4153ea7431 | |||
| 2c73577798 | |||
| 0b5c53ef7e | |||
| 0fca0d54f8 | |||
| 3da0c5b2b6 | |||
| d8519bc1c3 | |||
| 33a4a799c5 | |||
| 358a021ba6 | |||
| 6dba9c25b6 | |||
|
|
1f724c802b | ||
|
|
67e3dd0035 | ||
|
|
de657829fd | ||
| a2c27134a8 | |||
| 66c60ec10e | |||
| 867b39d8e1 | |||
| 1c8997d44a | |||
| 88b5112e30 | |||
| 3874b6a25a | |||
|
|
8ba6587c57 | ||
| 3ebe36f785 | |||
|
|
34ed0b96d3 | ||
| ab353d2268 | |||
|
|
fd6f013480 | ||
| 4d0b7bc127 | |||
| ce917b227f | |||
| dec1bb04e8 | |||
|
|
a1093c17bc | ||
| 8110fbf701 | |||
| cf502ebf05 | |||
| 7c7837b967 | |||
| 3bfc1cacb0 | |||
| cd2ea26608 | |||
| dcf8159d1c | |||
|
|
1bdab80b5d | ||
| 241c4dc600 | |||
| 18e93d2b24 | |||
| 084cf6b761 | |||
| 47ddf5f4e6 | |||
| 325321f5d4 | |||
| ecbeca3d35 | |||
| 862b434c72 | |||
| 6376ed3b7b | |||
| 42e40d1718 | |||
| 1c1cc12058 | |||
| e648b88a74 | |||
|
|
6fea46f7c8 | ||
| a06e5a6e55 | |||
| 85bf429a08 | |||
| dd6ba45cdd | |||
| ab967efa64 | |||
| 369505e1a0 | |||
| 86d858a0fb | |||
| d1f5158276 | |||
|
|
db83b0f78c | ||
| 24fcc5a0c6 | |||
|
|
9cb3d474aa | ||
| 0adfebfad6 | |||
| 66c4225aa3 | |||
| 6ddef9ffda | |||
| 27a1b22e6f | |||
| f9c18cb225 | |||
| b97733328a | |||
|
|
2fe17aefd9 | ||
| acddca54cd | |||
| 7d80fb31ce | |||
|
|
8ac1d78053 | ||
| 468d6d7752 | |||
| f49d789411 | |||
| 1f56115fbb | |||
| 20d277102e | |||
| 47b4036f09 | |||
|
|
ee9e154fec | ||
| 85256b4fb6 | |||
|
|
16be671f1d | ||
| af880115ab | |||
| e64ac8f616 | |||
| a7d4206b77 | |||
| 5a80690404 | |||
| 9b0d71d4ac | |||
| 729f77d8db | |||
| e89f96aa2d | |||
|
|
f05f81cace | ||
| f4883ee708 | |||
| 043fb739d9 | |||
| b19aa8702b | |||
| f4e0744879 | |||
|
|
d7c822f86d | ||
| c8f8e217e5 | |||
|
|
bfec7f1fe8 | ||
| 21fce1f618 | |||
| b5987b57ad | |||
| e8c1cb08a7 | |||
| 0de3a16898 | |||
| 278bd7f38e | |||
| 4a3b9db324 | |||
| b3e8e96875 | |||
| f5e50c6d76 | |||
| c551c2ee1a | |||
| 3fb8406e7b | |||
| 50d12407c9 | |||
| 17931bb0ea | |||
| 6d30940920 | |||
| 3a4c985baf | |||
|
|
960d8b6cb8 | ||
| 9b7e9d4ffa | |||
| c1a646c0d7 | |||
| 48ef0e7a65 | |||
| 8dff306a91 | |||
| a935acbc95 | |||
| b1f9c99eb1 | |||
| 0851f749c3 | |||
| 3a7d9f84c7 | |||
| b43b35ccab | |||
| 5fc6eacc53 | |||
| bd782362e8 | |||
| 32151a176f | |||
| f7c88d2086 | |||
| 1eb76f2b90 | |||
| 226b3e049d | |||
|
|
7f0e9851cd | ||
| 15df77940e | |||
| 9781516552 | |||
| a23264a542 | |||
| 0e7845985a | |||
| 93efe45347 | |||
| a46f824e1e | |||
|
|
0de9f036bc | ||
| 275f2515d6 | |||
| b4604928ec | |||
| 5a79b4021a | |||
| 280fd38039 | |||
|
|
b032364616 | ||
| a430aaee97 | |||
| f4ee2ca3b9 | |||
| 92b9212b31 | |||
| 73656e60ff | |||
| 401329d80c | |||
|
|
6131970724 | ||
| 85dcc4b78a | |||
| 34a1bf7e28 | |||
|
|
afdccefa98 | ||
| e1fd0a5535 | |||
| 15d2cd7342 | |||
|
|
f5fda8b71f | ||
| 8586dbbe8b | |||
| c508c71f60 | |||
|
|
33cabc8e2d | ||
| e244d477e2 | |||
| c2f81e182a | |||
| d7c68cfaa9 | |||
| b1aa5b8183 | |||
| 877b1dacf8 | |||
| 8eef664e50 | |||
| f0581f5038 | |||
| 0b584d48b8 | |||
| 9199875c51 | |||
| e062cc4870 | |||
| 2ba4d87b75 | |||
| 4b4c0df77d | |||
| b5e4f32fa5 | |||
| 27ca45b088 | |||
| 345e38a560 | |||
| 176e4c8b3e | |||
| 81083dd30c | |||
| 9a5a09438e | |||
| f17447b2f1 | |||
| 930eb14a8a | |||
| 08951f8c2e | |||
| c74bb01c3c | |||
| 34b9b189d4 | |||
| 960aca266e | |||
| 840e04dc55 | |||
| defaf3f970 | |||
| b8b2349b6c | |||
| c3c4671f76 | |||
| d9811d9d50 | |||
| da515f8595 | |||
| 467a8af504 | |||
| ed16571d44 | |||
| 6d06f1fbce | |||
| 9a59b2ad3f | |||
| 1dc03ca5ae | |||
| 260de6eae5 | |||
| 2458377010 | |||
| d4a7f2bbc8 | |||
| 1b8b009b58 | |||
| a6623058b5 | |||
| aa385a5e30 | |||
| 67e9113b48 | |||
| 9667279bf6 | |||
| 84e94c1122 | |||
| 12fd04b296 | |||
| 2e5ab39fee | |||
| b975fedfee | |||
| 0670bca67d | |||
| 9a976fdd3c | |||
| 91d189cf00 | |||
| b97e0df6c5 | |||
| e966df976f | |||
| 530347c847 | |||
| fc5acaf0be | |||
| affea597bf | |||
| 732ad976bf | |||
| b0133162f0 | |||
| 0fa462c385 | |||
| f7d7ee6b43 | |||
| b769b79b9a | |||
| 320001b3a3 | |||
| 5c53bb3853 | |||
| 584ac773b3 | |||
| be6e034153 | |||
| b85bb698d0 | |||
| ecab1a45b9 | |||
| d3a7f7a732 | |||
| 30b106c5c2 | |||
| 5b8148b83d | |||
| a973696501 | |||
| 5bb5ad7ba8 | |||
| 72188223ae | |||
| 4fbd037af1 | |||
| 9789ab6e37 | |||
| f766039139 | |||
| 9aad09be9a | |||
|
|
ea07a3e3ce | ||
| 4e9ea630d7 | |||
| c4aeecdf3a | |||
| ab36c69729 | |||
| ade7d1f735 | |||
| 5c7d62c59b | |||
| 772de4e67b | |||
|
|
2505f3904d | ||
| ea06ede236 | |||
| d9132fad5a | |||
| b20148b93f | |||
| 41dde8a164 | |||
| c83c4b052b | |||
| 3dad8258df | |||
| 60f358510c | |||
|
|
46e5e5fbdc | ||
| 6561a63956 | |||
| 9a390616ad | |||
| 815ede7e1a | |||
| 4960657be7 | |||
|
|
0b18aa4906 | ||
| f040827bc8 | |||
|
|
876c3b2ecd | ||
| 52c30a1e0e | |||
| f2d93564a5 | |||
| f19213d6f7 | |||
| 0e378ea982 | |||
| 51d8d48218 | |||
| 0f560a3594 | |||
|
|
07ea3e39b9 | ||
| 50851ad62a | |||
| d45a956520 | |||
| 7e9e95b7d2 | |||
| c02255dd08 | |||
| c465e15e96 | |||
|
|
1da99434b3 | ||
| 368db8d94c | |||
| a05c8cc3ea | |||
| 7d7ef2788e | |||
| 93844b0e20 | |||
|
|
45b8e7cc28 | ||
| 59aea26e72 | |||
|
|
a3a3a737ed | ||
| 69c048cfa6 | |||
| 045543f27a | |||
|
|
3369821875 | ||
| e880edd680 | |||
| c5d516949d | |||
| cd695e5024 | |||
| f97dd9747f | |||
| 67ed60731b | |||
| d2c756a08a | |||
| 976d8a16e4 | |||
| 55f7a181cd | |||
| 55d0ebad03 | |||
|
|
5db9a064ca | ||
| 2a5fe72a34 | |||
| fd6d36b6d9 | |||
|
|
2aafc85fae | ||
| 3905f4ed28 | |||
|
|
c6570ac499 | ||
| 9241be1f5f | |||
| 4fbd695f4e | |||
| 05b72a0c7e | |||
| 09ab982a4a | |||
| 70b2f20ca8 | |||
| 4f12a096e7 | |||
| ea5df706a8 | |||
| d92dae066d | |||
|
|
40345f6cb9 | ||
| 8aaf0eb99e | |||
| cd1885477d | |||
| 28bb578170 | |||
| 2965df19cb | |||
| 36929f3ab4 | |||
| e1f7ea8f18 | |||
| 7e4b603582 | |||
| f82eaee49e | |||
| e1fe3d959a | |||
| 86d3dd2c88 | |||
| 127e3ae325 | |||
| 16c8c3c873 | |||
| 58fbd9c581 | |||
| 91f75c60c6 | |||
| dee60a5a34 | |||
| 013d53c346 | |||
| d42e28f713 | |||
| dc69407d76 | |||
| ab228bb8d4 | |||
| 772ba40108 | |||
| f143e2ea49 | |||
| dd32fc58eb | |||
| 30f499f3cd | |||
| c7325645be | |||
| dfca75f19a | |||
| 70005b3b82 | |||
| e51e2dfe27 | |||
| 57ddcb47f2 | |||
| 0ec2764369 | |||
| 2215a7895e | |||
| bac86bd8e0 | |||
| 3f9540a415 | |||
| e5be78e832 | |||
| 8a03d1c704 | |||
| 1655fd33eb | |||
| af93e5d00e | |||
| f2f1f1c879 | |||
| fd7179c3f5 | |||
| bbf4200b42 | |||
| e50877ba5d | |||
| 61cf4eb964 | |||
| 87ca7fdc8d | |||
| 7745bbe60c | |||
| 949ebf83e3 | |||
| 6744282bf1 | |||
| 5b374b0410 | |||
| efaa97b9bd | |||
| 87c5dd5fa7 | |||
| 023a5b75b4 | |||
| 7e6a5de0f1 | |||
| 348500a288 | |||
| 27be26d12d | |||
| 4932eab08a | |||
| 341e2ed7ff | |||
| 7af6dac964 | |||
| f94d23723c | |||
| 170005fb79 | |||
| 5ebe3fd95c | |||
| d49d2247c9 | |||
| 593c90f605 | |||
| fa41140aed | |||
| e246509ebf | |||
| 81db94ac8a | |||
| de29c766b3 | |||
| 3e96586c8e | |||
| 7eac6cadb0 | |||
| 414ba32541 | |||
| 8d4db5b574 | |||
| 422b904e00 | |||
| 5ce627ee0b | |||
| 3ddb6f9abc | |||
| 583f2a17b5 | |||
| e628f1f6d5 | |||
| 89f21867a5 | |||
| 4dd557feee | |||
| cc315db34b | |||
| 086692e668 | |||
| e7528205f6 | |||
| af735e196e | |||
| 7dd59c3346 | |||
| 1c6ade5b77 | |||
| f714da7662 | |||
| 907ae1db04 | |||
| 691dd4a7bd | |||
| b1f531446a | |||
| c5aeaf1452 | |||
| c27914c957 | |||
| 6424e1e416 | |||
| 0fc0e12c08 | |||
| dfc3e79709 | |||
| f0b515ff5c | |||
| 2f39e3277f | |||
| 6576794456 | |||
| bdc8223c6c | |||
| d68cf3b557 | |||
| 11787d290b | |||
| 047a14dcbc | |||
| f9d441a97f | |||
|
|
9cee124afd | ||
| 43cf348b72 | |||
| 86eb344492 | |||
| 9030628090 | |||
| f6d39c7be3 | |||
| a9931b114e | |||
| 87d9ce4486 | |||
| b61256221a | |||
| e9f43cb6f4 | |||
| be79a9b4f9 | |||
| a3ff2fbefe | |||
| 8d032cd867 | |||
| 71392cd54f | |||
| 92f14b1c12 | |||
| 7e9674c7a7 | |||
| ccf0b070b0 | |||
| a9536cccd1 | |||
| 5f26bdcefb | |||
| 015c901047 | |||
| 201ac15aea | |||
| 04725db699 | |||
| 9b72c785b5 | |||
| ca20d585da | |||
| 48bcb80dd7 | |||
| e4bf1f0d53 | |||
| 769764ad17 | |||
|
|
cbf732d072 | ||
| 9a60ded31c | |||
| f7fa4a6dc5 | |||
|
|
d2f653decb | ||
| 7471265010 | |||
|
|
60e5dc0e57 | ||
| 9aacf8ca41 | |||
| b91b4cd5b2 | |||
| e5843a4f40 | |||
| 97259b5a14 | |||
| e0e56faa5d | |||
| f837fb31fc | |||
| d1eef0a8c5 | |||
| 4a657451ed | |||
|
|
68a5af4bef | ||
|
|
d0018d4de8 | ||
|
|
38dd92754a | ||
| 1f2d68ffd2 | |||
| f18d0c998f | |||
| 374e2e6b91 | |||
| b2e439c94e | |||
| 875d616cbf | |||
| 837e229d11 | |||
| 8fd038cd11 | |||
| ca8b44dd8d | |||
| e8f767a56b | |||
| b531c48083 | |||
| bbab1c13a2 | |||
| 4334b48ab1 | |||
| ab1ca7af80 | |||
| ac54b65c87 | |||
| 35db1ff6b2 | |||
|
|
126bdcca73 | ||
| 5cfcb71759 | |||
| e6a54b08f1 | |||
|
|
434ac7b577 | ||
| 7e3b3fae1c | |||
| 4c52629ba7 | |||
| 135b9b6e09 | |||
| 1647065b04 | |||
| bf6b39707a | |||
| 4a4450260f | |||
|
|
4701234c94 | ||
| 8c4c4a25cb | |||
| 7be14d2d8c | |||
| d8b387fbad | |||
| 6087377e8e | |||
| e5797ef6a8 | |||
| 6817cafbfa | |||
| bcdfeb03a8 | |||
|
|
8854328697 | ||
| 0d83b2a59f | |||
| 691b4b5e59 | |||
| c4bffbda11 | |||
| ef7da60ec0 | |||
| 95275c1b39 | |||
| 944714e4ba | |||
| aa7962c959 | |||
| 97c13d5f44 | |||
| 004faffc4c | |||
| eeef2abaf3 | |||
| 3a70c388cc | |||
|
|
b81044106e | ||
| a5230d1da4 | |||
| c1223aaee4 | |||
| 7175315abc | |||
| 00f4a722e1 | |||
| 664d847a71 | |||
| 21dedb7748 | |||
| 88a0c5dc57 | |||
| 4677c39b97 | |||
| 00b145d673 | |||
| f04fff354a | |||
| ca4264b838 | |||
| 0e4047c36f | |||
| 1f3bb11a5d | |||
|
|
f7a103ff89 | ||
| 555c326b0c | |||
| 4f74a770af | |||
| b26b14a0f2 | |||
|
|
7d0e8c96ee | ||
| a14439f128 | |||
|
|
4de8aba54a | ||
| 3814dda46e | |||
| 923716dd08 | |||
| fa0f86c8a0 | |||
| efb8d29f78 | |||
| 22c71009df | |||
| 4e48bae0ad | |||
|
|
ce55c5c011 | ||
| 7daf02e94c | |||
| e4fdf60b8a | |||
| b9ae73d865 | |||
|
|
e9583db6a7 | ||
| 89eda3c702 | |||
|
|
245e0e038b | ||
| 010579aa1b | |||
| 24e8337b41 | |||
| d5f6224236 | |||
| 9ec8fe921d | |||
| 6fd1d294e5 | |||
| d48c7271c0 | |||
| d155c675f2 | |||
| 3a56faf123 | |||
|
|
bc74608271 | ||
| 484a615981 | |||
| efe92ba356 | |||
| 25f4a815fd | |||
| 74f774bc3b | |||
|
|
e871beda8e | ||
| ddc3011dd9 | |||
| 9bf9322994 | |||
| 325d1567a7 | |||
| e3ed7fde45 | |||
| e8810cc2a7 | |||
| bf101519d8 | |||
| 58e1302b75 | |||
| 750a070fbb | |||
| e8b3131d2e | |||
| c00cc887e4 | |||
| 6fb8a5bbe5 | |||
| 952fcc2a1e | |||
| d070592491 | |||
| 4fe64ddcb1 | |||
| a10f10aa61 | |||
| db2ad358cf | |||
| 124b19525f | |||
| a64b9e1ab2 | |||
| a936daf95b | |||
| dba64f9ea7 | |||
| 2e6555a8cb | |||
| 52003c2d58 | |||
| da028d6665 | |||
| 01a7746ae9 | |||
| 827fe3578f | |||
| d4cf1d9aca | |||
| c2a4be71c4 | |||
| 404fd0dfdd | |||
| 9ffd7c91b7 | |||
| fc20046aaf | |||
| 934ccba81f | |||
| 4db502e0f1 | |||
| dd9dbd8321 | |||
| 47102edfef | |||
| 9b86a6ee33 | |||
| 9cb4dbc79d | |||
| 4789e40574 | |||
| 39109ae40b | |||
| b6ffe579e0 | |||
| d2b94a9a0e | |||
| 809022b388 | |||
| 5c2c614a65 | |||
| a0934d9aaf | |||
| 19ac2d9a0a | |||
| ae5870556a | |||
| 87c42c810e | |||
| d7d36b44b2 | |||
| 9bb44d3ae5 | |||
| f216c03a7b | |||
| d091fc48f6 | |||
| 0eeae3a7ff | |||
| 22c26c97f7 | |||
|
|
86c6416090 | ||
|
|
123c07efe0 | ||
| d64f01bc54 | |||
| 415d7988b9 | |||
| f846a1ac83 | |||
| e95269d539 | |||
| 4161a3afc1 | |||
| f842ed7b3d | |||
| 66c8bb7c78 | |||
| cd499edd6c | |||
| 51d4e187b6 | |||
| a75f648b71 | |||
| 66a253b4e9 | |||
| 076f8e0365 | |||
| 37c850cf6c | |||
| dc93213811 | |||
| 56d8c6c236 | |||
| 0a0b114baf | |||
| 7c444b3c53 | |||
| f4c72266ea | |||
| f45e6c885e | |||
| c4646a7de6 | |||
| 37be254f25 | |||
| 21d6244c3c | |||
| d141e67f52 | |||
| d2c4a79791 | |||
| 240058005f | |||
| 6d31f3791b | |||
| 7fba05d0d5 | |||
|
|
44ead9f469 | ||
| 7ef3971fe1 | |||
| 216a3d5d60 | |||
| ffb6844a41 | |||
| 575723b860 | |||
| 50a000f1c7 | |||
| 476c73fed3 | |||
| 471e655eb3 | |||
| 934a436065 | |||
| ba1508a82a | |||
| 0f67d14153 | |||
| 832d8f44f1 | |||
| 33343e1b1c | |||
|
|
28694cd2b7 | ||
| c65f66decc | |||
| 73a4743e88 | |||
| 2bcbf6365e | |||
|
|
43301108ba | ||
| 7898d26461 | |||
| 13b3158bd6 | |||
| f4f327f25d | |||
| 45ff09dadb | |||
|
|
bec78bc44c | ||
| 16e35935f9 | |||
| d6603488b6 | |||
| 717c2d2ece | |||
|
|
e704af5494 | ||
| 5399d26c94 | |||
| a32e29a378 | |||
| a62c474f99 | |||
|
|
c507bb0238 | ||
| 30902eeac8 | |||
| 9442d58f4c | |||
|
|
55a14cfedc | ||
| 3d0ea980c8 | |||
| c9f7696519 | |||
|
|
623d435758 | ||
| 05f06f09d4 | |||
| e19402c80c | |||
| cb87c5ba71 | |||
| 657eca08bf | |||
| 4c58c86f84 | |||
| 9b73a057fa | |||
| 3c9bf95728 | |||
| 2f48af5afe | |||
|
|
f7f5ba15ff | ||
| 5fd014f023 | |||
|
|
caf3f4a783 | ||
| b264bf6f58 | |||
| 06b07cf9a0 | |||
| e5804b22cd | |||
|
|
85f292e1d2 | ||
| 057f05a88a | |||
| a700267ee6 | |||
| 331e33ec70 | |||
| c9dc118af6 | |||
| e1e24a3350 | |||
|
|
339dfd1c0b | ||
|
|
5bcc81c00a | ||
| e7212ed506 | |||
|
|
c705c282d1 | ||
|
|
20ea1f8209 | ||
| fa73171d7d | |||
| a27675a783 | |||
| 2ddae1f0b9 | |||
| 415acc7780 | |||
|
|
7590cf824f | ||
| 3ee88ddfb4 | |||
| 4d5f90e0ec | |||
| 694a65e9de | |||
| 7e33103758 | |||
| e04f26223e | |||
| cd76b72d3c | |||
| 57372fe922 | |||
| 82ca7fdcdc | |||
| c6d9fb89ba | |||
| 2ecd88586b | |||
| d8779bed14 | |||
| 7ad988bceb | |||
| 04fc897a60 | |||
| 7922ba15ce | |||
| fae27f7e19 | |||
| 7ba2f29efc | |||
| 0e8439d52b | |||
| 17c60b6b4e | |||
| 4ec54b2dbc | |||
| 409b1fa00d | |||
| ad5fc52d66 | |||
| 4b4d21a0de | |||
| 3642a3fec5 | |||
| cac097ea66 | |||
| 4a2b0d9d9f | |||
|
|
6e53e7a23b | ||
| 1e1e86dc16 | |||
| e7777877fa | |||
| 3a685eb316 | |||
| 344509ec28 | |||
| b70a2951bd | |||
| 63282f42b2 | |||
|
|
03786817ed | ||
| 680bffa9e8 | |||
| 6f07ec52ad | |||
| daecb76f8e | |||
| 7f653d900d | |||
| 4cd3e682b9 | |||
| 143cceeade | |||
| fb8ca67ac1 | |||
| 80fc1c4112 | |||
| eecdeb0af4 | |||
| 5e9e11694d | |||
| bb41f89d44 | |||
| 4158f25b35 | |||
| 3694cd0694 | |||
| a5a06fbaab | |||
| a5ee38d4fd | |||
| eca95c36dd | |||
| 554b4047b9 | |||
| b1e8b4eb11 | |||
| b3f693556c | |||
| e2fd50d152 | |||
| f1cb9a824d | |||
| da0e718276 | |||
| 4519a4175b | |||
| 4fa375f4f4 | |||
| 3ca5fdb082 | |||
| 32ae726050 | |||
| 83950cbecb | |||
| 5b9f63bc91 | |||
| 7b93072093 | |||
| 90c312ea5c | |||
| 2bec38d85f | |||
| b8ab1780d3 | |||
| 997ad34151 | |||
| e960d4f00b | |||
| 52fe9e2c13 | |||
| e6b386dabf | |||
| f558095a0e | |||
| 014a604c86 | |||
| 5e7f2f0d5a | |||
| 11b52cb212 | |||
| aa7abab742 | |||
| f5ce6e47e2 | |||
| 05258e6fce | |||
| 7675ead837 | |||
| 6e80a19933 | |||
| 3a6cf05647 | |||
| 147cd32129 | |||
| 9789117759 | |||
| aeee5658d3 | |||
| 949ac2de98 | |||
| 5b4333dacf | |||
| 561cd7111e | |||
| 784ee84fb3 | |||
| b0cf548775 | |||
|
|
55c315eb98 | ||
| 9bcb41ab5d | |||
| 007b1bd21b | |||
| 9dae7988fd | |||
| 8877d7ea55 | |||
| f35a23a53d | |||
| 64484ef18b | |||
| 0d9381b528 | |||
| d57465d067 | |||
| 5039dac045 | |||
| 6073357bb6 | |||
| a00343d444 | |||
| a0b4681369 | |||
| ea340f3fc2 | |||
| db39483f97 | |||
| e575511849 | |||
| 9dc3030c18 | |||
| f990d7584e | |||
| 133d560e64 | |||
| b3ba764d89 | |||
|
|
3ff1124c30 | ||
| da927c8f7b | |||
| cb211d8ba1 | |||
|
|
9cbebbdd6a | ||
| 8684e21da3 | |||
|
|
3f9a90f758 | ||
|
|
661a58b549 | ||
|
|
a47b049dfd | ||
| 58b2918ea4 | |||
|
|
484b4cb280 | ||
| a77d512bac | |||
|
|
e295354aa1 | ||
| 7aeae1e998 | |||
| 9b98afd6cf | |||
| bad97971db | |||
| 6f8c122503 | |||
| 8b25aecb3a | |||
| 400846cab4 | |||
| 72708bdef0 | |||
|
|
23d817d1f3 | ||
| daf4086bc9 | |||
| ab2e9ca43e | |||
| 222d69218a | |||
| ae03da927e | |||
| 9cb311a6c9 | |||
| dfd665088e | |||
| 51afd70fff | |||
| 203abc1ff9 | |||
| a0487671d2 | |||
| 88e605fc5a | |||
| 130c390916 | |||
| 93389633ba | |||
| 53897cf779 | |||
| 9192ac2c81 | |||
| d54b8ab756 | |||
| 67210ce23b | |||
| a89f27abb9 | |||
| 37eccd8a58 | |||
| 452fd810ac | |||
|
|
cd91a79578 | ||
| 4c70fb9cfa | |||
| 62dcd672fe | |||
| fde40577ee | |||
|
|
963633328c | ||
| bddf214f91 | |||
| c7ef2eeec7 | |||
|
|
8b988a3f02 | ||
| 792337ed73 | |||
| d1e5f9d0a4 | |||
| d202746829 | |||
| 72724c6014 | |||
| 8cdbe8a491 | |||
|
|
4617161f3d | ||
| ffed4f56bf | |||
|
|
19f9390020 | ||
| 10a4401259 | |||
|
|
ed7ab76cd6 | ||
| ccfe1bac74 | |||
|
|
0c2071bc5d | ||
| 2d503ab05a | |||
| 0f204b48dd | |||
| ac586267c5 | |||
| 897619f126 | |||
| 5fef08797d | |||
| b43c3a7294 | |||
| 423b145b7f | |||
|
|
93333a4915 | ||
| b3708abcb6 | |||
|
|
f8c2580773 | ||
| 4609d5588d | |||
| cbeb44779f | |||
| 7c848aa372 | |||
| 015fca8145 | |||
| 54cc5fbad5 | |||
| 66f66dd408 | |||
|
|
24de9d5988 | ||
| 7dc5fdc88e | |||
|
|
2f912b4ddd | ||
| 0931cb9f9c | |||
| bc98a80de6 | |||
| 9e3ad237b5 | |||
|
|
40a6ce7aa7 | ||
| 85bedbf56b | |||
| 5575710d7e | |||
| df442f7fff | |||
| 89e665b369 | |||
| 42f845a7e2 | |||
| a226bc9fbb | |||
| b2f9935abb | |||
| d8a22ba5ee | |||
| be09821778 | |||
| 4f0f440f2c | |||
| 9cd46a7f9f | |||
| 66c875584f | |||
| f14d2fddc7 | |||
| fa2dda6ee4 | |||
| 73c8117a67 | |||
| 5f42e94da7 | |||
| c31a95dd4b | |||
| 19ec0ddcb9 | |||
| 5e24086476 | |||
| 72934000b0 | |||
| 8a240e2c9c | |||
| 9f081359b0 | |||
| 679a1f0d05 | |||
| 4533e87bef | |||
| 47a60b41a8 | |||
| b06100d0dc | |||
| e5591cc59e | |||
| 9831216ca4 | |||
| 57e36c5fdb | |||
| 35904bd79d | |||
| f16a2a7fec | |||
| 0b4b8a62aa | |||
| ca4fa30cdb | |||
| 90c024dfe5 | |||
| e634c44797 | |||
| 8eea4072f6 | |||
| a0886e1a3d | |||
| b93d77c907 | |||
| a148e27211 | |||
| 3cd9b52d2a | |||
| 37206916e4 | |||
| d36d2fa4b4 | |||
| 4591c9006b | |||
| 5b4ceb49df | |||
| ba937c1e77 | |||
| 2b77254322 | |||
| b27cb78f62 | |||
| 106af5b177 | |||
| 4b31160670 | |||
| 0593be9928 | |||
| 6602119ee7 | |||
| 2943d1f1b7 | |||
| 485ab6793c | |||
| 164a450784 | |||
| f12db7e55f | |||
| 23e000afb3 | |||
| 5c77dd91e7 | |||
| 1c4140daec | |||
| ad1035547d | |||
| e61be60f71 | |||
| 9d6db1e847 | |||
|
|
76a417fee7 | ||
| 9e836148bb | |||
| 8d32e404e7 | |||
| fb3b0f0ae6 | |||
| 6ad415fa5a | |||
| 79d2cc26b0 | |||
| df0ad48348 | |||
| b1024d1da1 | |||
| 5db1ad1535 | |||
| b45fef8b0b | |||
| 22d4a9a809 | |||
| 0478356ea5 | |||
| 45e35c4396 | |||
| eb5079a5ef | |||
| a62e01e02e | |||
| c58db08e5f | |||
| 6476fe1d2c | |||
| 8ea34d5736 | |||
| 4d4f43fa93 | |||
| 3e7b2ee6c1 | |||
| 0d16d3abf1 | |||
| 3923ea9dd6 | |||
| e1292620c7 | |||
| 4e2b2e1f54 | |||
| fcf75b652c | |||
| 9765b51969 | |||
| 0769183f61 | |||
| 845cc1a2ef | |||
| 16a95c12b4 | |||
| 0516aeb94f | |||
| 6b92ecebf9 | |||
| 9010c71051 | |||
| fcca331224 | |||
| e3a2f76f78 | |||
| 7e221454a3 | |||
| ebecffca03 | |||
| 42f99fd7a2 | |||
| b31ab0ae16 | |||
| bec041aa01 | |||
| 8ded811295 | |||
| f1c0b9f1ed | |||
| d32a371b3a | |||
| c0860cd275 | |||
| bfd125fc4f | |||
| a115ab28ba | |||
| d21b126401 | |||
| 410bf03682 | |||
| 0a9f3de5c3 | |||
| 0ecadb918b | |||
| e90b055f12 | |||
| 02877dc23c | |||
| e22439af4c | |||
| 298c777971 | |||
| 39cf12f51a | |||
| 276bc480cf | |||
| f0b2820034 | |||
| ba65103df4 | |||
| ae64b70f10 | |||
| 56ea2dd6f4 | |||
| 1633b9fca7 | |||
| cb7a79953f | |||
| 5c12d9d6a6 | |||
| 244ae40969 | |||
| e2b7fec286 | |||
| 4cc2bc16c0 | |||
| 24a4872058 | |||
| 91a9788706 | |||
| 2a6ef8853d | |||
| 56358508c8 | |||
| 976b94464e | |||
| d166a0ccfb | |||
| 7c5c389629 | |||
| f447b527eb | |||
| a251714989 | |||
| ea27f817e3 | |||
| b6fb47fc16 | |||
| fa540d516d | |||
| 4b068d2a52 | |||
| 50f5289e2e | |||
| 1ba20a4ea7 | |||
| e18067b2f0 | |||
| 025f19728f | |||
| 248496021a | |||
| d56a73f0f7 | |||
| a2b68ebce1 | |||
| 93b92f2b17 | |||
| f5219bf79b | |||
| cc1d99d9c9 | |||
| f7101db942 | |||
| 97ae9d17bf | |||
| a5624e7e3c | |||
| 0be42900c1 | |||
| 9722e1ca64 | |||
| 63a20506a0 | |||
| 9b334e416f | |||
| 7805956cdf | |||
| 04ffd0ad45 | |||
| f6e7055c2b | |||
| 77167655e8 | |||
|
|
24e2d0a3db | ||
| 29795074fe | |||
| e017675b4d | |||
| 74a9897deb | |||
| f593e77d35 | |||
| 8b9ab7f0b8 | |||
| 02951833cc | |||
| 08e300d065 | |||
| 776e4c2418 | |||
| cbe439815d | |||
| 5c43222e4f | |||
| a6fe7e2fe3 | |||
| 6f6523836d | |||
| 650a57350c | |||
| 736b34e834 | |||
| d29682fd19 | |||
| 59c9058426 | |||
| dbd32201c5 | |||
| 55158a67fc | |||
| 49dfe99a14 | |||
| 165672f9fd | |||
| 3bf03aa0b2 | |||
| 8ccbe8e7a6 | |||
| 1d54f90c95 | |||
| 000de62093 | |||
| 7ed82a796a | |||
| 4ad7e4c3f3 | |||
| e1c17d8926 | |||
| 47cc40d0dd | |||
| 1a66d91885 | |||
| 01fbe11831 | |||
| d834965d79 | |||
| b6eeb79500 | |||
| 460d6c4e02 | |||
| c6089bd480 | |||
| 3d18d4bc6f | |||
| 61954b820f | |||
| dee67c6ea2 | |||
| 2fdaf1d482 | |||
| 8af986a4c5 | |||
| c3a13b66e6 | |||
|
|
78111fd408 | ||
| efeaa3f0b5 | |||
| aed5e1aa63 | |||
| 44211065a0 | |||
| 364d38cc05 | |||
| b38bfd7ada | |||
| f05efd1693 | |||
| c64b03135c | |||
| 860a50f025 | |||
| 5e9706ca23 | |||
| d3d23fe7b9 | |||
| af2aa2e083 | |||
| bb52b17a12 | |||
| 17c68cb9b7 | |||
| 5c3fe16e9c | |||
| 81ce4e3445 | |||
| 9aaa5174bb | |||
| 8d9c89d281 | |||
|
|
62fa42f1ba | ||
| 502cb1595f | |||
| c1e358e9fd | |||
| 7600ef1b30 | |||
| b9057da526 | |||
| 99e143b686 | |||
|
|
0414f1f4de | ||
| 80f651d17a | |||
| 1a373eb876 | |||
| 5882b6b58e | |||
| f876e56a2d | |||
| ac98ec92e4 | |||
| 6cf6445c01 | |||
| 062a6def14 | |||
| 282a5451f8 | |||
| b410f782e6 | |||
| f4d997957c | |||
| 5551c7d341 | |||
| 642e028fca | |||
| ffe5cd21e5 | |||
| 93679924f4 | |||
| 212e3c1066 | |||
| ebd4baa6ff | |||
| 6d846144dc | |||
| e9767ee6c7 | |||
| b4a77f25f9 | |||
| 08098f48a0 | |||
| 478ebe95f0 | |||
| bc48b85478 | |||
| a5f7610230 | |||
| 98157bbc8b | |||
| 4e0792b139 | |||
| a171970c96 | |||
| 38490fd462 | |||
| b9726cc23f | |||
| e047696fba | |||
| bea5a0896a | |||
| c15ddb64e8 | |||
| e4e3cbd68e | |||
| fd8b27d3f1 | |||
| 497a1632c0 | |||
| 5e991a9708 | |||
| 0e49874d38 | |||
| a93b99bca7 | |||
| 4d541c175c | |||
| 588de95004 | |||
| 8850d73ea3 | |||
| feca7c7d7c | |||
| 1507368ea7 | |||
| ce74ec97fb | |||
| 1fd5e2c3f6 | |||
| 79e23fe0c7 | |||
| ab0c62c73a | |||
| e72de994ad | |||
| 364a84cc6d | |||
| 40072c6423 | |||
| 2d56f71588 | |||
| d7a8b87473 | |||
| 7c8c2c655c | |||
| 8ea870b2a7 | |||
| 7e1317a400 | |||
| 6569a4af3b | |||
| 31ee85a523 | |||
| acf6384318 | |||
| 539426d585 | |||
| f8401414d3 | |||
| e3cbdb9420 | |||
| 823126dad6 | |||
| 6d3c8d6679 | |||
| da2ee9c6f4 | |||
| 0b4299d6c3 | |||
| 0830419658 | |||
| 85bfcd7447 | |||
| 729b7e0bcc | |||
| 4dbaf5d0f8 | |||
|
|
cbad208be6 | ||
| f7992d8eae | |||
| 8c7c344ea9 | |||
|
|
2a52aa61c1 | ||
| 8d06bd94b8 | |||
| 8f53747515 | |||
| d7a997052d | |||
| 324e98b639 | |||
| 7aebb728f8 | |||
| c386a2a57a | |||
| 6e5c4917d9 | |||
| d5d9ae51c5 | |||
| ca39f0da3a | |||
| 41cdbb43fc | |||
| 6f3897886e | |||
| 6b1bfdfeca | |||
| a07a37f256 | |||
| 896512cf3e | |||
| 60602e68f5 | |||
| 7066eb2632 | |||
| ddfdebced2 | |||
| 4913cc9ed6 | |||
| 6427bb794d | |||
| 77bc7c85f8 | |||
| 54a90a2e99 | |||
| e4a9c32caa | |||
| 13b9711d4a | |||
| af15d6a592 | |||
| 132580ea6c | |||
| 262ab0ab73 | |||
| 1e19d70fa1 | |||
| 48b8d530ca | |||
| 1f35c5b8fa | |||
| 574b87f5b5 | |||
| 88be69ffc7 | |||
|
|
87e49e3244 | ||
| 4a392126cd | |||
| c0489ebb44 | |||
| ed65f63a37 | |||
|
|
2b4ff60de0 | ||
| 8069bf51ab | |||
| 369b53e2fd | |||
| 1cf0c1636b | |||
| 060fe39331 | |||
| 8bec4058e2 | |||
|
|
61d30d6f77 | ||
| 7daae8a129 | |||
|
|
ab2c9e450e | ||
| 24c269bf4c | |||
| 8f0c8fd800 | |||
|
|
a0e29a2bc2 | ||
| 94cab2e71a | |||
| 266b9fa5c2 | |||
|
|
31de8edc4a | ||
| 1e59eda9a9 | |||
|
|
36c3fcb395 | ||
| ea8f0523d5 | |||
| 71625b7c0e | |||
| 408a62c755 | |||
|
|
da4a61b716 | ||
| 996c4474ec | |||
| cdc05d8d98 | |||
| 52892ef5df | |||
| ce0340689b | |||
| 58cdb0db95 | |||
| c2c49d09e9 | |||
| c9a2997cea | |||
| 94aba71ae4 | |||
| b80c7bdc23 | |||
| 279231c08c | |||
| ef47b839ad | |||
| ffc8ae7d04 | |||
| 7d8269832f | |||
| 6ba516f66e | |||
| 53d4fcbd6d | |||
| 237ef10545 | |||
| 23650aa800 | |||
| 9998691524 | |||
| bd61a953ad | |||
| f53379829c | |||
|
|
04a4faba22 | ||
| 65d4369306 | |||
| 6ab650d498 | |||
|
|
2a06c108ac | ||
| 3bd31c55fd | |||
|
|
ea2fb6d4cd | ||
| d4dccc8016 | |||
| 3cdc78af1f | |||
| 7784f0c929 | |||
| a399d3d04a | |||
| d261dd8fb4 | |||
| 273f475732 | |||
|
|
35cce62b9b | ||
| 7b5d2820ca | |||
| 46374cfb2d | |||
| 020bfb2abc | |||
| d3ab317b34 | |||
| da3dd52484 | |||
| 9514cd9cce | |||
| 654a66062a | |||
| 5ac5b13f9d | |||
|
|
7f39bf5c6e | ||
| 0e07a10dcf | |||
|
|
8cbedd00b8 | ||
| d9d3d3fdb4 | |||
| 364c142f0a | |||
| 92c0177c44 | |||
| 6bdca8c37e | |||
| fcb51e727a | |||
| 4523a10898 | |||
| 4e155c8039 | |||
| 8b9f861f30 | |||
|
|
3595ae6066 | ||
| 351d4a0ad6 | |||
| c6e6771839 | |||
| fb51e0fcd1 | |||
| 4fbf9c345d | |||
| f3557e9acf | |||
| cb7367264b | |||
| 2fb722edbf | |||
| fce1608403 | |||
| c6117cb599 | |||
| c68e056267 | |||
| 4c9924580a | |||
| 058b3b8d62 | |||
| 1eb29acf7c | |||
| 48ac052de8 | |||
| 4863ff2a19 | |||
| 0b0fd8ea67 | |||
|
|
b8f4ed2767 | ||
| 732eba7d7d | |||
|
|
8b676f3c1b | ||
| 960ca4deca | |||
|
|
63ecec4edb | ||
| c6f04aa26f | |||
| 8f29df66d5 | |||
| 2d9e62aedb | |||
| eba511add2 | |||
|
|
e9e38d5224 | ||
| b131ba8b93 | |||
|
|
a12046fe93 | ||
| 769f065bdf | |||
| 81f9ee5eca | |||
| 42fb44dd15 | |||
| 600bb8b516 | |||
|
|
8831d91cbe | ||
| d7d734565c | |||
| 791e9bf156 | |||
| 284812b4e3 | |||
| 505d59bc77 | |||
|
|
02eda25aaf | ||
| d19521810e | |||
| f8d17724f9 | |||
|
|
6548125747 | ||
| ef375619b6 | |||
| fe89ae1846 | |||
| 4ccf142917 | |||
|
|
89fbeb85a2 | ||
| 62b3d15991 | |||
| 52a305f5cd | |||
|
|
658305ef28 | ||
| 19295e2832 | |||
| 719e8165f1 | |||
|
|
d5ba280654 | ||
| 00dc48b439 | |||
| 8cc87ba7a0 | |||
| 8b2f7ee188 | |||
| 167c551b89 | |||
| b57968d106 | |||
| 68514b8eae | |||
| 88a8e35793 | |||
| ec4dcb2720 | |||
| f01b64edd5 | |||
| ef0b2af84b | |||
| 90af2e0c3c | |||
| 344393244e | |||
| 96c75eb2d3 | |||
| 5594cbd1a8 | |||
|
|
cb87abf19a | ||
| bf21399113 | |||
|
|
f0fc8a644b | ||
| a668234f2a | |||
| 6e80450e37 | |||
| 5639be6713 | |||
| d36da3dc59 | |||
| e83c743908 | |||
| 3410103859 | |||
| 2685d9ebec | |||
| c5574b3d93 | |||
| 832d65bac8 | |||
| 8394f43b3f | |||
|
|
c77ab16f3b | ||
| 2d10a3a69e | |||
| 90714961eb | |||
| b2586adda0 | |||
| d11dbbccb7 | |||
| 1390afae0e | |||
| 3ec2d5e757 | |||
| cc0d0407af | |||
| 1e9e437b1c | |||
| eae75e24f9 | |||
| 045d672d68 | |||
| caeff7de55 | |||
| b2785070bc | |||
| 545dbeecca | |||
| b4fa69642c | |||
| f84fcc035c | |||
| f556eb9607 | |||
| b8aa2ffc38 | |||
| 2be79ead78 | |||
| c900048329 | |||
| b7850eaa29 | |||
| ec5a5c8d15 | |||
| 94c51196cf | |||
| 35c18c807f | |||
| da3d44fc2a | |||
|
|
b9b9ab9006 | ||
| 5df23c79e7 | |||
|
|
ee4d49752d | ||
| d26bf25359 | |||
| b1edc7b2fb | |||
| 459fb086d0 | |||
|
|
7872391c54 | ||
| c2f4171ee9 | |||
| 9fdfa1c22f | |||
| 8ad3018fe7 | |||
| 64883aa424 | |||
| 6a41f68289 | |||
| ffd96b03e5 | |||
| 961dd5a56e | |||
|
|
f5cb8ba23d | ||
| 6bfe9ea2af | |||
| 134b9ce2f2 | |||
| 54582fb676 | |||
|
|
2e9864be2a | ||
| 73f01522ed | |||
|
|
73a67ebdd6 | ||
| 92c000f63e | |||
| 665d278b52 | |||
| 9393775e90 | |||
|
|
fe441c7afe | ||
| c865ff433b | |||
| 7eb729e809 | |||
|
|
f667fbff55 | ||
| 7c6b5bb200 | |||
| d00ee1a35a | |||
|
|
7364e19051 | ||
| 97263ba55d | |||
| a9cccbb85f | |||
|
|
732bdd5a1c | ||
| 2934d8fd7d | |||
| 992555e80e | |||
| 4a1348403a | |||
|
|
6353a4dc58 | ||
| 9e45f493c6 | |||
| 66fc8c43db | |||
| e4761d52bc | |||
|
|
893eccfa07 | ||
| fc3dc8276a | |||
|
|
9ab5e3354d | ||
| ec607798b5 | |||
| b01a2d15de | |||
| 7e3f40cb20 | |||
| 7a38224198 | |||
| 5a4f1c3cce | |||
| f5287e42ca | |||
|
|
3ec02b8ead | ||
| d25e4b3093 | |||
| 0b81a1402e | |||
| 891ba787e1 | |||
| 85a85a83e4 | |||
| 449b4e6882 | |||
| fba98f5ed6 | |||
| fda19b6103 | |||
|
|
d6d2325506 | ||
| d4354f1a65 | |||
| 7ebe82cf9c | |||
| 3029f8d263 | |||
|
|
00d3aaeb22 | ||
| a8bd5cbbeb | |||
| 48bcf77deb | |||
|
|
a24f3b72bd | ||
| e916e134dc | |||
| b9b18b063c | |||
| 753460c594 | |||
| e6f8e3e6e3 | |||
| 952c6da41c | |||
| adb1d0b26d | |||
|
|
15efadfa9e | ||
| f09cc4996b | |||
| 01a81156cc | |||
| 89ee88d734 | |||
|
|
23880fdb91 | ||
| 10628d1c69 | |||
| 897e54cb86 | |||
| 85da840688 | |||
| 10348168eb | |||
| 4a2570f96a | |||
|
|
65a4635b09 | ||
| 3a5264b783 | |||
|
|
b5866f2a90 | ||
| a35913ab78 | |||
| cc27602454 | |||
| 8920a1132c | |||
| 536c76b000 | |||
| d054f7f9cd | |||
|
|
16bab8f0fa | ||
| b7aa774eb7 | |||
| cef521390a | |||
| f4cda4ec36 | |||
| 628b65a569 | |||
| 142f0fc6c9 | |||
| fe0f3f63a2 | |||
|
|
dd3f5fdb0c | ||
|
|
b7dd2fa05d | ||
|
|
1cf471704c | ||
| 54f8fdeec2 | |||
| 7ea45e4c0a | |||
| 5b93e616dc | |||
| 2c4176de32 | |||
| 114dcea023 | |||
| 3c6b5f3573 | |||
|
|
258b9b610f | ||
| 717d2b1ad0 | |||
| 3cdd7027d6 | |||
| c5dc755cb6 | |||
| 89d13494c0 | |||
| 6fe6f55df8 | |||
| 2f38ecf505 | |||
| c4ba4b4702 | |||
| eab13531d4 | |||
| 62d78dbed8 | |||
| f5a4f1b275 | |||
| ebe761b1cf | |||
| 6e754f2f7b | |||
| 39cd3d1f4d | |||
| 778d595e6e | |||
| 5f4622fdcd | |||
| 9664609bbd | |||
| aa92699562 | |||
| edc45d5d3b | |||
| ed732232f2 | |||
| db04b3f98a | |||
| fb7c5d9d47 | |||
| 7d7df74500 | |||
| 909ba46f66 | |||
| df84ad25d6 | |||
| 178cfb17f3 | |||
| fcef7ec3ca | |||
| c1e758c9df | |||
| a06c98d52f | |||
| bf49153973 | |||
|
|
f40cfb7d25 | ||
| dcd4527c75 | |||
|
|
225ea91f43 | ||
| 172ca05efa | |||
| b3e2c8d9df | |||
| 934327052d | |||
| 24a94b4965 | |||
| c5385541a7 | |||
| 5be2d5eb22 | |||
| 3688cf6145 | |||
|
|
1328699da0 | ||
|
|
1b0e42c79e | ||
|
|
6ca80e3d8c | ||
|
|
d992541a78 | ||
| d13adc707e | |||
| 90d00d0d9a | |||
|
|
fbc9685b3c | ||
| 5507cfdb7b | |||
| 2c8de3e7be | |||
| cbba4adb19 | |||
| b1e36d8a71 | |||
| af6403d752 | |||
| 232db27a15 | |||
| e04fc75be0 | |||
| 5fd4c84a18 | |||
| ce65fb9234 | |||
| d8d8c630a5 | |||
| 08558b1581 | |||
| 21637f170f | |||
|
|
5013ffd38d | ||
| f471368d21 | |||
|
|
8ddf0168fd | ||
| 5a4ba1297f | |||
| 0608c531ed | |||
| 5d242de4e2 | |||
| b7efc0394b | |||
|
|
16681d0193 | ||
| 31ea0865d1 | |||
| 602ad3390c | |||
|
|
d8891a1767 | ||
| 2400a0b353 | |||
|
|
de0248d59a | ||
| 3eb72d23f8 | |||
| c0606112b2 | |||
| 5decb77f1e | |||
| a04b9487f1 | |||
| 8835cd4ab5 | |||
| b17452a949 | |||
| 2072eee3a4 | |||
| 4321c0e6b3 | |||
| f1f98b2624 | |||
| ec37cee26c | |||
| a8adaf7e5a | |||
| 84c763c39e | |||
| 33e86c4f79 | |||
| c00f93a1d3 | |||
| 81100f21b4 | |||
| b8a720919f | |||
| cba37ae2fc | |||
| 5d15cbd4ea | |||
| 5f87606cf7 | |||
| 9b22efe3d4 | |||
| 675c3b27aa | |||
| ddd9e70d05 | |||
| 30ed2049e4 | |||
| b8092ed6dd | |||
| c80c0962dd | |||
| 63c223b237 | |||
| 07aa49accc | |||
| c595c541fb | |||
| e6ec8b65ce | |||
| bc8c455094 | |||
| 507820d25e | |||
| 35e08295ce | |||
| 9e581d8608 | |||
| dcb8e69aac | |||
| 617b354d03 | |||
| 76b095b344 | |||
|
|
5ff05e8025 | ||
| b973a16247 | |||
| 0cb5d7507f | |||
| 2851ebf126 | |||
| 2f5b1bb205 | |||
| 5528336698 | |||
| 4eda80baa8 | |||
| 02918d4bcd | |||
| 8315e3ea96 | |||
| cae4a2ee13 | |||
| 26762eee73 | |||
| 2f05fb12a0 | |||
| befdf2e1d3 | |||
| aac7b98913 | |||
| baa9b5e6bb | |||
| 99c919811a | |||
| ac7a4e739e | |||
| f8936aa7bc | |||
| 6933c44571 | |||
| dfb2f4b3af | |||
| 6601aebddf | |||
| 4d2ca9587d | |||
| 694a5ceab2 | |||
| e522093f6c | |||
| feefa2e90a | |||
| 3709569cd8 | |||
| 052b4870bf | |||
| 5cb413a911 | |||
| 80e77dda3f | |||
| 7a3d554d6c | |||
| ab0fb063cf | |||
| caec558bea | |||
| 2d44b62fef | |||
| f9cc72ba4a | |||
| f8544d0600 | |||
| c96d361b4d | |||
| fa5305f332 | |||
| b996bdea93 | |||
| 1bae6538f7 | |||
| 5673964de4 | |||
| 25623170bb | |||
| 2aa5739cd8 | |||
| bdff56843f | |||
| 6e563e6d74 | |||
| 334a3fb6fe | |||
| a1d130a674 | |||
| e14f2ea247 | |||
| 3f478eef8e | |||
| e929274b51 | |||
| 526c0301f6 | |||
| 7e7a19829d | |||
| bc3854b4c4 | |||
| 193bf20a3e | |||
|
|
4cc5194939 | ||
| 46a525ae09 | |||
| 6d006a357e | |||
| fad73b0922 | |||
| d2d2d4658e | |||
| 84e6ce2b48 | |||
| b91a9963e2 | |||
|
|
25fcf93185 | ||
|
|
26bb2005c1 | ||
|
|
ee58805ae9 | ||
| 1bfc02a892 | |||
| 0c7dc4dbee | |||
| 42e9859ccb | |||
| bf9b6b6046 | |||
|
|
c10eac5190 | ||
| d01ccf1208 | |||
| 703b9be83e | |||
| e0fea93983 | |||
| 84ceef47cb | |||
| 255ed5944f | |||
|
|
0dc2b635ae | ||
| ebd1cfe203 | |||
|
|
43b9297476 | ||
| aa2f4a0167 | |||
|
|
ad31d046d4 | ||
| a67ad5a7db | |||
| 90d4fb245a | |||
| 19f55fde6e | |||
| 227be5258e | |||
|
|
913190bfeb | ||
| 0b0097f367 | |||
|
|
97ac8264f2 | ||
| d9cca6ccc9 | |||
|
|
28d0b1fd25 | ||
| cbcfe4cb9d | |||
|
|
70c14d3369 | ||
| 0bcf7edab5 | |||
| 0f410c29f7 | |||
| b451a6da25 | |||
|
|
e6c7bc1544 | ||
| d1ff8f1cac | |||
| d967100c14 | |||
| 8461c20e40 | |||
|
|
52924f06d2 | ||
| fad4e7bd7d | |||
|
|
666ee1a083 | ||
| 8f05e47363 | |||
|
|
67d8abf7bf | ||
| 30842a6659 | |||
| 3ee500d339 | |||
|
|
c5ade905b0 | ||
| 8e6784bf21 | |||
| 4eacbca674 | |||
|
|
149fe72324 | ||
| 43f752afbc | |||
| 19efde1fda | |||
| 98f267057b | |||
| 4f98894ce4 | |||
| 8cb225779e | |||
| 57ff303c39 | |||
| 29cae6ae6c | |||
| e0c761bdf0 | |||
| 6ee1a8dc52 | |||
| db69532dde | |||
| d4c47c16e4 | |||
| a28733521b | |||
| b957595944 | |||
| b985d42336 | |||
| 9d467a13bb | |||
| 4629d39980 | |||
| 11d245dadf | |||
| bb72b53f19 | |||
| 3bef89d625 | |||
| 792b1348c1 | |||
| 7399cd7d45 | |||
| d6347f6d4a | |||
| 3b5585193f | |||
| 5efa218247 | |||
| e5d312dbf9 | |||
| 38e1c07e0b | |||
| 809c9ea237 | |||
| 08a41f039b | |||
| 3dd14307d0 | |||
| 4122c11372 | |||
| 9360a9cd68 | |||
| 24746dab08 | |||
| 8879cba15d | |||
|
|
1b38c60f57 | ||
| 7c944b27dd | |||
| 523e54eeb2 | |||
| 7b08a3f1bc | |||
| 018826e1a3 | |||
|
|
a36b0b78b3 | ||
| 19964d39c1 | |||
| 31fd04456b | |||
| 76cf2e8fa5 | |||
| f6b61c935e | |||
| c736a04b4f | |||
| 14432fd6f9 | |||
|
|
047d84acb5 | ||
| 8de4e6a0ee | |||
| 90fbb0e9ac | |||
| 041f36ba00 | |||
| 9e6cc873cb | |||
| 20a6ced7b5 | |||
| 34d9682cc1 | |||
| 4ac797406a | |||
| bb3cdb5d85 | |||
|
|
245923d825 | ||
| 41c048594e | |||
| 1360d2d066 | |||
|
|
6286f03432 | ||
| c7a99cc61b | |||
| e4aae5147c | |||
| 2a2e37ca97 | |||
| a1489959d1 | |||
| 4561e37167 | |||
| 9fa04c9a90 | |||
| aa4951b7fc | |||
| 53d08c261c | |||
| 74058243c6 | |||
| 62223d53be | |||
|
|
06d33758eb | ||
| af85245b06 | |||
| 01d8ebbd40 | |||
| 1c1cd1f434 | |||
|
|
aaa801964c | ||
| 01b490dff1 | |||
|
|
1be1441352 | ||
| 0b47b56fb5 | |||
| 0a1251ad0b | |||
|
|
a506bebc94 | ||
| 26147acfa0 | |||
|
|
f1d0be9f61 | ||
| 45c1e9d28c | |||
|
|
df3db9e632 | ||
| 8c1e672da7 | |||
|
|
4bf5e68a9d | ||
| 0d4b157b1b | |||
| 5fd207c84c | |||
| 6829baba67 | |||
| b9983929fc | |||
|
|
d540e7497b | ||
| 0e0cc248fa | |||
| bcf6808e49 | |||
| e2e27765ee | |||
| f3b07bda65 | |||
|
|
f555f1084f | ||
| 212be88efe | |||
| c131f0ecda | |||
|
|
e3afb4b481 | ||
| ca61f70d37 | |||
| 90850ebfa3 | |||
| 771a6d6e08 | |||
| 55a6d17bc8 | |||
| 649990f2e1 | |||
| 48468a1c2d | |||
| dd685b664d | |||
| 36898e3e11 | |||
| 737450ba6e | |||
| 0f63145319 | |||
| 6a942db113 | |||
| 6119adbe2d | |||
| c31af5cbf4 | |||
| 784629f11b | |||
|
|
a581b7cdef | ||
| d6918621fa | |||
| dc54ec241e | |||
| 2f94674479 | |||
| 10b94514a7 | |||
|
|
f5eacd6016 | ||
| 7383317f0b | |||
| 26fa9378f5 | |||
| 9f37033e5c | |||
| dc4ff9b694 | |||
| 3c9dc0a127 | |||
| 2afbe1c6fd | |||
|
|
309b5b9563 | ||
| f64f0ebf4e | |||
| dd646a155b | |||
| 6db38826d8 | |||
| a18fd9b05c | |||
| 611c7b7b21 | |||
| 3961e95a5f | |||
| d6e6a53301 | |||
| e500026017 | |||
| 84632b6296 | |||
| 9b79e93967 | |||
|
|
88e7ad0411 | ||
| d0e96eab91 | |||
| 6ac78d9eb6 | |||
| 5d830ae2f8 | |||
| 178f844420 | |||
| adf3df510e | |||
| f58c03c429 | |||
| 15f9edc5c7 | |||
| 07f77024ab | |||
| 5b8ff7e612 | |||
|
|
1182e7405e | ||
| 9c6d8ef8e6 | |||
| eb807fb205 | |||
|
|
0602fb5569 | ||
| 1dd78c3ea1 | |||
| 3175de4576 | |||
| dfd1f172bb | |||
|
|
0f6cf01fd3 | ||
| 3e9e7922b4 | |||
| eb480c8838 | |||
| a9bc4fe914 | |||
| 3bf1573e36 | |||
| de6fd60f41 | |||
| cf72ebdfd8 | |||
| 6e44452807 | |||
| 88c6abb53c | |||
| 27fd93f590 | |||
| 8b391f03c2 | |||
| ea3033e22a | |||
|
|
c88dff350e | ||
| 73e1a312b5 | |||
| 28c2b6dc77 | |||
| 0348cc55c6 | |||
| e439918e05 | |||
|
|
a5a7f405b3 | ||
| 4cd620fe2b | |||
| b1826a60d2 | |||
| 50ec1c59d2 | |||
| 36d604ec49 | |||
|
|
7d6407afcf | ||
| 92358db548 | |||
|
|
161c6a69b1 | ||
| 215e053b6d | |||
| e8a99af37f | |||
| de258a2568 | |||
| 728db39ab7 | |||
|
|
d885870302 | ||
| 2c87079686 | |||
| e89785c1d2 | |||
| 38f8eff1c5 | |||
| 077394ec99 | |||
| 7c23f85f75 | |||
|
|
20c1ceab92 | ||
| e5585caa09 | |||
| a11b49befe | |||
|
|
58027e4607 | ||
| 1285641746 | |||
| 843ac18d2d | |||
| 4383a202f2 | |||
| cda412f6a9 | |||
| f26b84c799 | |||
| 2fc3fd3ef1 | |||
| 57d04de8a5 | |||
| 3e43c22a42 | |||
| ea643b58d8 | |||
| d366490fe5 | |||
| 83093f4c85 | |||
|
|
99fcfa5223 | ||
| f6786b367b | |||
| eb373af6ed | |||
| 037e7e907b | |||
| 80844562e9 | |||
| 2a935845bf | |||
|
|
3acbad4606 | ||
| 1973f6a2b0 | |||
| 85e6a678dd | |||
| 798c52f23d | |||
| aee405f9e2 | |||
| f82ff2d482 | |||
| d9d9721249 | |||
| 8ef84763c6 | |||
| c4f6db4431 | |||
| 60a7e7f885 | |||
| 7e3c36cd27 | |||
| 5304abcd00 | |||
| 19d94893cb | |||
| 485424b5c9 | |||
| a8af7bed62 | |||
| f63e7df28d | |||
| 7e81da6741 | |||
| 0c8e69c252 | |||
| da8881dd1e | |||
| d10c0a2fb9 | |||
| 34ed4b2f25 | |||
| a109b148f6 | |||
| 65c7eeb9c9 | |||
| e45b9b4101 | |||
| 8c9bfd7e2d | |||
| 208e418bba | |||
| 05278bdacb | |||
| 5b11b35d5d | |||
| 9f5844c4de | |||
| ae888e6d48 | |||
| f5b4af6e38 | |||
| 84035e0cb1 | |||
| 6d5c3e81c8 | |||
| 16db84793d | |||
| 8b8a63c352 | |||
| 698cf0d263 | |||
| 6385d49eeb | |||
| 43e69909cd | |||
| aeb0f866c8 | |||
| 3e5c005f25 | |||
| bde27382b7 | |||
|
|
c17df789de | ||
| ca6a84fec4 | |||
| 1c952ca4cd | |||
| c2c7073cd9 | |||
|
|
6c84be1a82 | ||
| 16c90290f9 | |||
| cf707df5e0 | |||
| 078f1b763b | |||
| 6060faa3ac | |||
| 5059157515 | |||
| 49924c6b2b | |||
| 6ff1781372 | |||
| 29fd4d5220 | |||
| 484dc13977 | |||
| 59fc82c788 | |||
| 9a4c9154f1 | |||
| 9949bb2493 | |||
| 07fc6ac8c4 | |||
| 6a93c3e90f | |||
| 7bd88335c4 | |||
| 9893e3bac2 | |||
| 946e886799 | |||
| b84301debc | |||
| cb5c95ce03 | |||
| c0a7e1ddba | |||
| 220af054bb | |||
| a9634f0249 | |||
| fd77048d4b | |||
| 40cb632c80 | |||
| 2313af825e | |||
| 762efdaa9a | |||
| b58cafb47a | |||
| 308d909e72 | |||
| c88eb37b1a | |||
| e3189d39f4 | |||
|
|
de351c7473 | ||
| 9d08b7de34 | |||
| 5cf0745da0 | |||
| 10009b65be | |||
|
|
80c1c0b8b4 | ||
| 9169d8899f | |||
| 5166e05dbe | |||
| e6e9dcb48c | |||
|
|
712550cec8 | ||
| f9061088fe | |||
|
|
c53390d038 | ||
| 6fb93b19c9 | |||
| e07e8391e1 | |||
| ccd8c056b1 | |||
| 32ee0cf14c | |||
| 9a6c02dd9a | |||
| 3dd77715d8 | |||
| 0f99cdb115 | |||
| aecd0726e5 | |||
| 258b3495a1 | |||
| eb52d1a79a | |||
| 1ad16b2b94 | |||
| fc28136936 | |||
| 575f3c9ce4 | |||
| 7c9ca83b46 | |||
| 0d6c4010ff | |||
| cb6d9762d5 | |||
| a4414d2083 | |||
| 5bcae1b00c | |||
| 5c01f36fe7 | |||
| 84b7d1123e | |||
|
|
b819f345cf | ||
| 5278116f49 | |||
| af36366a89 | |||
| cabba7e2d2 | |||
| ef6cc77dc2 | |||
| 4b6de5cd9f | |||
| 403e35c6c5 | |||
| e1dbdc5886 | |||
| 9e0a64a51f | |||
| 971cc3a42a | |||
| 1c109ee86f | |||
| b84106ae70 | |||
| 05881be9b9 | |||
| 06a7b3da19 | |||
| 9d77f66e47 | |||
| 85afff4848 | |||
| 8498eaa16d | |||
| 1e78341559 | |||
| e4ed5a1d19 | |||
| 7b247d1e6f | |||
| f98a7298c5 | |||
| 8e4e9ddf62 | |||
| aea7fbe385 | |||
|
|
392442b7b7 | ||
| cce3102beb | |||
| fdf2e3854e | |||
|
|
95011544c1 | ||
| fc97eb1c14 | |||
| fc6ec859fe | |||
| 582ea54ed5 | |||
|
|
6caedb5b06 | ||
| 307135d495 | |||
| f786be41e7 | |||
| e18ae543ee | |||
| ab606e0629 | |||
| b6cda24231 | |||
| ad4714418d | |||
|
|
a77e37ac65 | ||
| 296b1cfe86 | |||
|
|
c225570c00 | ||
| 4b1db3d02f | |||
| c358954e6d | |||
| cfea5d498f | |||
| e69136534b | |||
| 400ecfe690 | |||
| c64287fd51 | |||
| 0edc306aa8 | |||
| 839fec3a79 | |||
| db2aab88f4 | |||
| a5195956c2 | |||
| fc0b79f018 | |||
| 1b42bafc49 | |||
| 83a2ea9b4e | |||
| 4bf5fd712d | |||
| 8a42898afe | |||
| 6577b37b76 | |||
| ccb02f14a6 | |||
| 0bd476340a | |||
| 84c9436546 | |||
| e9d220b54d | |||
| adecec811e | |||
| c3285f8b2e | |||
| 864b5bb985 | |||
| 39bf6b811a | |||
| c7d0744fa3 | |||
| 726cdd8dc7 | |||
| 93f895d4e4 | |||
| 00ed75502b | |||
| a4b1ca831e | |||
| 18e59c9538 | |||
| 3fa51efd16 | |||
| fcd747c4a5 | |||
| 4d9dd26337 | |||
| 7996722f04 | |||
| b0aa889a1f | |||
| 4b1d6acb77 | |||
| d00da44fcb | |||
| 1da7d08887 | |||
| a04d24d410 | |||
| 055422bac9 | |||
| 4e09a802b0 | |||
| a66bab02ab | |||
| d217ebfc4f | |||
| 31cafa59ab | |||
| c66b9794ee | |||
| ddd3d3a33a | |||
| 7816dbf264 | |||
| f78279a271 | |||
| 411174c07c | |||
| 28988eff93 | |||
| 5ee9a6790e | |||
| 6cce658ea3 | |||
| 3c54e7943b | |||
| c0923f7d4f | |||
| 7973d369cc | |||
| 2b70534fb2 | |||
| 1d0a4c371c | |||
| 18eee3f8e8 | |||
| 2ae280e6c9 | |||
| baf56d71e9 | |||
| fe6649a0a5 | |||
| 452623a56e | |||
| a361c31c11 | |||
| fb15ca1d76 | |||
| 3d319c7c76 | |||
| 069bef1f0d | |||
| 3b0e624d92 | |||
| ee13c8f519 | |||
| 51eff5f671 | |||
| e544ba53b5 | |||
| e28a7124a2 | |||
| dd0c46286c | |||
| a1b39dcff5 | |||
| a3022c7f80 | |||
| f16dfd6819 | |||
| 9f2bc6e0e1 | |||
| f2c4831786 | |||
| 3f5b6a0b00 | |||
| 1bf02abc1d | |||
| 5dcf932a81 | |||
| f2f02e4b1b | |||
| 47ce5e05bc | |||
| e878f79ffc | |||
| 5e6774ee63 | |||
| ec4c3f3574 | |||
| e7e9379c09 | |||
| 65c303272c | |||
| 2be356a875 | |||
| d60e1291c8 | |||
| ae342c71a7 | |||
| ec79bfc93b | |||
| f3006b55b3 | |||
| 08aa864fa0 | |||
| 93b39f4fb0 | |||
| b189241745 | |||
| 3206e62583 | |||
| 299b216c48 | |||
| fe53068055 | |||
| 0a8b921327 | |||
| 1119371bdb | |||
| 980129a8ec | |||
| 5554ff3350 | |||
| 3fd55fd5de | |||
| d4f9f5bcfd | |||
| 318cba0d43 | |||
| 173aae22b4 | |||
| f86e30634d | |||
| 6f9b9bbbc1 | |||
| fa7ad33474 | |||
| 834978adfd | |||
| e70fa6d257 | |||
| 326179479c | |||
| 909b8a3f88 | |||
| 973cbf4f9d | |||
| ca52d5bf65 | |||
| 5427a2b1ee | |||
| 7b925e5504 | |||
| 6b7752e14a | |||
| f13a0c68dc | |||
| 017d96872a | |||
| f11b8637bb | |||
| 5452ee3229 | |||
| 41a76e1ac6 | |||
| 2e3d5fda44 | |||
| d26e2fb06f | |||
| 7aa0bf59f2 | |||
| 4cc72c7164 | |||
| 10dffd71f7 | |||
| 56c4710c55 | |||
| 2bebf3503f | |||
| 8e61c89bd3 | |||
| 2e4fe6e434 | |||
|
|
be1e03828c | ||
|
|
55d9700401 | ||
| e55f79b9b8 | |||
| 5684fc217f | |||
| f0071b5e82 | |||
| 7bb7ce2ef3 | |||
| 47569c475f | |||
| eb8886b4eb | |||
| 83711db1c8 | |||
| 6858a827f9 | |||
| 66c8900a30 | |||
| 892b1144e1 | |||
| 06d2b381b2 | |||
| f8e3df36c8 | |||
| b69c986dcd | |||
| 3501323a12 | |||
| 0671455b59 | |||
| c4727b90da | |||
| a14c4b566e | |||
| 33353dd2a3 | |||
| 3b436a15c1 | |||
| 49f45314d1 | |||
| 679aefba86 | |||
| 255d297d61 | |||
| c1a59d63fc | |||
| d75c729e29 | |||
| f50be939f5 | |||
| 3394a8892f | |||
| 02e5f595fd | |||
| 1bee5e7429 | |||
| a8b6ae4c21 | |||
| 154c482e67 | |||
| 723d5e3959 | |||
| 73f8226854 | |||
| 3826bb53aa | |||
| 7fbe40e73f | |||
| e3606a1ca4 | |||
| bfe0e0a31a | |||
| 643338eab1 | |||
| 0777b65919 | |||
| c9ef1cd141 | |||
| b9a1d142d1 | |||
| 7959167554 | |||
| fcf20958c9 | |||
| 92b26bd14f | |||
| d6b414f022 | |||
| dd3092639e | |||
| 0e633491bd | |||
| 2250770f66 | |||
| 1d8448d6bb | |||
| 6eda36aec2 | |||
| 09d656ef60 | |||
| 19aadb581f | |||
| c27248af98 | |||
| 829a2516ba | |||
| ac0dac2561 | |||
| a308c630fc | |||
| 661a346060 | |||
| b251de30b1 | |||
| 716c0a6d46 | |||
| ac8c048b28 | |||
| 00afaec282 | |||
| 727c36ebc7 | |||
| 2f5bd3b5ed | |||
| 49ca076d6c | |||
| f63e375b13 | |||
| e480582fcf | |||
| f45dd1bbf5 | |||
| d37f651f23 | |||
| 4d2793d4dc | |||
| fb3404e92f | |||
| 2cdb62cbd3 | |||
| a3d231f619 | |||
| 34551409d9 | |||
| be83c2c3bb | |||
| b963302a96 | |||
| 5238d8f263 | |||
| 384419fa30 | |||
| d18952a72e | |||
| f1478369b5 | |||
| face89efc5 | |||
| 44f5f7f270 | |||
| a30d68a3d9 | |||
| 0e5f3bc23b | |||
| 0f97e0db44 | |||
| 79ed3f3a4c | |||
| 869a321c14 | |||
| f338d8bd8e | |||
| 2a60cd981d | |||
| 3504ca24f2 | |||
| ebcd450f55 | |||
| db8f74f848 | |||
| b34cf3fc86 | |||
| a1bb8b9816 | |||
| 3db30b1298 | |||
| 773bc41a12 | |||
| c91928c55f | |||
| 5588976f95 | |||
| 87d76d7cf7 | |||
| 18202b859b | |||
| 8309b8e493 | |||
| 50a96b198a | |||
| 6ebc7a1514 | |||
|
|
156f6d1791 | ||
| 6a013dd7f6 | |||
| 05d8181202 | |||
| 2f8217d5d5 | |||
| d9234c9b77 | |||
| 16b54569e0 | |||
| 23ed3a7e47 | |||
| d2f586dadd | |||
| c72dcf02d2 | |||
| a099a83f9b | |||
| a986082f62 | |||
| 5c9cde381c | |||
| c23ae77215 | |||
| 33a83a6d8e | |||
| 07d6ce0a34 | |||
| 940006a10a | |||
| c77b8d47fd | |||
| 58f24f0c75 | |||
| 41b11669d8 | |||
| bb04f1666f | |||
| 49c22a2d70 | |||
| 5616b90474 | |||
| 230d3925df | |||
| 5e92740b62 | |||
| f353fc3e5a | |||
| 8f5936135d | |||
| 2954b92963 | |||
| 57141b3a90 | |||
| 3b0db0359c | |||
| ad66ecc628 | |||
| 766dc32287 | |||
| 761ddd72e9 | |||
| 16e312bbe7 | |||
| d36b36e1d3 | |||
| 9eabb10e41 | |||
| 7be3cdd5e8 | |||
| 2f5842622c | |||
| 3f52744747 | |||
| 91a2558bf4 | |||
| aa397eb8e7 | |||
| 7663bdbc14 | |||
| 51a9b551fd | |||
| 725572745b | |||
| 77e65252fb | |||
| 1fc0a106b6 | |||
| e8d72985e6 | |||
| 2ecc29269f | |||
| 299eaa06b8 | |||
| 154c7cbdbc | |||
| 06bb5b5bb0 | |||
| a718db5457 | |||
| 2e17870d03 | |||
| e70113c280 | |||
| 4240f49de9 | |||
| 9b0b8015df | |||
|
|
998938e7ab | ||
|
|
faffdaf535 | ||
| fce4cc332c | |||
| d211aa168c | |||
| 2430cbb11a | |||
| ddef7b1182 | |||
| f5af7688a7 | |||
| 1faef6799b | |||
| 34e6bb75e0 | |||
| 332aa43419 | |||
| f96ff13843 | |||
| f25066e23e | |||
| 35ce2abae6 | |||
| 897e236719 | |||
| 916b3dff6c | |||
| 846600ef99 | |||
| 8cbae18986 | |||
| c81616894c | |||
| 408c9c291b | |||
| 824032b338 | |||
| 99c2c7ce4c | |||
| 93d4749d9e | |||
| 163776c5ff | |||
| fd907e1dc9 | |||
| 6c306ad97f | |||
| 0357087b06 | |||
| 85a9ee4b29 | |||
| 5bab1ab4da | |||
| 0312c5ceb0 | |||
| eca12b2cf3 | |||
| 3476b4c30a | |||
| 46d7f83cc4 | |||
| e1ef876db1 | |||
| 8f1a89f978 | |||
| a14301f769 | |||
| 54bd8f1d5c | |||
| 9d05edf09f | |||
| a0c228e352 | |||
| dfaf09735e | |||
| 6f2cc03044 | |||
| 2ff6d17d23 | |||
| af83750220 | |||
| 70dbb9eab5 | |||
| a58fd4476e | |||
| 5f7f45c648 | |||
| ca5b315725 | |||
| 6a33354b2b | |||
| 821683d484 | |||
| a2e4fc8085 | |||
| bb37fccee3 | |||
| e9a01b5056 | |||
| c1dec87784 | |||
| 74b3118bc3 | |||
| 853d5045f0 | |||
| 30db5569ad | |||
| 8c841e93a0 | |||
| 46a752480c | |||
| 1dddc3c5ba | |||
| 61d90ef8e7 | |||
| c3241a5c17 | |||
| c6d1a44d43 | |||
| 8ce5d6dfbe | |||
| a8c8f922eb | |||
| e7d458b2a9 | |||
| 9cba031049 | |||
| 73e5cc4fbd | |||
| 3df5ae86d4 | |||
| 542610f5a7 | |||
| 1b1d3aec8c | |||
| 829f4def27 | |||
| cc9e628346 | |||
| 921af80598 | |||
| dae5d2d062 | |||
| 6ee0496c4c | |||
| bbcac5f899 | |||
| 7fb006b2ff | |||
| 6bfa72feb6 | |||
| 8867799825 | |||
| 12c90f5e6e | |||
| 8ed608e9cd | |||
| 1c0dbe76f9 | |||
| 14ad247168 | |||
| b114c768dc | |||
| 58129d5209 | |||
| 54f5b44e04 | |||
| 85f64b72cb | |||
| 626c64afb2 | |||
| e639fc5746 | |||
| d988dce5d8 | |||
| 34f03d2ccb | |||
| 4e66ed3a32 | |||
| fde9da1af5 | |||
| bad42e8e48 | |||
| c562f9481d | |||
| caa9b598d0 | |||
| ffa16648ae | |||
| a7a06ea7bf | |||
| 99de06abc0 | |||
| b538e646bd | |||
| 6088386e51 | |||
| 404a38d990 | |||
| 91e3900d12 | |||
| 2b0dfcd460 | |||
| f561a1da4f | |||
| 34b435ffca | |||
| 5b92f109b9 | |||
| fe68bf7226 | |||
| 1e4989857e | |||
| 56055000d9 | |||
| 9365139430 | |||
| b22f240719 | |||
| 5b0e8ad2da | |||
| cb794a946a | |||
| 446e39fb68 | |||
| 9276dd1bb8 | |||
| 1972df1f2d | |||
| afb9f430b3 | |||
| 52e9ff38fa | |||
| 37f4244484 | |||
| 485b0605b4 | |||
| 30e2ea56c8 | |||
| 371b6651f0 | |||
| 37448dc827 | |||
| 6e197f81ef | |||
| bdd0634f61 | |||
| a8019d1ec4 | |||
| d33ce43ad3 | |||
| cc165c4cde | |||
| 1f6e6957ea | |||
| 7b8bb6380d | |||
| b432b1c165 | |||
| ce6f7153b3 | |||
| 1cc4aee6f7 | |||
| eafaf13a3d | |||
| 7352f30d91 | |||
| 31db220719 | |||
| 000a4cccf1 | |||
| 1cc6953415 | |||
| 519951ad69 | |||
| b97b8c2c9b | |||
| 27b3bc1150 | |||
| c66ad2b7fb | |||
| f040b590d5 | |||
| a36d2f2665 | |||
| b0c3e5b7e5 | |||
| 3d3cde4353 | |||
| da9c57cf84 | |||
| 9915ed35d0 | |||
| 97829cd944 | |||
| c355a120b4 | |||
| 1d9bc68e28 | |||
| 778e1860df | |||
| 76b9298efd | |||
| 371cc9c4c8 | |||
| ab7c4792e2 | |||
| 7e2c192d40 | |||
| 6874de5cda | |||
| bd2ab7d529 | |||
| 152fadfc87 | |||
| d3b3b526db | |||
| 00002a8540 | |||
| a5bc06acf6 | |||
| 2dcb8b257e | |||
| fd1f64a6e2 | |||
| fbf1a36e00 | |||
| 52d66b33cb | |||
| eb0fe483b1 | |||
| acc57ee993 | |||
| 0397090458 | |||
| 5f6df46c90 | |||
| 3030849bd2 | |||
| 7a1bc2ee39 | |||
| efc715678b | |||
| 65ec08d40f | |||
| 7855df5aa2 | |||
| abbe16eb87 | |||
| 45645a52b5 | |||
| 48d193141e | |||
| 264dd392f6 | |||
| 3e9b47cbf3 | |||
| b350747eab | |||
| d078b285a2 | |||
| 7b5d9bb3c1 | |||
| 346f07657d | |||
| f4859ef7f6 | |||
| 40bc9798a9 | |||
| 98899cd76e | |||
| 6da57d6ccc | |||
| 0cef478d71 | |||
| 30e0db5adb | |||
| bb6a64c0dc | |||
| d940a12ed5 | |||
| fe9cd42210 | |||
| ad503863ad | |||
| a660014740 | |||
| 637a4dacb9 | |||
| 79c8081c30 | |||
| 0903589e5b | |||
| 777bfa1145 | |||
| 4826dbd8a9 | |||
| 70507e9f65 | |||
| b8d6d9ed60 | |||
| d62531c700 | |||
| 96d5a817d5 | |||
| 6436a933c9 | |||
| db79be96ed | |||
| d2d79092ca | |||
| 8f4fca5a6c | |||
| dbe9fd4222 | |||
| d0bc04e53b | |||
| 49858199a5 | |||
| 7750fb7b38 | |||
| d1d276d026 | |||
| 70aca29ce1 | |||
| dc17d67dc9 | |||
| 407dc56f40 | |||
| bdfc044783 | |||
| 325a113ac6 | |||
| 7e55e1e29a | |||
| d604c966b2 | |||
| 573e3192fc | |||
| 8ec3b66f20 | |||
| 5b9a414688 | |||
| 9553add27a | |||
| fb3cd54ce0 | |||
| 1ce5cac3ba | |||
| fa1819feb8 | |||
| bd873b48d0 | |||
| 37e3850b43 | |||
| 3182bac95e | |||
| 71dc92f59d | |||
| dc5a81ea89 | |||
| e04214c14d | |||
| 1846af7271 | |||
| 58cb061ef2 | |||
| c71a59cce1 | |||
| c3d6ec90e8 | |||
| 9569b64d26 | |||
| 6f7c26adc4 | |||
| 82e4e5c1d3 | |||
| d6972bae75 | |||
| 2beae29d25 | |||
| e2d800a167 | |||
| 630e518ae3 | |||
| 2c8108a0be | |||
| 11ba771db3 | |||
| e20341d3da | |||
| 179b00e8a1 | |||
| ab287d42ef | |||
| 8b439801ab | |||
| 8e39af00a3 | |||
| 493553ec4b | |||
| 0d1d4b66d6 | |||
| 7ba7272a7c | |||
| 12d5ce5eea | |||
| a059e09244 | |||
| 2ac28e9d80 | |||
| de3f2ab4d2 | |||
| ee206f014d | |||
| 852fc6e5a7 | |||
| 17b40e5d1d | |||
| 769f36c560 | |||
| 7d1c2bac14 | |||
| 58101f889d | |||
| 009be7e920 | |||
| 74817313f6 | |||
| 1dc55d5e1a | |||
| 2a943efcd7 | |||
| dfef78fc84 | |||
| 51d092ca19 | |||
| c210429086 | |||
| 9cbec5a68f | |||
| b02a6a2d19 | |||
| e5ef7d3e4d | |||
| 1d1dbfc54c | |||
| 49867fadf0 | |||
| 0df069b9a4 | |||
| ef59c3a654 | |||
| af3ceeefe7 | |||
| 655c2c2268 | |||
| 83f48cb05e | |||
| 25527d09f5 | |||
| 32718d5450 | |||
| 7ce3160ade | |||
| f4f4e32612 | |||
| 1bb99bb90e | |||
| 0ccf70be56 | |||
| d6b68f45e5 | |||
| 41978d33bd | |||
| 85c62dd51a | |||
| ccb9d678f6 | |||
| d950501ec6 | |||
| 4edae5114e | |||
| eb925d004e | |||
| 58fafd6233 | |||
| c4c3c1659c | |||
| 5a800ac1b2 | |||
| 0e6ea7cb7e | |||
| 4078f4c67b | |||
| a44873b142 | |||
| 293c902a80 | |||
| d25993e78b | |||
| c3ef396728 | |||
| 17d46958f8 | |||
| aef3c5d694 | |||
| 50eb0137a4 | |||
| e8d1e6e1eb | |||
| e4c7a04701 | |||
| e145e2c57f | |||
| 2d892575f3 | |||
| 0c4460282a | |||
| da0e599bad | |||
| 7d2ca4ea74 | |||
| 7a1b30994b | |||
| 3994d8f0ce | |||
| aa5ffdc3c0 | |||
| db1dfb9aed | |||
| 06db6b02d8 | |||
| bf0269a7da | |||
| 486361f3b7 | |||
| ab088d9355 | |||
| c9c52cf7f5 | |||
| e4ad01dd42 | |||
| 86ac000117 | |||
| ae016172ea | |||
| 2c5ab01237 | |||
| 9b0a41c3d5 | |||
| be55e79dcf | |||
| 9265bd57e4 | |||
| 26bc566aed | |||
| 1b007460de | |||
| b007010912 | |||
| 053cd9bc01 | |||
| 37c8da89ae | |||
| 4f1001e487 | |||
| f9f074ef01 | |||
| 8df9799564 | |||
| 80e49a6ff8 | |||
| 2a3bad681b | |||
| f7c9d61e8d | |||
| 296f54127d | |||
| b8161b86d2 | |||
| 3305e51fda | |||
| 4adaa8565c | |||
| 5564b27ff2 | |||
| 0f1b98721b | |||
| 3894eebfb5 | |||
| e3070b618f | |||
| 5f144924e4 | |||
| 570f775c66 | |||
| cbdf826951 | |||
| 9933035cae | |||
| 4e3e7b7301 | |||
| 4ed5c48650 | |||
| f46c28b39d | |||
| b124aabb7b | |||
| 6e48e0c339 | |||
| 79160854b9 | |||
| f5089eeb16 | |||
| 453639bf92 | |||
| 5ca5265b92 | |||
| 88b86b2b2b | |||
| 7da0fa4379 | |||
| add4d4bb5e | |||
| 39a1c1e6e0 | |||
| 0b37a34528 | |||
| ffcceb16cf | |||
| 4b7296f99e | |||
| 5796e311a9 | |||
| 8af22d14c8 | |||
| bd5753eb27 | |||
| 6a6166c8ac | |||
| 82a5af071a | |||
| 4e6f867ba4 | |||
| 8e0a4ec34b | |||
| d5678d3f34 | |||
| cfd7030d00 | |||
| 360d94d573 | |||
| b3370f2937 | |||
| ce3e37b6f8 | |||
| 43384f8ca4 | |||
| 9ab361604f | |||
| 000b825ef0 | |||
| d00101f6eb | |||
| 2d8528ca8d | |||
| c6d900c4e0 | |||
| 15314974f6 | |||
| 60a1bfe679 | |||
| a3af4ac405 | |||
| df7a43919f | |||
| ae4f3851e5 | |||
| e0384dd6cb | |||
| d8a34e7a0b | |||
| e013eb8137 | |||
| 72917bdc5b | |||
| cf74b0a941 | |||
| e027700d50 | |||
| d7fe140ff2 | |||
| 00c0c592fc | |||
| 6603a49d7b | |||
| 3340112c11 | |||
| 741e1aedf6 | |||
| a156c14248 | |||
| d56de6d827 | |||
| 6433d6fc50 | |||
| 58950d581c | |||
| 6cd613a172 | |||
| 1fa57de2c0 | |||
| 5ebdbc74ad | |||
| 9c130746fb | |||
| 7d83f29db7 | |||
| 729ea2df8f | |||
| c560d3fced | |||
| 590a246fc0 | |||
| 5ea712b943 | |||
| 6329ce3fd6 | |||
| cd069b24c6 | |||
| 3da3453503 | |||
| 1ab7c21910 | |||
| d074224447 | |||
| 3f0f219896 | |||
| 06bdad4ab0 | |||
| f2dac6e1e6 | |||
| 116140f5df | |||
| df46b40c0f | |||
| 196ab188bb | |||
| dc9d114ca4 | |||
| d1f8cf9a8e | |||
| 7836622cd6 | |||
| c5500c57f1 | |||
| 7956b6c8f4 | |||
| 80dd014be5 | |||
| 06f8a3c01a | |||
| e76a0b1288 | |||
| e9efc4cc35 | |||
| 8d6e0f049c | |||
| b83e632328 | |||
| 315a3759c3 | |||
| df0efaa7b1 | |||
| c731077fba | |||
| c9f03c0f69 | |||
| 941285d168 | |||
| 141c56fbc2 | |||
| beb6dd874a | |||
| b0392b7cf1 | |||
| d57b0d0922 | |||
| add966bc62 | |||
| 62a94c5c15 | |||
| 3b857833a7 | |||
| 27e1e0c73c | |||
| 41000f0c5d | |||
| c249da061d | |||
| aa2126e7e6 | |||
| 2893f02735 | |||
| 5a072d7cda | |||
| 5218468b96 | |||
| e4d7ebbe25 | |||
| d32523c451 | |||
| 7eb88d6a92 | |||
| 2d14184c82 | |||
| 080e4ad696 | |||
| c639d2736e | |||
| 339d9e829e | |||
| 21af7b2c1c | |||
| 7ef0b5a9ae | |||
| ba96b38a82 | |||
| 92f9a2b9a2 | |||
| 9f6cd75893 | |||
| d8bf989f83 | |||
| 2cf2a893e6 | |||
| 08ac6ea08c | |||
| 6246cc84d5 | |||
| b056892676 | |||
| c1607752b2 | |||
| cf7323cf12 | |||
| 9765cb99ea | |||
| 33c67f90db | |||
| f510d23c92 | |||
| 5b2e40404d | |||
| ed375271d2 | |||
| 4a16502269 | |||
| 0862df3182 | |||
| 4c1e2a206d | |||
| 98fbaa099f | |||
| 8ae54e30d0 | |||
| 0eed51452a | |||
| f6ff18dd8d | |||
| 4602b9fc07 | |||
| bf6aa15505 | |||
| ee64e55eca | |||
| f685054a9a | |||
| bfc1974043 | |||
| c8b9dd2ae9 | |||
| 289f736160 | |||
| 28def91c7d | |||
| b9371e9b94 | |||
| 29c65c0e7c | |||
| b10e1ba8cc | |||
| 31ac0c2542 | |||
| 2d32d8dbb2 | |||
| e6f1bbfabe | |||
| 98f7c4810e | |||
| c9288e8dbe | |||
| 1cc8ac17f3 | |||
| 687f16e1ef | |||
| 1616d4d191 | |||
| 0da42e9ec8 | |||
| 49bfb5b3a8 | |||
| 17a9f07f04 | |||
| b03f7abcb8 | |||
| 990199ec89 | |||
| 96fcdb8af3 | |||
| 6e88b3b0c5 | |||
| c182635fd6 | |||
| 7b37a86acb | |||
| 0f5e196e3f | |||
| 8f1396bb8b | |||
| 89d6bbd317 | |||
| 111aaa768a | |||
| f511b8e44f | |||
| af29e865e8 | |||
| 97b23b7148 | |||
| a9f10842e7 | |||
| 8520bfcd27 | |||
| d8a2e91922 | |||
| 08336eb2ee | |||
| 9945425f99 | |||
| 3a1a3f6579 | |||
| 23b3f213b6 | |||
| c2a8b95231 | |||
| e6af36a34b | |||
| e5ed04ce31 | |||
| 433ad507e7 | |||
| d50d8abab8 | |||
| 8233ba99df | |||
| 020c8da426 | |||
| f1a74e6275 | |||
| 260c37f63b | |||
| 03e37a2b88 | |||
| e1e7fd51cd | |||
| 5c24373a32 | |||
| 8ed2f6848b | |||
| 8f1ec01b58 | |||
| d5a8efff0e | |||
| 43420b7c38 | |||
| fa614fcf88 | |||
| 1b4785ec52 | |||
| 35c42d94c6 | |||
| 8609033d37 | |||
| 881facdfa8 | |||
| e994cd80f1 | |||
| accdb5834d | |||
| bcfdd22161 | |||
| 82ee20b1a9 | |||
| d83e7b6905 | |||
| f9b7526007 | |||
| 9ce92eb69e | |||
| a4079c09ef | |||
| a09a9d5889 | |||
| 1a3972fa21 | |||
| e477a69b98 | |||
| 1ec2c33384 | |||
| a0a4292cb8 | |||
| 2fe3ed5407 | |||
| 0c9eae860f | |||
| f6c4f3b2ee | |||
| ec70d3e955 | |||
| 534af4f017 | |||
| 804f7646d1 | |||
| 4d44650609 | |||
| 91327c50f0 | |||
| 024dedabaf | |||
| fed4a4ae48 | |||
| f217c07d12 | |||
| 8d2e70c31e | |||
| 36bd8d8a1f | |||
| 6bf4371a2b | |||
| 72c6dbabab | |||
| ef7b680e37 | |||
| 910defe18b | |||
| 6f232aa46e | |||
| 8010ead35a | |||
| 39c634ab14 | |||
| 9cf9b53671 | |||
| fbe2da1cd8 | |||
| b1dd7ea609 | |||
| d4cfd90ccd | |||
| 1826adf50e | |||
| b58d457742 | |||
| f37cd39a65 | |||
| 98a53b8639 | |||
| 795bee7de3 | |||
| 477b8b1f73 | |||
| d795dd7d47 | |||
| 4a7fea3b4c | |||
| 83ae4562fa | |||
| 06abfd92f2 | |||
| 3ae8f0253e | |||
| c77d409317 | |||
| 39603f367f | |||
| 044eb2b65f | |||
| 0b6cdec4ce | |||
| 8f0be8a475 | |||
| 6114bcd9bc | |||
| 0f89246b17 | |||
| 63145c736f | |||
| 52e98f6d41 | |||
| 0c6fb48989 | |||
| 7c696c24f3 | |||
| 91612a8465 | |||
| 063263d924 | |||
| 514518e278 | |||
| 3ce1289c44 | |||
| ed90f9a087 | |||
| 9a9faa6a33 | |||
| fa6900a937 | |||
| b020f4c502 | |||
| 122bef53ff | |||
| bed1d7ee85 | |||
| 7f68067f67 | |||
| 06d43efd7f | |||
| 745d95bbcb | |||
| 7f5b45fbdc | |||
| 179bc05e09 | |||
| 5e53c637c3 | |||
| d7b7c572b0 | |||
| 18e59ef377 | |||
| fa64dfb89b | |||
| 5788693e6c | |||
| 083f5e2271 | |||
| 6b378a9712 | |||
| 2a19a8f93d | |||
| c6418069fb | |||
| b66ab079ed | |||
| 07bca6cac8 | |||
| 01b6ede03e | |||
| 472f441c0e | |||
| 31571a60e3 | |||
| 8803d3ee03 | |||
| d81764cc78 | |||
| c56c1c7274 | |||
| 580235b105 | |||
| 210dbfe164 | |||
| e6598ff57d | |||
| 1dc21b3c0b | |||
| 1bae404c5e | |||
| 39b3936eca | |||
| 205bb47101 | |||
| a4cdc1e03b | |||
| 0467d39c90 | |||
| f8f152affb | |||
| b2f595dfb2 | |||
| 3f3221a3df | |||
| 0eea41ba85 | |||
| 90a406e5e9 | |||
| 671859ad3c | |||
| 306d69bd39 | |||
| f3552281c5 | |||
| 669bbfce93 | |||
| 9b1552269b | |||
| 559e549590 | |||
| fb8b5314e1 | |||
| d767a4d4a2 | |||
| 759cbd1065 | |||
| 386448d60c | |||
| 3d381b6dfa | |||
| 39c9a260cb | |||
| b0e9a13984 | |||
| 9ff26caa5f | |||
| d5e2f554f1 | |||
| 14ee45918f | |||
| 9fd7d20051 | |||
| 328543852c | |||
| aa23809390 | |||
| 6da09e708a | |||
| b44fec1859 | |||
| 71ab5920c8 | |||
| 4bd04eb71d | |||
| c08a2965ab | |||
| 87c50e529d | |||
|
|
2438717c79 | ||
| f952f88d9a | |||
| 019594cee7 | |||
| d7980147d6 | |||
| 2e45194b34 | |||
| 8d21448edb | |||
| 1d986a2b16 | |||
| c291cc57e7 | |||
| 0c9fe565e4 | |||
| c04e52d3df | |||
| 966506bb8d | |||
| 11f42d5d2c | |||
| ebf474aefb | |||
| 1ab3718fe5 | |||
| aad35bb73b | |||
| 1c72e5b664 | |||
| 028307d1fc | |||
| d0fc6a4c08 | |||
| 7507de7ed2 | |||
| eb3f78ef4a | |||
| a015c32e42 | |||
| e5e106e3bc | |||
| bc85a6003b | |||
| e7bf5ef8b9 | |||
| 2337ea901b | |||
| 3a17126ac0 | |||
| 164bfa780c | |||
| cb0375d9f7 | |||
| 1d15747753 | |||
| af7c38a8c2 | |||
| 57bbf07432 | |||
| fe1bc268d5 | |||
| ea2cf0b691 | |||
| a593e2b8e0 | |||
| 5394dd329a | |||
| b121d08503 | |||
| 97800e3424 | |||
| bb1068a03c | |||
| f2443d9473 | |||
| f7c9e6636a | |||
| feb3e0220e | |||
| c9951f870d | |||
| c876c59e4f | |||
| 0e75844fd3 | |||
| 515a8c4272 | |||
| d026385370 | |||
| 95ff8c66f6 | |||
| 6d90935784 | |||
| 94d45b1b07 | |||
| dc1458fb2a | |||
| ae74121229 | |||
| f5b0ce7788 | |||
| 5757627a12 | |||
| 28f8f8945d | |||
| e5386ab01c | |||
| 8ad9153012 | |||
| 2790a3890b | |||
| a978c494c4 | |||
| e1dd781b73 | |||
| 13a7c20764 | |||
| 7812c4983f | |||
| a35ae6df9e | |||
| 32eefbba10 | |||
| 81de116417 | |||
| 37ace1226b | |||
| 7c780b32b8 | |||
| eba923aa5f | |||
| d97e265035 | |||
| 86014afedb | |||
| b3626022bf | |||
| e5bf49bca4 | |||
| d988ce55b1 | |||
| 2804d0ed24 | |||
| 730797dcde | |||
| 476a4e9d88 | |||
| 665710f912 | |||
| 890a1ee864 | |||
| 5d7872200d | |||
| ccdfde3186 | |||
| f74c1ab784 | |||
| 82efc1a3e5 | |||
| b6f87e16ac | |||
| e1014a72bd | |||
| 218991d8d0 | |||
| 7937747a05 | |||
| 649ea30cb4 | |||
| b92cfdeade | |||
| 76199a20a4 | |||
| c42b92bb95 | |||
| ce2affb119 | |||
| d3e228bcd6 | |||
| baac8e84ec | |||
| 3d0a55a383 | |||
| d8c822de9d | |||
| ee8a928357 | |||
| 785fb9698b | |||
| 14c3ccbfa5 | |||
| 41edac3e3a | |||
| 36173cdd0a | |||
| ee59bd31e6 | |||
| 63004db963 | |||
| 93b3c5ba3c | |||
| 613063ce24 | |||
| 2e7a147fd3 | |||
| 894c0bff1f | |||
| 3ad999db00 | |||
| 1e5e1afeeb | |||
| 463d5c1b23 | |||
| a24b98cbea | |||
| 94034125f7 | |||
| 17d446161b | |||
| fb8c1d124e | |||
| 5033692886 | |||
| e5ba11ae79 | |||
| 0be032041a | |||
| 3bf3e83442 | |||
| bd7aefb6cc | |||
| 7009dfc3c4 | |||
| 21c30c6e99 | |||
| 63a0d2205d | |||
| 2e2e48e440 | |||
| 2f125383bf | |||
| 71877f7403 | |||
| 3d9d528694 | |||
| 39e8b98287 | |||
| fe960a32ee | |||
| 645450f886 | |||
| 77b4917d3a | |||
| 893797b5e4 | |||
| 019543b790 | |||
| c85fb94bc2 | |||
| 60f3dbf6fc | |||
| b1846342d7 | |||
| 2f583d2e97 | |||
| 551bb1c3f7 | |||
| 36cf6c87cc | |||
| 66bea6a943 | |||
| 212d3e4ee8 | |||
| 422060b929 | |||
| 2b4f755244 | |||
| 67c477af14 | |||
| a8502e30b3 | |||
| 82db6d14e0 | |||
| c529d18ad8 | |||
| cf54ee1736 | |||
| 8e8c4c47ec | |||
| a6c981355c | |||
| 69755363ca | |||
| eaa5a4d4ed | |||
| 4dcae9f877 | |||
| 424830729f | |||
| a678d28a12 | |||
| b9b5ecbac9 | |||
| 199716e1fd | |||
|
|
c95efcdd04 | ||
| 02f14b29ee | |||
| 97789d0796 | |||
| ea5af1ac5b | |||
| 6e279a67e2 | |||
| 9758becc46 | |||
|
|
da2d0fd2ce | ||
| 6e0d580f6b | |||
|
|
34481c12e1 | ||
| bc27feab00 | |||
| 1e48b32170 | |||
| b9cbf63945 | |||
| 8437e236db | |||
| 838043a9dd | |||
| 8debe50f1b | |||
| 8a97577b61 | |||
| cb9b6b2816 | |||
| 92d5126230 | |||
| 50c6f9fce5 | |||
| cbb1403f80 | |||
| 21f995f8ac | |||
| 185e0dae22 | |||
| e555829f1b | |||
| 2f50a2ffa0 | |||
| 069ececfe1 | |||
| 1ea1ad5ae0 | |||
| 3787913a12 | |||
| 45f51954f9 | |||
| 0c7773c6d4 | |||
| 81e1940b8f | |||
| 2e24a715ba | |||
| a05be24ed1 | |||
| f09017fe6e | |||
| 1aedb66132 | |||
| 2e3aef3776 | |||
| 0d3cf64e9a | |||
| 66220c1963 | |||
| 1ed52a1033 | |||
| 1a4ac13f8b | |||
| e2e94015a0 | |||
| dee7abc850 | |||
| a1464006db | |||
| b4c6bcec6e | |||
| 1d55ef3d0a | |||
| 24dab4775c | |||
| 7a7b77887c | |||
| c39d21b300 | |||
| 3f7bce197f | |||
| e706ff17c1 | |||
| 5a80422b77 | |||
| d3fb392f28 | |||
| 6817043007 | |||
| 3106c101d8 | |||
| e25c7f424d | |||
| 156459b6a4 | |||
| c73fe892b7 | |||
| 491ad84103 | |||
| cdb27c5d83 | |||
| 7d35f9d129 | |||
| b34d1e7caa | |||
| 055a52da03 | |||
| 2c3746c596 | |||
| 96a0ca9b2f | |||
| 1cd436bc5d | |||
| 3834a9b0b8 | |||
| 30454e3b4a | |||
| 020354f5bc | |||
| 6e5dbeda72 | |||
| 804922a707 | |||
| 23081f4487 | |||
| 2f75f57365 | |||
| f4f7ffe5dd | |||
| 0de23a3914 | |||
| d1f47ca406 | |||
| 8d16fe1d46 | |||
| d1bffd74ae | |||
| 23de77d951 | |||
| 172fe65df4 | |||
| 4cd96252f1 | |||
| a88f4c35bd | |||
| 19ffeb7e8e | |||
| 004e1ce03c | |||
| 485efb0c88 | |||
| 227107719b | |||
| d683f18bd0 | |||
| 78446b5a68 | |||
| 3fe638d16c | |||
| f71973b10a | |||
| 0ab0a918c3 | |||
| c694688220 | |||
| f979fd32e3 | |||
| 7e11e8138e | |||
| 590575f5b1 | |||
| b76c525106 | |||
| 56a276f019 | |||
| 62050fcff1 | |||
| d4989bb762 | |||
| dff54109a4 | |||
| 5850d69c10 | |||
| 908507f64e | |||
| ecd455ab21 | |||
| c4ebfff55e | |||
| 6adf5f472d | |||
| ee6b5c304a | |||
| 03327a549f | |||
| f6e245e77d | |||
| 4dcf9d9a40 | |||
| a6ef4e12ad | |||
| 2e751bb3fb | |||
| 5ab15341e4 | |||
| d6b027e969 | |||
| 76a4b3b3e6 | |||
| 3062aee5cb | |||
| dbf0d95329 | |||
| c42b0d5fda | |||
| 59f2688399 | |||
| 395bf3aa5a | |||
| bd85e418bc | |||
| 091ac05920 | |||
| 127d175e5c | |||
| 744a7f83df | |||
| 5f61a7af17 | |||
| c4ceaddcca | |||
| 12729092d8 | |||
| 94830659e9 | |||
| aef307c312 | |||
| 2b15c77cdc | |||
| 78acddb043 | |||
| 9106f3d2f5 | |||
| 2df8355a33 | |||
| c5610aae05 | |||
| f1f3a42557 | |||
| 63072120fc | |||
| 0be966646b | |||
| c3201118bd | |||
| 12896574c7 | |||
| 5bf877647f | |||
| 697e692c92 | |||
| 7b620a294b | |||
| 8c1e4d7c4d | |||
| 468c64df40 | |||
| c560bc666f | |||
| 8c4ab4a60d | |||
| 1a0b7895d9 | |||
| 26a29fea96 | |||
| 5c9a12a7b2 | |||
| 7d103e8f4a | |||
| 4f4dfd0ee1 | |||
| c02e95bc49 | |||
| e77bfb055a | |||
| 712814515c | |||
| 512213df15 | |||
| e07c5ab7f9 | |||
| 83471a37b0 | |||
| f82944a043 | |||
| 6eb5dec1f6 | |||
| f7ec845a7e | |||
| 0e7ed65e04 | |||
| 60910c9d87 | |||
| f4b4b3c3a6 | |||
| ce363b0319 | |||
| 42b0a4067b | |||
| 1e6f755045 | |||
| c7570e84a0 | |||
| 34b7a5f7ba | |||
| ff2146a49d | |||
| 5038d8b07f | |||
| aacc87c190 | |||
| e68a21ccb7 | |||
| 435f0a0c84 | |||
| d4269dc886 | |||
| 079ae529ee | |||
| f8d3e23025 | |||
| efa168a4b1 | |||
| ebfc32be74 | |||
| 79a2f6d7a4 | |||
| edd218b139 | |||
| d3645425f6 | |||
| 2f34c689d0 | |||
| 548e5fbde1 | |||
| f0b3d7f151 | |||
| c85a6fd462 | |||
| ddbc7988b2 | |||
| 83b1e3b8af | |||
| 8fd4f83793 | |||
| 32321c822f | |||
| bb8e8b7367 | |||
| 224417e2bd | |||
| afb2a39f8c | |||
| f240f167ff | |||
| 69273f9cf9 | |||
| e30079bd2c | |||
| d882de1582 | |||
| 2e14dc22ff | |||
| 6839e387c2 | |||
| bde2bc0b2b | |||
| e6fe0885a5 | |||
| f95440da99 | |||
| cbfff99f89 | |||
| ee119ea3cc | |||
| ebefee67bc | |||
| 60fc2e5803 | |||
| b766a6eb6a | |||
| f67342fd7a | |||
| 2f98228310 | |||
| b07739a66a | |||
| 3b293e3163 | |||
| cec5fc594f | |||
| ed10a050bc | |||
| cfc3dc73c4 | |||
| 7c23c16280 | |||
| 0f27552817 | |||
| 7464789c8c | |||
| a1f9c023c7 | |||
| 9e8cdc2c49 | |||
| dc9b44f5f9 | |||
| b7ea4bd6f1 | |||
| 65dddb798c | |||
| 2451f0b593 | |||
| 418734eb31 | |||
| fce7fe4fe3 | |||
| e31407527f | |||
| 9250e81059 | |||
| f54f9c6736 | |||
| 54135a88f9 | |||
|
|
5b219708db | ||
| b62a5e457b | |||
| 3af325f0c4 | |||
| 514ced2cc6 | |||
| 55cdf6c0cf | |||
| 00cd6e4309 | |||
| 03012d8296 | |||
| b4952dbb20 | |||
| 9123cedf4c | |||
| 1c9fbfc2eb | |||
| d6e182b94f | |||
| e48e56f36a | |||
| adde6acf4d | |||
| e929aa0875 | |||
| f784376486 | |||
| 7c0d0f4a07 | |||
| e25804998d | |||
| 6e69f843a0 | |||
| 978d2878a4 | |||
| 675ea86b83 | |||
| c2be36b8b3 | |||
| 938e204691 | |||
| 70eb911e58 | |||
| 0440d0f286 | |||
| 9651a88d67 | |||
| 6b8d2622b4 | |||
| e743499825 | |||
| 127535c7d4 | |||
| 0a322c9fed | |||
| db36c34332 | |||
| 6c00e8c350 | |||
| 4e21b136d7 | |||
| 407b317f01 | |||
| eb55ff7c30 | |||
| 3729146b80 | |||
| 48636eae43 | |||
| ae62bee78d | |||
| d0f33a0813 | |||
| 6627b67692 | |||
| 32f58e6323 | |||
| 4c82688f7d | |||
| 479ed7ac1e | |||
| 1320a8aad1 | |||
| ed1b1322fa | |||
| b9b61d1462 | |||
| fc982674f8 | |||
|
|
3e51722085 | ||
| d5f5287ca3 | |||
| f0f491fa86 | |||
| bbaa00a479 | |||
| 6df248ec22 | |||
| eae61c827d | |||
| eed5c91435 | |||
| 9e09952471 | |||
| 46a3591019 | |||
| 5419a01b5c | |||
| 3770ebc0f3 | |||
| 209ae03e63 | |||
| fc7609874d | |||
| 3c99f17158 | |||
| 6fb3a52e31 | |||
| 4f6a626e3e | |||
| 7021015a66 | |||
| 185c5cb45b | |||
| 011d3dccaa | |||
| a629eeac55 | |||
| d3ce4a1aac | |||
| 8ff13fac5b | |||
| d5c232201b | |||
| fb4e16790e | |||
| be4cdd194d | |||
| 398165d832 | |||
| 7d70fdcd1d | |||
| 5c33504582 | |||
| e4223a7a58 | |||
| f2d5e166da | |||
| 55d0f2fad8 | |||
| 58068a06dd | |||
| 01a76ff5de | |||
| 53dd0d81e7 | |||
| 8d4b5e8403 | |||
| c6e491a9bc | |||
| 55f6a3189c | |||
| dfaa041843 | |||
| 0bcf0465a6 | |||
| 517c65b790 | |||
| 4bda6ab1b1 | |||
| 5a71ec8d6c | |||
| f622ea6779 | |||
| 875db69bcf | |||
| 97a07b4955 | |||
| 0912360794 | |||
| bfc2a82384 | |||
| 6f02ccf6e7 | |||
| 36e7085ce0 | |||
| 044f130671 | |||
| bf95d50cfd | |||
| 63349ce33b | |||
| 3618940713 | |||
| f5f9064c52 | |||
| fd8bda7f48 | |||
| 5e9b5466a6 | |||
| 82a0fde37c | |||
| 72367c40fe | |||
| b8502c7459 | |||
| 6b33cfe6bb | |||
| 81e61a469e | |||
| 267213efc1 | |||
| 03ca9512a4 | |||
| e77b24a213 | |||
| 6118c49335 | |||
| 843321dfe5 | |||
| bd13bc8d68 | |||
| 9f8af72c70 | |||
| 50e3173706 | |||
| a43bfbebab | |||
| ffaac9553e | |||
| f480cbeda4 | |||
| e8a72a0b7a | |||
| b4b14d2c28 | |||
| 672ebb013f | |||
| 8e5f1ae809 | |||
| 1cf77806ba | |||
| d1a6c61bd8 | |||
| e15cb57c27 | |||
| c187c36c51 | |||
| 5bec8e0c38 | |||
| ed4a58025b | |||
| 67178757c0 | |||
| d4679e2b5c | |||
| 7b8ecd520c | |||
| f2c188b245 | |||
| 8ca6c32a76 | |||
| e6ac37644b | |||
| 048df7e1e7 | |||
| 73f0ed73d4 | |||
| 4ce9a8cc0f | |||
| 3c6538565e | |||
| 995fa234d9 | |||
| efa9890019 | |||
| d7f710d3e0 | |||
| 552cab5632 | |||
| 4d32683e66 | |||
| 2cd41b91f5 | |||
| d26f840b68 | |||
| 93ddd3c2c4 | |||
| 95dcd4c04d | |||
| a886359e94 | |||
| 1d48010a0d | |||
| b220628ca1 | |||
| c7a8ca2ec9 | |||
| 9720cf7a69 | |||
| 4eb33806bf | |||
| 3d12bbcdd1 | |||
| fa8b2aefb7 | |||
| e08bea00eb | |||
| 8427a0d9be | |||
| 79a44f4b15 | |||
| 2dba6e6816 | |||
| 258576963d | |||
| 68e9ffffe3 | |||
| b26717eab3 | |||
| 83810a8dd8 | |||
| 3ba6d5c3c4 | |||
| c6b5a92e7d | |||
| 8a5e2a45dc | |||
| 0fd5ef28e2 | |||
| fcb5a44abe | |||
| fb83cb40df | |||
| 4211934490 | |||
| 4ca64694ff | |||
| 75d13f3cc7 | |||
| db14ede309 | |||
| 9e2d17f3f3 | |||
| 7af56e1c56 | |||
| 0c06ae6028 | |||
| c39076dfb7 | |||
| fd81666453 | |||
| 2e7afad20b | |||
| c962c6f86a | |||
| 277939ba29 | |||
| c7f3445e79 | |||
| c1a1e80185 | |||
| 16892d474e | |||
| 167977a3d1 | |||
| bc876232f9 | |||
| d529ff596b | |||
| c7cad36fde | |||
| b299d5f74b | |||
| 20df24ca80 | |||
| 2482a24898 | |||
| 9621f112d1 | |||
| 82e2ee2aea | |||
|
|
b602ab678f | ||
| 03a5f750ec | |||
| e3888244ac | |||
| d0a513d64d | |||
| 26817c7e61 | |||
| cad77c69d9 | |||
| ad0b667ed2 | |||
| bc6c1886b9 | |||
| 084843586e | |||
| 981e320664 | |||
| e57e043edf | |||
|
|
5045af850f | ||
| 59f80800c3 | |||
| ae4db462b0 | |||
| ab939aa359 | |||
| e90c4a0e19 | |||
| bed6b7c347 | |||
| f626291e2e | |||
| 996e452966 | |||
| d32ccbce0c | |||
| 72b44bcceb | |||
| ed02c3bad6 | |||
| 86deba3111 | |||
| dbc7fc08a8 | |||
| 948dadcf57 | |||
| f0a7ceb255 | |||
| c14bd47ad2 | |||
| 0824c0f509 | |||
| 0177e3d7bb | |||
| 159701e840 | |||
| 1c47a4cc2f | |||
| f9caafcb86 | |||
| da6d8aa797 | |||
| 88a846aef1 | |||
| 6ee0780fec | |||
| 116549329c | |||
| 11d90b704a | |||
| 8bdc60e75a | |||
| 7fd3e4e0a7 | |||
| 18ed21b36a | |||
| b77801112f | |||
| efb237c783 | |||
| 15375c575a | |||
| e5dc00dbd3 | |||
| 8d8231f787 | |||
| 025ed5aa5a | |||
| b62d228ec8 | |||
| 01894ad91e | |||
| ee3258196b | |||
| b1ce9fb05e | |||
| 2c996b0a41 | |||
| e4ded8950e | |||
| 11a69cfb8b | |||
| 85fa8fcbd1 | |||
| 68b2bcee33 | |||
| 1796eecc9b | |||
| d6b0601f74 | |||
| 2c250ee67c | |||
| 5b451e211f | |||
| 1389f210de | |||
| b0f0706dfd | |||
| 8f66e8c39e | |||
| 3c788afce6 | |||
| e03ada5191 | |||
| 7998177e03 | |||
| aef54656d2 | |||
| 9b52fee2a7 | |||
| f89be00419 | |||
| bad4415650 | |||
| e5f3e85f8b | |||
| 0c45a5035b | |||
| c3347fcb28 | |||
| a97e8ceee5 | |||
| 87da782cc3 | |||
| 7416be6680 | |||
| df8eb7f9ef | |||
| 8198ea6941 | |||
| 6f7513a013 | |||
| 5f163b0661 | |||
| 11a9bd2ecc | |||
| 1e6f143eb7 | |||
| b99bd5fded | |||
| 442896eadc | |||
| 38c7b2d44e | |||
| 662e983f01 | |||
| de402e9996 | |||
| 69fc4323f3 | |||
| 617a986325 | |||
| c09c7fa1a8 | |||
| 2b43680099 | |||
| c1bb8ad7a6 | |||
| f59c68edb8 | |||
| 85ae2ff349 | |||
| b52635c747 | |||
| d09c8ac98f | |||
| 1921deb7e3 | |||
| a21c7eac02 | |||
| 50ab9c21cd | |||
| edd1b34217 | |||
| c1f165bfa3 | |||
| 799be0fd3e | |||
| 1f8c975c4d | |||
| b10967bfad | |||
| 835cf6ad8f | |||
| 6b55a4e0ca | |||
| aa3a5a90c4 | |||
| 5f34d52542 | |||
| ad49cadfb8 | |||
| b4c00a2d27 | |||
| 42dfe63c2f | |||
| 4fa2588b66 | |||
| a3227a681a | |||
| 1abbaa29ff | |||
| b783a90d81 | |||
| 263243ecd4 | |||
| 5942c2e822 | |||
| 0a87dcc610 | |||
| a80e1db128 | |||
| fad851edea | |||
| 4d23d7d354 | |||
| a19fe7b0b5 | |||
| 15096e7954 | |||
| 2da9b59ae9 | |||
| c4cda94309 | |||
| d32dd19dd0 | |||
| 269d0dc57e | |||
| bd221e2c75 | |||
| eb26c7d4e1 | |||
| 4de0ecb5cc | |||
| 5753e3fb51 | |||
| 14cf933487 | |||
| df240ac681 | |||
| cf2d53fe0d | |||
| 520f46441e | |||
| be1117ba31 | |||
| e35ff2da63 | |||
| 99f91768ad | |||
| d8e6e63b90 | |||
| 9f4a3ed53d | |||
| b06dd84f6b | |||
| bf77d84227 | |||
| 1ff24a2835 | |||
| a10f31f4fb | |||
| c47dac6731 | |||
| 0de883b5b1 | |||
| a160b5bfac | |||
| d7580842a8 | |||
| 40dfc60079 | |||
| bfa593ea46 | |||
| 68de2d2c5f | |||
| e883ccfb62 | |||
| 2f530d8c9e | |||
| 43e7363535 | |||
| fbe8a06d0b | |||
| 421757e88a | |||
| 07340dc68c | |||
| f7559f6a55 | |||
| dd45fb47d6 | |||
| d2ade7aa90 | |||
| 7bed4cda8e | |||
| 24c37d5b92 | |||
| c03add82a5 | |||
| 6f443ef414 | |||
| 939b44699f | |||
| db5f979149 | |||
| ded03b5c46 | |||
| f755a39589 | |||
| 788c097a50 | |||
| 3fe4c80784 | |||
| 74ae353761 | |||
| 05d62c7105 | |||
| 79ffa0b5b1 | |||
| ec68096e52 | |||
| 2e79def40d | |||
| c0fbf71a8e | |||
| cb62e367ec | |||
| 1608fae975 | |||
| 8d1ce42004 | |||
| fd7314b603 | |||
| e2433964e9 | |||
| 9ef7b36998 | |||
| 9ae7fd716c | |||
| 7fbc7bef5e | |||
| 43bab2a80e | |||
| b4eaec1ca5 | |||
| c391e54ee5 | |||
| 0c226b1fc7 | |||
| b218874a2b | |||
| fce367075d | |||
| f06b5004e2 | |||
| 03c5191e89 | |||
| 492ac0bb60 | |||
| 9ab9330671 | |||
| 9851b5406e | |||
| 19e10199e0 | |||
| f11b1b3219 | |||
| 49c37fa307 | |||
| d7fedc3118 | |||
| 0cd9f7f0d6 | |||
| 82f6b8cb56 | |||
| 1f723ab8e0 | |||
| 9496968fbb | |||
| c1e66ac574 | |||
| 0b6506885a | |||
| d018163ef2 | |||
| d89ab5c45f | |||
| c3c536632d | |||
| b73070231e | |||
| a9c0c0d1d2 | |||
| f65b16723b | |||
| 69f828b169 | |||
| dbf0b963bd | |||
| 5c20185426 | |||
| f976b8c315 | |||
| d5fac8bb07 | |||
| c33431a2fc | |||
| 23ba7bf636 | |||
| 232f992203 | |||
| 4438d5a8b4 | |||
| ca213242fd | |||
| 360221aa7a | |||
| 987f56331b | |||
| 8ee23d694f | |||
| 0f93997aed | |||
| 012bc92042 | |||
| ef6fdfc71c | |||
| 150d8e30b0 | |||
| 491927a32c | |||
| f5020404df | |||
| d4ddf22e5b | |||
| 45b30dc38e | |||
| fed57f1f68 | |||
| 6d02d6a85f | |||
| 39bc0ff55d | |||
| 9e0f045696 | |||
| 2d1897fe62 | |||
| b35107016e | |||
| 67f5342be4 | |||
| 835796a9b1 | |||
| b32fe10047 | |||
| 43bdff7af1 | |||
| 6628e135fc | |||
| e94dc60f28 | |||
| 8a4453ec84 | |||
| 1916e4c34a | |||
| f2a54682a1 | |||
| 163903cc8a | |||
| 6653f0b8ee | |||
| 7fa0200889 | |||
| 7bf0611d67 | |||
| 1cd3c3df17 | |||
| e9ff82f0a3 | |||
| e381309ba7 | |||
|
|
bd6d063a49 | ||
| de1e1401b9 | |||
| 52bb6099d7 | |||
| f42ec4973a | |||
| ba8374cc5a | |||
| d1f0ee9c20 | |||
| 2a934daf89 | |||
| ac0fab2f2b | |||
| ffab97d4c8 | |||
| f51ed6115d | |||
| f126015e70 | |||
| 699f71e636 | |||
| 88a7431067 | |||
| bf256c6e60 | |||
| 0c718229d1 | |||
| 2366a4e551 | |||
| 81bddd7eee | |||
| 35f2791c72 | |||
| 0dbbbf4359 | |||
| 04b7e07e90 | |||
| 85c84b64c4 | |||
| b0014c0477 | |||
| a53ad5d1b1 | |||
| 73a418376d | |||
| 437088663e | |||
| 9db5188d30 | |||
| 53563cb130 | |||
| c989644c6f | |||
| c2a55f2658 | |||
| 8ca63c0396 | |||
| 3b1fcac0aa | |||
| 7c093617f5 | |||
| 9e2f1dc172 | |||
| 25591dc3e5 | |||
| 4596a3a73c | |||
| 818ab604e2 | |||
| 04e18490fb | |||
| b519bb5522 | |||
| 9d8e76faec | |||
| cbdfb1a033 | |||
| 8b10a5839a | |||
| 905cecc060 | |||
| 486cf03c79 | |||
| 735b669d44 | |||
|
|
489caa6070 | ||
| f6284ed167 | |||
|
|
a843dd4a87 | ||
| b5c14926df | |||
|
|
8b58c34e5b | ||
| f29d10b0b9 | |||
|
|
ce15f78a56 | ||
| 033db86d3b | |||
| 7d32309183 | |||
| 353290cfa8 | |||
| a7aa34795b | |||
| 994bc4aa6a | |||
| 9737d65592 | |||
| e9ee66e932 | |||
| 42f66a2fb8 | |||
| 5aaac1bdec | |||
| 57a1721f62 | |||
| 8134c3c9c0 | |||
| cdaf64b41b | |||
| 5f5384f5aa | |||
| a11e43f9a5 | |||
| b76a9bf13c | |||
| 4653667ef1 | |||
| a1cc2f0147 | |||
| d08b97415b | |||
| 7fc9045688 | |||
| d9f77537f1 | |||
| bc8a874c68 | |||
| 8067c20e5a | |||
| 22526d5255 | |||
| 83f846eb69 | |||
| 61a8bf0275 | |||
| 1662c3ff3f | |||
| 41a1fa856c | |||
| eee5881ca7 | |||
| 3ce92f2477 | |||
| 4020ecff80 | |||
| 979b86b7cb | |||
| 26080ade0e | |||
| 4deb9c59ea | |||
|
|
b640515243 | ||
| 130da4e311 | |||
|
|
dcca93baef | ||
| 92525b0549 | |||
| fa0bca88b3 | |||
| 72961e3dca | |||
| 7ac4000687 | |||
|
|
3c0d0f9036 | ||
| 790fa18e1b | |||
| 7b06b5533c | |||
| 9301783c3a | |||
| 39359ab783 | |||
| a44be32cdc | |||
| d4a4f238f2 | |||
| 60584ee79f | |||
|
|
95845663f4 | ||
| d1991b5c0d | |||
| 5448ba4611 | |||
| 11b59cdc41 | |||
| 05234cba77 | |||
| f17c7f3d13 | |||
| 546792b733 | |||
| 4e38909ef2 | |||
| d411439734 | |||
| 599d3ca8fc | |||
| 5a2bb0ca5c | |||
|
|
a1492d4f3e | ||
| aee881b302 | |||
| 6287dc523d | |||
| a9a48aef39 | |||
| ef861ef28e | |||
| 9af9908ab2 | |||
| 29fd76fc91 | |||
|
|
b69f978371 | ||
| 89cea53d92 | |||
| 2a08d91021 | |||
| c726bc48a9 | |||
| 69272110fb | |||
| 682502ff2b | |||
| 345eff8a0b | |||
| dec8135478 | |||
| 60f37fa586 | |||
| 61849bb14e | |||
| f32a40b53f | |||
| 4e2921f3b9 | |||
| 8652f3b2d8 | |||
| d4f75fa0f1 | |||
| 93accf9ff3 | |||
| 22b06b36f9 | |||
| b0a763f972 | |||
| 44e4dfb0bd | |||
| 80fe60211f | |||
| 0a1e47da4d | |||
| a6acecb8c2 | |||
| 33cb0149f2 | |||
| ff873d2e2d | |||
| bc184f03cc | |||
| b3147b9a2c | |||
| 8a4e64a079 | |||
|
|
24bb36e320 | ||
| 285aa45de8 | |||
| b9eee76b6e | |||
| e3bb8e6463 | |||
| e72f33b201 | |||
| 22fe247707 | |||
| a3a44e1494 | |||
| 9c3dcaf2ec | |||
| 2a088241da | |||
| ed8af0728b | |||
| cc01c5cbc1 | |||
| 828a079184 | |||
| 189f73e522 | |||
| 692f51fe44 | |||
| ddacecec9a | |||
| af629be9e0 | |||
| dc76d41369 | |||
|
|
cc65840a1f | ||
| dad72a1fe2 | |||
| e43eff325b | |||
| 1a41f2d248 | |||
|
|
58274ba9dc | ||
| aa22501aac | |||
| b74e733252 | |||
| 2cc714abf0 | |||
| d86e6dd5c4 | |||
| 31f7269ba5 | |||
| f335f08c27 | |||
| 7a16486105 | |||
| 4a05152adc | |||
| 379478b2c2 | |||
| c2b3d4113a | |||
| 4655867db0 | |||
| 5daf371c65 | |||
| 8488f73d7f | |||
| 2788d76c00 | |||
| d55a89766e | |||
| df64e00a27 | |||
| 4569c20f58 | |||
| d0cdbf48d2 | |||
| 24b45afe6f | |||
| a577235ccc | |||
| cc98b3322a | |||
| 214d784413 | |||
| c2c02f9c62 | |||
| c4ce555f91 | |||
| 5901b1bd33 | |||
| 2c65fc65dd | |||
| 322141b927 | |||
| 9d94b1571a | |||
| ff64f3a70f | |||
| dc0bfd25c8 | |||
| 10ccbc0dc2 | |||
| 45e46b6826 | |||
| 7d20b9e9a2 | |||
| 086765090a | |||
| 1c49000ec2 | |||
| e9261a1e19 | |||
| 796afbdf1d | |||
| 3dfe8d2829 | |||
| 77368f9908 | |||
| 2acad0fa01 | |||
| e0ccb81014 | |||
| 93cedaaa41 | |||
| d306bb4f36 | |||
|
|
4c66354c6a | ||
| 35ecb54a26 | |||
| e2780ecbdf | |||
| 4e4cfb24d6 | |||
| 89307534df | |||
| d0e126118d | |||
| 7e424691fd | |||
| 12fb0067d1 | |||
| f0fd643ef0 | |||
| cd48a580af | |||
| a08ebed065 | |||
| 0784b9092d | |||
| 6954bb947e | |||
| eafa053e21 | |||
| 90900b1589 | |||
| 40397e6aaa | |||
| 2196c5c70e | |||
| c4d9f13380 | |||
| 27fbc15469 | |||
| c96bb414fe | |||
| d428d0ff03 | |||
| d80216ff31 | |||
| adcb4e74fa | |||
| 4df1555179 | |||
| 4bd78541fc | |||
| 2f2a4722f9 | |||
| 7ad235bba1 | |||
|
|
c7faaeff62 | ||
| 525bcc909f | |||
|
|
aecd605df9 | ||
| f1eac4f309 | |||
| 562ab87771 | |||
| 26d7aeb401 | |||
| 3798ac5e09 | |||
| a2ee74c700 | |||
|
|
1a3aaad591 | ||
| 4a5196a5cb | |||
| 207062cf1a | |||
| d6291dbcb0 | |||
| 7b8737006c | |||
| 75818ebb0a | |||
| 37f26e0f1b | |||
| 3250fc2620 | |||
| cf44358190 | |||
| 87a5685fec | |||
| 8b6c761812 | |||
| 21d86bf57a | |||
| 7d892fe298 | |||
| 0b16f3541d | |||
| cd1947a445 | |||
| 1929e19647 | |||
| 31e3c4442f | |||
| c1459377a1 | |||
| 2f30f37b7c | |||
| b31924f316 | |||
| efb10c477c | |||
| 7adfd7a589 | |||
| 87ec027cdb | |||
| d171e28e49 | |||
| 9169502512 | |||
| 92d42ec297 | |||
| e6457ac82e | |||
| 7eb85fc0c3 | |||
| a97de38574 | |||
| b426b66f9e | |||
| d9d66fa6b4 | |||
| ade2809a72 | |||
| 0c0f20a75d | |||
| a8b531b08e | |||
|
|
40ce57ba37 | ||
| 3488dce7ad | |||
| 0af71b83cb | |||
| 39a92a56d1 | |||
| e4943fe60a | |||
| 1d0c965ff0 | |||
| 6d5b5efafa | |||
| 665e7454bb | |||
| 63056b2d91 | |||
| c995488e68 | |||
| f4f7d7795e | |||
| ddad227ae9 | |||
| 0af1308b7d | |||
| e2afed1dc4 | |||
| 47de48806f | |||
| 7886066fd4 | |||
| db27f0b8b7 | |||
| 1d28c83807 | |||
| 9f13b4ec5a | |||
| 133f8fd9f3 | |||
| 757e20c75c | |||
| 6b75914c91 | |||
| ae6af8d777 | |||
| 770718ba95 | |||
| da4a9f7088 | |||
| bea62f6d3c | |||
| ef960f945e | |||
| 9fd8ac426c | |||
| a68a5e6c8b | |||
| f78296c1b0 | |||
| 91a3b0d659 | |||
| 6fdba85d04 | |||
| c0fd42d5c4 | |||
| 4095263c72 | |||
| 57558d7241 | |||
| 28b64b3819 | |||
| 08a0b5e186 | |||
| 7d8d34b15e | |||
| d93a1d92f3 | |||
| 6a5b63c359 | |||
| 5d10c45501 | |||
| 28449b5dfd | |||
| e1ea19b54d | |||
| 87a18b54ad | |||
| 1d36b669b0 | |||
| 0d67c6a3ca | |||
| 9e2919dc35 | |||
| 88219e7802 | |||
| dee06ecf45 | |||
| cc6e4d07b0 | |||
| f824293c96 | |||
| 92e48f44dc | |||
| 02402eb5dc | |||
| 9e9e0d8f35 | |||
| 3a955ce107 | |||
| 1d22d64558 | |||
| e59f29badf | |||
| 8cfb038a0c | |||
| 5ba48d965c | |||
| d72a7c4bf8 | |||
| fb20289a80 | |||
| 21df235fd5 | |||
| e001e9eb0d | |||
| 7ebe575132 | |||
| 34951d1058 | |||
| 2ccb45bfcb | |||
| 0a7945806e | |||
| e3fe8aff79 | |||
| 12fdc90536 | |||
| 870ee26b60 | |||
| 7b84624e81 | |||
| 43d7f918a5 | |||
| 78e61a7718 | |||
| 3e3e3db323 | |||
| 335073102e | |||
| c749154fb3 | |||
| 752152fb56 | |||
| cb2883646d | |||
| bc6370ad8e | |||
| 15ceaa1dbb | |||
| 66dfd39757 | |||
| b7b82da73f | |||
| f95946a11b | |||
| 1609f189dd | |||
| 26de27fc85 | |||
| 41a3771efe | |||
| d0ac69832f | |||
| df6c2687af | |||
| 8783a619c9 | |||
| 573cc3fc47 | |||
| 4d1ac42057 | |||
| 12dc740f56 | |||
| 7b61f433a0 | |||
| 0e25a1e87b | |||
| e9ff3b72e1 | |||
| 623ec17c29 | |||
| cc1a665f7a | |||
| f092846ece | |||
| b547fef1b9 | |||
| 665bc9f8d1 | |||
| c961921be8 | |||
| 3f65a92446 | |||
| bfe82cc95d | |||
| 85ededc849 | |||
| 7efe37c35c | |||
| d394a70f46 | |||
| 1db88e3343 | |||
| b9e1e33dc8 | |||
| 8af80459a1 | |||
| c2b46f77b7 | |||
| 5f5955d51b | |||
| e29d632a78 | |||
| b5845d3881 | |||
| f75c42690b | |||
| 18f9ac498b | |||
| 865c71ab96 | |||
| 2d40e2d84b | |||
| e7e160c8e1 | |||
| a542c95433 | |||
| 83a3e0ca24 | |||
| 7eeb7c97d2 | |||
| 8629224f2f | |||
| 76866a46f0 | |||
| 3f96854d9a | |||
| 92001aadfe | |||
| 3473645798 | |||
| 47834ea34e | |||
| 1329a87a06 | |||
| 38217e7571 | |||
| fff22ede33 | |||
| ab3c395f2f | |||
| 4a4502a33c | |||
| 07b2dcbba0 | |||
| c582d520b7 | |||
| 22fcff77c2 | |||
| 4b49ca9194 | |||
| 95a500f09d | |||
|
|
5f186e0bf2 | ||
| 64ac1b2188 | |||
|
|
e60f0b2cf7 | ||
| aeb79cfe57 | |||
| 05e74503c5 | |||
|
|
ef34b4b3f1 | ||
| dc22fc81e7 | |||
| 2ce73aefad | |||
| a8b37fb5ba | |||
| ee1d3dec38 | |||
| dbdea53fc3 | |||
|
|
cc770cd61b | ||
| 5c3f71c2e4 | |||
| 6b61fa6136 | |||
| 5509a7b3ae | |||
| 9d24d7024f | |||
| 17405babae | |||
| a61c636455 | |||
|
|
c17f6a3956 | ||
| 0042b12ad5 | |||
| bbbf670ec9 | |||
| 986685169a | |||
| 4d160967bc | |||
| 0163c9b728 | |||
|
|
b6b7fa1021 | ||
| 6d9d5024a0 | |||
|
|
fbe9937782 | ||
| 6b60315036 | |||
|
|
64f15e77bc | ||
| efc4fb9cb2 | |||
| f1f0a0f1a5 | |||
|
|
7f696862ef | ||
| 4cf00c1a6b | |||
| 2908ffc1c8 | |||
| 761cd1f064 | |||
| b2026ce74f | |||
| ee565b22db | |||
| ba34b851e4 | |||
| aadfe8a6e8 | |||
| 736677fdc6 | |||
| 6870dbf711 | |||
| 679a112a2c | |||
| 391ca28f46 | |||
| f3e897b6c5 | |||
|
|
29a3e606e1 | ||
| d2b837f52c | |||
| 8984381ea2 | |||
| ff015b19f7 | |||
| 1e71eedc19 | |||
| fbce4a1abc | |||
| d0cdab81d4 | |||
| a69d5367d7 | |||
| 8d0ee48aa6 | |||
| c24510e1e7 | |||
| 2e1c2e21b7 | |||
| fee8c8c3db | |||
| d94290019c | |||
| 27b914a905 | |||
| ded25a7c87 | |||
| 5ba5da4cd1 | |||
| cbb2a076e3 | |||
| 62df45da6a | |||
| e441d8dc87 | |||
| 6d77e923b7 | |||
| 17a40802ae | |||
| 13bb828a95 | |||
| eaa1a74dd6 | |||
| 80b615810c | |||
| 23fe4928e0 | |||
| 5a37c742e2 | |||
| b94af58524 | |||
| 082c90b769 | |||
| 5012dc3a7b | |||
| a6619b6e9d | |||
| fe4ab5a013 | |||
| ade3a5621f | |||
| 1cadeb3f59 | |||
| 05a2ba7f21 | |||
| 1759b6bcb1 | |||
| a9e4c1b793 | |||
| 650b565fdf | |||
| 7ab974e565 | |||
| 95f8faf30f | |||
| 3d69e3723e | |||
| 4420bbc36c | |||
| d5d96f6323 | |||
| 0bc9ed9d49 | |||
| 95e29d6266 | |||
| 207cef200f | |||
| 0082bcd159 | |||
| f2a1674247 | |||
| cd1a3de04a | |||
| 60a0bd7a65 | |||
| a0696fc974 | |||
| 159660815b | |||
| 1c5a3f3c13 | |||
| b36fc58839 | |||
| 0cd7a7dc9e | |||
| 52724dd5b2 | |||
| dccea0e283 | |||
| 0f488b26d1 | |||
| c3b60695c7 | |||
| 6fe205dbfb | |||
| d8efd3f987 | |||
| 4c50e44e2c | |||
| 60717c3363 | |||
| 48388edcfc | |||
| 3c90a0fbe6 | |||
| 6eadb548a1 | |||
| 204be55c81 | |||
| a44ac7606a | |||
| 67505f7aef | |||
| 369dc69364 | |||
| 1ffe197d69 | |||
| 6a584ccf7b | |||
| 43aba019c0 | |||
| a0a61d3bbc | |||
| 9b008fcff9 | |||
| e80162cfa9 | |||
| 30b02aea33 | |||
| 1cf49c24ef | |||
| 3bb0552ba6 | |||
| b33be2ab84 | |||
| 8a4a63c85b | |||
| 640e8bbfa0 | |||
| e6463e0286 | |||
| c6cf6fef0b | |||
| a428636e1c | |||
| 6fd145fa52 | |||
| 6164d462f3 | |||
| d05e72e1d7 | |||
| d9fbfcb184 | |||
| 660d95616d | |||
| 6160819920 | |||
| c83b2d5bc5 | |||
| 44d529d32f | |||
| b81e51c0e8 | |||
| beccfe8519 | |||
| 9e774fb629 | |||
| 10c7ec8718 | |||
| 4bb9d0f002 | |||
| 39676d1a37 | |||
| 50f44afd82 | |||
| 9884bbd0e3 | |||
| 39853e39c7 | |||
| 894656741d | |||
| 93ef3659f2 | |||
| 3ccf084a53 | |||
| aaa2b766a3 | |||
| dc1f03b7ae | |||
| 36b50d3bf2 | |||
| b9b7081eb4 | |||
| 808e65091d | |||
| 9b0c30e554 | |||
| ffec30883f | |||
| 900dfc6ca7 | |||
| 203b4d11a8 | |||
| 2ba57ab05f | |||
| 4b11415f36 | |||
| 1ef0c4b8f0 | |||
| 4d85ed4e88 | |||
| 6c40e57ff6 | |||
| 19a4974537 | |||
| 6783e1507d | |||
| 8f3e722a0f | |||
| 3437d2e484 | |||
|
|
06e654ae2e | ||
| c3103a23c6 | |||
| 55b38c2741 | |||
| 9f30c1968d | |||
| d2455768ff | |||
| 6331155dc7 | |||
| 06d9695d33 | |||
| 80b35284ed | |||
| 27c38153b8 | |||
| db62524dab | |||
| 190b858e46 | |||
|
|
be69e5838e | ||
| b12122f53b | |||
| 33be35eb60 | |||
| 39b3f96ab5 | |||
| 7d214f74fd | |||
| 10aa140fe9 | |||
| c68eaa9c61 | |||
| 3cc6021540 | |||
| bfe7d49e07 | |||
| c02aacb0d5 | |||
| 6d0f6fd171 | |||
| 712061e814 | |||
| 197dfa1b7e | |||
| f21b66ddc0 | |||
| 5e086537d6 | |||
| 23532cb7e6 | |||
| 864ee5caec | |||
| 5db6d0332b | |||
| eb9504658a | |||
| a67389d611 | |||
|
|
f6d4609947 | ||
| d2ddcebe3e | |||
| fd2f2c0aa9 | |||
| 0aae3b0654 | |||
| e56606ac61 | |||
| 54e11700c2 | |||
| fe3b8de490 | |||
| 9874069100 | |||
| 9419d6d55b | |||
| 2f61facbda | |||
| 5cd8d7bf18 | |||
| 41f016050f | |||
| d931790dd3 | |||
| a81e63e131 | |||
| 30717079d4 | |||
| 3eb2fbb654 | |||
| cf54a430eb | |||
| 04925c0534 | |||
| 6473755127 | |||
| 35f40fc8fc | |||
| 088a4ab969 | |||
| 3c00ddad75 | |||
| 5edd9baeb6 | |||
| 734548d764 | |||
| e47d3a235d | |||
| 0a93015720 | |||
| 1078d71370 | |||
| b482127044 | |||
| 27b6ee135c | |||
| cc2000267d | |||
| c1e3655923 | |||
| b0a6181dca | |||
| 33e7efa440 | |||
| 6d7a38dc08 | |||
| 8a60965069 | |||
| ef9d627095 | |||
| b66acc8c2a | |||
| fb56f6c190 | |||
| af0f55e6ef | |||
| fb95e2ad8d | |||
| 138a6d3e66 | |||
| 98b9065585 | |||
| 36c3fae920 | |||
| 70ee376958 | |||
| 47b2f1b009 | |||
| f2a35b97b0 | |||
| c26e5dc40d | |||
| 893b2d601c | |||
| 28964721ec | |||
| 5a2028d797 | |||
| fcbe6a87df | |||
| 7f5cf75d68 | |||
| c368638181 | |||
| decc9d2b29 | |||
| 7da2a90712 | |||
| 7dd377460c | |||
| 6b6ae63aea | |||
| cc9a4b5536 | |||
| fd44a312f6 | |||
| a5802b4280 | |||
| d8c928dc02 | |||
| 339cdf662b | |||
| 1e2a174e30 | |||
| 2714538ad7 | |||
| 8a43f67801 | |||
| f3f7ef4a3d | |||
| 3523f8d6c8 | |||
| eb8c2b6ad5 | |||
| 438322361d | |||
| c902f48931 | |||
| e67a258e7a | |||
| 007993ed9e | |||
| 81171c0c7c | |||
|
|
15d07e4234 | ||
| 786fac9142 | |||
| 5499c8795f | |||
| 322698f345 | |||
| f229e03f10 | |||
|
|
6024dbcdb6 | ||
| c874ea44eb | |||
|
|
c70e4d1412 | ||
| 235c5e329e | |||
|
|
b77bfab5a8 | ||
| 890bbc4214 | |||
| 2f86f4d6ff | |||
| c9ced293e8 | |||
| 43f081aac1 | |||
| 49aa411248 | |||
| d597e15402 | |||
| a74ee3faf4 | |||
| cfe55ca196 | |||
| 2edffd36d0 | |||
| c7077ec283 | |||
| cfcd4c2050 | |||
| 2387af2880 | |||
| 72f825c0c4 | |||
| fbe4857944 | |||
| 01fe75e553 | |||
| 2bd607edad | |||
| 9b9a806dee | |||
| 9bc6d392a0 | |||
| 26b5dc7cd4 | |||
| 80a690c431 | |||
| 71840ee1e2 | |||
| fbdbb25a78 | |||
| e546428596 | |||
| a16259f718 | |||
| df0b708462 | |||
| b3c383327d | |||
|
|
8a9c99fb0d | ||
| a22d90198c |
4
.github/workflows/python-app-mysql55.yml
vendored
4
.github/workflows/python-app-mysql55.yml
vendored
@@ -34,5 +34,5 @@ jobs:
|
||||
cd /www/server/mdserver-web/plugins/mysql && sudo bash install.sh install 5.5
|
||||
- name: Start DEBUG
|
||||
run: |
|
||||
gunicorn -c setting.py app:app
|
||||
python3 task.py &
|
||||
cd web && gunicorn -c setting.py app:app
|
||||
python3 panel_task.py &
|
||||
|
||||
4
.github/workflows/python-app-mysql56.yml
vendored
4
.github/workflows/python-app-mysql56.yml
vendored
@@ -34,5 +34,5 @@ jobs:
|
||||
cd /www/server/mdserver-web/plugins/mysql && sudo bash install.sh install 5.6
|
||||
- name: Start DEBUG
|
||||
run: |
|
||||
gunicorn -c setting.py app:app
|
||||
python3 task.py &
|
||||
cd web && gunicorn -c setting.py app:app
|
||||
python3 panel_task.py &
|
||||
|
||||
4
.github/workflows/python-app-mysql57.yml
vendored
4
.github/workflows/python-app-mysql57.yml
vendored
@@ -34,5 +34,5 @@ jobs:
|
||||
cd /www/server/mdserver-web/plugins/mysql && sudo bash install.sh install 5.7
|
||||
- name: Start DEBUG
|
||||
run: |
|
||||
gunicorn -c setting.py app:app
|
||||
python3 task.py &
|
||||
cd web && gunicorn -c setting.py app:app
|
||||
python3 panel_task.py &
|
||||
|
||||
4
.github/workflows/python-app-mysql80.yml
vendored
4
.github/workflows/python-app-mysql80.yml
vendored
@@ -34,5 +34,5 @@ jobs:
|
||||
cd /www/server/mdserver-web/plugins/mysql && sudo bash install.sh install 8.0
|
||||
- name: Start DEBUG
|
||||
run: |
|
||||
gunicorn -c setting.py app:app
|
||||
python3 task.py &
|
||||
cd web && gunicorn -c setting.py app:app
|
||||
python3 panel_task.py &
|
||||
|
||||
6
.github/workflows/python-app-op.yml
vendored
6
.github/workflows/python-app-op.yml
vendored
@@ -31,8 +31,8 @@ jobs:
|
||||
sudo bash scripts/install_dev.sh
|
||||
- name: Install OpenResty
|
||||
run: |
|
||||
cd /www/server/mdserver-web/plugins/openresty && sudo bash install.sh install 1.21.4.1
|
||||
cd /www/server/mdserver-web/plugins/openresty && sudo bash install.sh install 1.27.1
|
||||
- name: Start DEBUG
|
||||
run: |
|
||||
gunicorn -c setting.py app:app
|
||||
python3 task.py &
|
||||
source /www/server/mdserver-web/bin/activate && cd web && gunicorn -c setting.py app:app
|
||||
cd /www/server/mdserver-web && python3 panel_task.py &
|
||||
|
||||
4
.github/workflows/python-app-php.yml
vendored
4
.github/workflows/python-app-php.yml
vendored
@@ -64,5 +64,5 @@ jobs:
|
||||
cd /www/server/mdserver-web/plugins/php && sudo bash install.sh install 81
|
||||
- name: Start DEBUG
|
||||
run: |
|
||||
gunicorn -c setting.py app:app
|
||||
python3 task.py &
|
||||
source /www/server/mdserver-web/bin/activate && cd web && gunicorn -c setting.py app:app
|
||||
cd /www/server/mdserver-web && python3 panel_task.py &
|
||||
|
||||
66
.gitignore
vendored
66
.gitignore
vendored
@@ -87,6 +87,7 @@ venv/
|
||||
ENV/
|
||||
env.bak/
|
||||
venv.bak/
|
||||
lib/python*
|
||||
|
||||
# Spyder project settings
|
||||
.spyderproject
|
||||
@@ -98,6 +99,12 @@ venv.bak/
|
||||
# mkdocs documentation
|
||||
/site
|
||||
|
||||
bin
|
||||
pyvenv.cfg
|
||||
include
|
||||
share
|
||||
pip-selfcheck.json
|
||||
|
||||
# mypy
|
||||
.mypy_cache/
|
||||
|
||||
@@ -108,7 +115,28 @@ tmp/*
|
||||
*.swp
|
||||
*.pem
|
||||
*.zip
|
||||
logs/*
|
||||
debug.out
|
||||
mdioks.session-journal
|
||||
*.session
|
||||
|
||||
temp
|
||||
|
||||
logs/panel.pid
|
||||
logs/panel.log
|
||||
logs/panel_task.log
|
||||
logs/panel_exec.log
|
||||
logs/panel_task.lock
|
||||
|
||||
ssl/local
|
||||
ssl/nginx
|
||||
ssl/choose.pl
|
||||
|
||||
scripts/init.d/mw
|
||||
scripts/mdserver-web
|
||||
|
||||
data/api_login.txt
|
||||
data/sessions
|
||||
|
||||
data/*.db
|
||||
data/iplist.txt
|
||||
data/json/index.json
|
||||
@@ -122,27 +150,11 @@ data/default.pl
|
||||
data/backup.pl
|
||||
data/debug.pl
|
||||
data/default_site.pl
|
||||
|
||||
ssl/input.pl
|
||||
|
||||
lib/python*
|
||||
|
||||
bin
|
||||
pyvenv.cfg
|
||||
include
|
||||
share
|
||||
pip-selfcheck.json
|
||||
|
||||
scripts/init.d/mw
|
||||
scripts/mdserver-web
|
||||
|
||||
data/api_login.txt
|
||||
data/sessions
|
||||
data/ssl.pl
|
||||
|
||||
data/port.pl
|
||||
data/ipv6.pl
|
||||
data/restart.pl
|
||||
data/ssl.pl
|
||||
data/edate.pl
|
||||
data/osname.pl
|
||||
data/only_netio_counters.pl
|
||||
@@ -155,6 +167,7 @@ data/api.json
|
||||
data/bind_domain.pl
|
||||
data/unauthorized_status.pl
|
||||
data/auth_secret.pl
|
||||
data/panelSpeed.pl
|
||||
|
||||
plugins/vip_*
|
||||
plugins/own_*
|
||||
@@ -163,8 +176,6 @@ plugins/op_auth
|
||||
plugins/l2tp
|
||||
plugins/openlitespeed
|
||||
plugins/tamper_proof
|
||||
plugins/cryptocurrency_trade
|
||||
plugins/mtproxy
|
||||
plugins/zimg
|
||||
plugins/bk_demo
|
||||
plugins/mail
|
||||
@@ -174,8 +185,15 @@ plugins/frp
|
||||
plugins/file_search
|
||||
plugins/proxysql
|
||||
plugins/tidb
|
||||
debug.out
|
||||
plugins/goedge-admin
|
||||
plugins/goedge-node
|
||||
plugins/goedge-happy
|
||||
|
||||
|
||||
mdioks.session-journal
|
||||
*.session
|
||||
/logs
|
||||
/data
|
||||
/plugins/flexcdn
|
||||
/plugins/tools
|
||||
/plugins/choose-linux-python
|
||||
*.md5
|
||||
/bak
|
||||
*.tmp
|
||||
|
||||
79
README.md
79
README.md
@@ -22,7 +22,6 @@
|
||||
|
||||
* SSH终端工具
|
||||
* 面板收藏功能
|
||||
* 网站子目录绑定
|
||||
* 网站备份功能
|
||||
* 插件方式管理
|
||||
|
||||
@@ -30,23 +29,28 @@
|
||||
|
||||
- 吹水组 - https://t.me/mdserver_web
|
||||
- 交流论坛 - https://bbs.midoks.icu
|
||||
- discord - https://discord.gg/3zXk4mES
|
||||
|
||||
```
|
||||
如果出现问题,最好私给我面板信息。不要让我猜。如果不提供,不要提出问题,自行解决。 — 座右铭
|
||||
Talk is cheap, show me the code. -- linus
|
||||
```
|
||||
|
||||
- [兼容性测试报告](/compatibility.md)
|
||||
- [常用命令说明](/cmd.md) [ mw default ] [ mw update_dev ]
|
||||
- [常用命令说明](/cmd.md) [ mw default ] [ mw dev ]
|
||||
|
||||
### 特别赞助
|
||||
|
||||
- [虚位以待](https://bbs.midoks.icu)
|
||||
|
||||
### 主要插件介绍
|
||||
|
||||
* OpenResty - 轻量级,占有内存少,并发能力强。
|
||||
* PHP[53-83] - PHP是世界上最好的编程语言。
|
||||
* PHP[53-85] - PHP是世界上最好的编程语言。
|
||||
* MySQL - 一种关系数据库管理系统。
|
||||
* MariaDB - 是MySQL的一个重要分支。
|
||||
* MySQL[APT/YUM] - 一种关系数据库管理系统。
|
||||
* MySQL[community] - 一种关系数据库管理系统。
|
||||
* MongoDB - 一种非关系NOSQL数据库管理系统。
|
||||
* PostgreSQL - 功能强大的开源数据库。
|
||||
* phpMyAdmin - 著名Web端MySQL管理工具。
|
||||
* Memcached - 一个高性能的分布式内存对象缓存系统。
|
||||
* Redis - 一个高性能的KV数据库。
|
||||
@@ -75,10 +79,10 @@ Talk is cheap, show me the code. -- linus
|
||||
|
||||
```
|
||||
phpMyAdmin[4.4.15]支持MySQL[5.5-5.7]
|
||||
phpMyAdmin[5.2.0]支持MySQL[8.0]
|
||||
phpMyAdmin[5.2.1]支持MySQL[8.0+]
|
||||
|
||||
PHP[53-72]支持phpMyAdmin[4.4.15]
|
||||
PHP[72-83]支持phpMyAdmin[5.2.0]
|
||||
PHP[72-84]支持phpMyAdmin[5.2.1]
|
||||
```
|
||||
|
||||
# 郑重声明
|
||||
@@ -88,14 +92,8 @@ PHP[72-83]支持phpMyAdmin[5.2.0]
|
||||
- https://www.youtube.com/watch?v=2taa5K-Jmmw
|
||||
|
||||
|
||||
# 特别赞助
|
||||
|
||||
- [找资源 - 阿里云盘资源搜索引擎](https://zhaoziyuan1.cc)
|
||||
|
||||
# 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 |
|
||||
@@ -110,25 +108,22 @@ docker run -itd --name mw-server --privileged=true -p 7200:7200 -p 80:80 -p 443:
|
||||
```
|
||||
|
||||
|
||||
### 版本更新 0.16.9
|
||||
### 版本更新 0.19.0
|
||||
|
||||
- mysql同步优化,享受丝滑般感觉。
|
||||
- 网站统计 - 实时-可调节1-10s。
|
||||
- 网站统计 - 加入大小条件。
|
||||
- Sphinx优化。
|
||||
- 常规优化。
|
||||
|
||||
### JSDelivr安装地址
|
||||
### 增加在线更新支持。
|
||||
|
||||
- 初始安装
|
||||
|
||||
```
|
||||
curl --insecure -fsSL https://cdn.jsdelivr.net/gh/midoks/mdserver-web@latest/scripts/install.sh | bash
|
||||
bash <(curl --insecure -fsSL https://cdn.jsdelivr.net/gh/midoks/mdserver-web@latest/scripts/install.sh)
|
||||
```
|
||||
|
||||
- 直接更新
|
||||
|
||||
```
|
||||
curl --insecure -fsSL https://cdn.jsdelivr.net/gh/midoks/mdserver-web@latest/scripts/update.sh | bash
|
||||
bash <(curl --insecure -fsSL https://cdn.jsdelivr.net/gh/midoks/mdserver-web@latest/scripts/update.sh)
|
||||
```
|
||||
|
||||
- 卸载脚本
|
||||
@@ -142,14 +137,15 @@ wget --no-check-certificate -O uninstall.sh https://cdn.jsdelivr.net/gh/midoks/m
|
||||
- 初始安装
|
||||
|
||||
```
|
||||
curl --insecure -fsSL https://raw.githubusercontent.com/midoks/mdserver-web/master/scripts/install.sh | bash
|
||||
curl --insecure -fsSL https://code.midoks.icu/midoks/mdserver-web/raw/branch/master/scripts/install.sh | bash
|
||||
|
||||
bash <(curl --insecure -fsSL https://raw.githubusercontent.com/midoks/mdserver-web/dev/scripts/install.sh)
|
||||
bash <(curl --insecure -fsSL https://code.midoks.icu/midoks/mdserver-web/raw/branch/master/scripts/install.sh)
|
||||
```
|
||||
|
||||
- 直接更新
|
||||
|
||||
```
|
||||
curl --insecure -fsSL https://raw.githubusercontent.com/midoks/mdserver-web/master/scripts/update.sh | bash
|
||||
bash <(curl --insecure -fsSL https://raw.githubusercontent.com/midoks/mdserver-web/master/scripts/update.sh)
|
||||
```
|
||||
|
||||
- 卸载脚本
|
||||
@@ -164,24 +160,47 @@ wget --no-check-certificate -O uninstall.sh https://raw.githubusercontent.com/mi
|
||||
- 需已经安装mdserver-web
|
||||
|
||||
```
|
||||
curl --insecure -fsSL https://raw.githubusercontent.com/midoks/mdserver-web/dev/scripts/quick/app.sh | bash
|
||||
bash <(curl --insecure -fsSL https://raw.githubusercontent.com/midoks/mdserver-web/dev/scripts/quick/app.sh)
|
||||
```
|
||||
|
||||
|
||||
### DEV使用
|
||||
|
||||
```
|
||||
curl --insecure -fsSL https://raw.githubusercontent.com/midoks/mdserver-web/dev/scripts/install_dev.sh | bash
|
||||
curl --insecure -fsSL https://raw.githubusercontent.com/midoks/mdserver-web/dev/scripts/update_dev.sh | bash
|
||||
bash <(curl --insecure -fsSL https://raw.githubusercontent.com/midoks/mdserver-web/dev/scripts/install.sh)
|
||||
bash <(curl --insecure -fsSL https://raw.githubusercontent.com/midoks/mdserver-web/dev/scripts/update.sh)
|
||||
|
||||
bash <(curl --insecure -fsSL https://raw.githubusercontent.com/midoks/mdserver-web/dev/scripts/install_dev.sh)
|
||||
bash <(curl --insecure -fsSL https://raw.githubusercontent.com/midoks/mdserver-web/dev/scripts/update_dev.sh)
|
||||
|
||||
wget --no-check-certificate -O install.sh https://raw.githubusercontent.com/midoks/mdserver-web/dev/scripts/install.sh && bash install.sh
|
||||
|
||||
wget --no-check-certificate -O uninstall.sh https://raw.githubusercontent.com/midoks/mdserver-web/dev/scripts/uninstall.sh && bash uninstall.sh
|
||||
|
||||
curl --insecure -fsSL https://raw.githubusercontent.com/midoks/mdserver-web/dev/scripts/quick/debug.sh | bash
|
||||
bash <(curl --insecure -fsSL https://raw.githubusercontent.com/midoks/mdserver-web/dev/scripts/quick/debug.sh)
|
||||
|
||||
curl --insecure -fsSL https://code.midoks.icu/midoks/mdserver-web/raw/branch/dev/scripts/install_dev.sh | bash
|
||||
curl --insecure -fsSL https://code.midoks.icu/midoks/mdserver-web/raw/branch/dev/scripts/update_dev.sh | bash
|
||||
bash <(curl --insecure -fsSL https://code.midoks.icu/midoks/mdserver-web/raw/branch/dev/scripts/install.sh)
|
||||
bash <(curl --insecure -fsSL https://code.midoks.icu/midoks/mdserver-web/raw/branch/dev/scripts/update.sh)
|
||||
|
||||
bash <(curl --insecure -fsSL https://code.midoks.icu/midoks/mdserver-web/raw/branch/dev/scripts/install_dev.sh)
|
||||
bash <(curl --insecure -fsSL https://code.midoks.icu/midoks/mdserver-web/raw/branch/dev/scripts/update_dev.sh)
|
||||
```
|
||||
|
||||
### 旧版安装/更新
|
||||
```
|
||||
curl --insecure -fsSL https://raw.githubusercontent.com/midoks/mdserver-web/dev/scripts/old/install.sh | bash
|
||||
curl --insecure -fsSL https://raw.githubusercontent.com/midoks/mdserver-web/dev/scripts/old/update.sh | bash
|
||||
```
|
||||
|
||||
### 偶然问题
|
||||
- 在安装/更新时,突然出现python插件pip,psutil,可尝试如下解决方案
|
||||
```
|
||||
cd /www/server/mdserver-web rm -rf lib
|
||||
cd /www/server/mdserver-web && rm -rf lib64
|
||||
cd /www/server/mdserver-web && rm -rf bin
|
||||
cd /www/server/mdserver-web && rm -rf include
|
||||
|
||||
mw update/mw update_dev/mw dev
|
||||
```
|
||||
|
||||
### 捐赠地址 USDT(TRC20)
|
||||
|
||||
52
app.py
52
app.py
@@ -1,52 +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
|
||||
|
||||
sys.dont_write_bytecode = True
|
||||
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'
|
||||
# app.run(host=HOST, port=PORT)
|
||||
|
||||
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)
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,146 +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()
|
||||
return True
|
||||
|
||||
|
||||
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))
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,690 +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 hook
|
||||
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'].find('database_') > -1 or params['stype'] == 'logs' or params['stype'] == 'path':
|
||||
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, sname, sbody,urladdress',
|
||||
(iname, field_type, get['where1'], get['hour'], get['minute'], get['save'], get['backup_to'], sname, 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 type(addData) == str:
|
||||
return addData
|
||||
|
||||
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 == 'site' or stype == 'sites' or stype == 'database' or stype.find('database_') > -1 or stype == 'path':
|
||||
hookPath = mw.getPanelDataDir() + "/hook_backup.json"
|
||||
if os.path.exists(hookPath):
|
||||
t = mw.readFile(hookPath)
|
||||
bak_data = json.loads(t)
|
||||
|
||||
if stype == 'database' or stype.find('database_') > -1:
|
||||
sqlite3_name = 'mysql'
|
||||
path = mw.getServerDir() + '/mysql'
|
||||
if stype != 'database':
|
||||
soft_name = stype.replace('database_', '')
|
||||
path = mw.getServerDir() + '/' + soft_name
|
||||
|
||||
if soft_name == 'postgresql':
|
||||
sqlite3_name = 'pgsql'
|
||||
|
||||
if soft_name == 'mongodb':
|
||||
sqlite3_name = 'mongodb'
|
||||
|
||||
db_list = {}
|
||||
db_list['orderOpt'] = bak_data
|
||||
|
||||
if not os.path.exists(path + '/' + sqlite3_name + '.db'):
|
||||
db_list['data'] = []
|
||||
else:
|
||||
db_list['data'] = mw.M('databases').dbPos(path, sqlite3_name).field('name,ps').select()
|
||||
return mw.getJson(db_list)
|
||||
|
||||
if stype == 'path':
|
||||
db_list = {}
|
||||
db_list['data'] = [{"name": mw.getWwwDir(), "ps": "www"}]
|
||||
db_list['orderOpt'] = bak_data
|
||||
return mw.getJson(db_list)
|
||||
|
||||
data = {}
|
||||
data['orderOpt'] = bak_data
|
||||
|
||||
default_db = 'sites'
|
||||
# if stype == 'site' or stype == 'logs':
|
||||
# stype == 'sites'
|
||||
|
||||
data['data'] = mw.M(default_db).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"
|
||||
|
||||
start_head = '''
|
||||
SCRIPT_RUN_TIME="0s"
|
||||
MW_ToSeconds()
|
||||
{
|
||||
SEC=$1
|
||||
if [ $SEC -lt 60 ]; then
|
||||
SCRIPT_RUN_TIME="${SEC}s"
|
||||
elif [ $SEC -ge 60 ] && [ $SEC -lt 3600 ];then
|
||||
SCRIPT_RUN_TIME="$(( SEC / 60 ))m$(( SEC % 60 ))s"
|
||||
elif [ $SEC -ge 3600 ]; then
|
||||
SCRIPT_RUN_TIME="$(( SEC / 3600 ))h$(( (SEC % 3600) / 60 ))m$(( (SEC % 3600) % 60 ))s"
|
||||
fi
|
||||
}
|
||||
START_MW_SHELL_TIME=`date +%s`
|
||||
'''
|
||||
|
||||
source_bin_activate = '''
|
||||
export LANG=en_US.UTF-8
|
||||
MW_PATH=%s/bin/activate
|
||||
if [ -f $MW_PATH ];then
|
||||
source $MW_PATH
|
||||
fi''' % (mw.getRunDir(),)
|
||||
|
||||
head = head + start_head + source_bin_activate + "\n"
|
||||
log = '.log'
|
||||
|
||||
script_dir = mw.getRunDir() + "/scripts"
|
||||
source_stype = 'database'
|
||||
if stype.find('database_') > -1:
|
||||
plugin_name = stype.replace('database_', '')
|
||||
script_dir = mw.getRunDir() + "/plugins/" + plugin_name + "/scripts"
|
||||
|
||||
source_stype = stype
|
||||
stype = 'database'
|
||||
|
||||
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'])
|
||||
wheres['site'] = head + "python3 " + cfile + \
|
||||
" site " + param['sname'] + " " + str(param['save'])
|
||||
wheres['database'] = head + "python3 " + cfile + " " + \
|
||||
source_stype + " " + \
|
||||
param['sname'] + " " + str(param['save'])
|
||||
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"`
|
||||
END_MW_SHELL_TIME=`date +"%s"`
|
||||
((SHELL_COS_TIME=($END_MW_SHELL_TIME-$START_MW_SHELL_TIME)))
|
||||
MW_ToSeconds $SHELL_COS_TIME
|
||||
echo "★[$endDate] Successful | Script Run [$SCRIPT_RUN_TIME] "
|
||||
echo "----------------------------------------------------------------------------"
|
||||
'''
|
||||
cronPath = mw.getServerDir() + '/cron'
|
||||
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):
|
||||
file = '/var/spool/cron/crontabs/root'
|
||||
current_os = mw.getOs()
|
||||
if current_os == 'darwin':
|
||||
file = '/etc/crontab'
|
||||
elif current_os.startswith("freebsd"):
|
||||
file = '/var/cron/tabs/root'
|
||||
|
||||
if not os.path.exists(file):
|
||||
file = '/var/spool/cron/root'
|
||||
|
||||
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(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():
|
||||
# mw.execShell('/usr/sbin/cron restart')
|
||||
if os.path.exists('/etc/crontab'):
|
||||
pass
|
||||
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()
|
||||
396
class/core/db.py
396
class/core/db.py
@@ -1,396 +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)
|
||||
# print(self.__OPT_PARAM)
|
||||
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
|
||||
|
||||
if os.path.exists('data/debug.pl'):
|
||||
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
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,513 +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
|
||||
__isIptables = False
|
||||
__isUfw = False
|
||||
__isMac = False
|
||||
|
||||
def __init__(self):
|
||||
iptables_file = mw.systemdCfgDir() + '/iptables.service'
|
||||
if os.path.exists('/usr/sbin/firewalld'):
|
||||
self.__isFirewalld = True
|
||||
elif os.path.exists('/usr/sbin/ufw'):
|
||||
self.__isUfw = True
|
||||
elif os.path.exists(iptables_file):
|
||||
self.__isIptables = True
|
||||
elif 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.__isIptables:
|
||||
cmd = 'iptables -I INPUT -s ' + address + ' -j DROP'
|
||||
mw.execShell(cmd)
|
||||
elif self.__isFirewalld:
|
||||
cmd = 'firewall-cmd --permanent --add-rich-rule=\'rule family=ipv4 source address="' + \
|
||||
address + '" drop\''
|
||||
mw.execShell(cmd)
|
||||
else:
|
||||
pass
|
||||
|
||||
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()
|
||||
protocol = request.form.get('protocol', '').strip()
|
||||
|
||||
data = self.addAcceptPortArgs(port, ps, stype, protocol)
|
||||
return mw.getJson(data)
|
||||
|
||||
# 添加放行端口
|
||||
def addAcceptPortArgs(self, port, ps, stype, protocol='tcp'):
|
||||
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}][{2}]成功', (port, protocol,))
|
||||
mw.writeLog("防火墙管理", msg)
|
||||
addtime = time.strftime('%Y-%m-%d %X', time.localtime())
|
||||
mw.M('firewall').add('port,protocol,ps,addtime',
|
||||
(port, protocol, ps, addtime))
|
||||
|
||||
self.addAcceptPort(port, protocol)
|
||||
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')
|
||||
elif self.__isFirewalld:
|
||||
mw.execShell(
|
||||
'firewall-cmd --permanent --remove-rich-rule=\'rule family=ipv4 source address="' + address + '" drop\'')
|
||||
elif self.__isIptables:
|
||||
cmd = 'iptables -D INPUT -s ' + address + ' -j DROP'
|
||||
mw.execShell(cmd)
|
||||
else:
|
||||
pass
|
||||
|
||||
msg = mw.getInfo('解除IP[{1}]的屏蔽!', (address,))
|
||||
mw.writeLog("防火墙管理", msg)
|
||||
mw.M('firewall').where("id=?", (sid,)).delete()
|
||||
|
||||
self.firewallReload()
|
||||
return mw.returnJson(True, '删除成功!')
|
||||
|
||||
def delAcceptPortArgs(self, port, protocol='tcp'):
|
||||
if self.__isUfw:
|
||||
if protocol == 'tcp':
|
||||
mw.execShell('ufw delete allow ' + port + '/tcp')
|
||||
if protocol == 'udp':
|
||||
mw.execShell('ufw delete allow ' + port + '/udp')
|
||||
if protocol == 'tcp/udp':
|
||||
mw.execShell('ufw delete allow ' + port + '/tcp')
|
||||
mw.execShell('ufw delete allow ' + port + '/udp')
|
||||
elif self.__isFirewalld:
|
||||
port = port.replace(':', '-')
|
||||
if protocol == 'tcp':
|
||||
mw.execShell(
|
||||
'firewall-cmd --permanent --zone=public --remove-port=' + port + '/tcp')
|
||||
if protocol == 'udp':
|
||||
mw.execShell(
|
||||
'firewall-cmd --permanent --zone=public --remove-port=' + port + '/udp')
|
||||
if protocol == 'tcp/udp':
|
||||
mw.execShell(
|
||||
'firewall-cmd --permanent --zone=public --remove-port=' + port + '/tcp')
|
||||
mw.execShell(
|
||||
'firewall-cmd --permanent --zone=public --remove-port=' + port + '/udp')
|
||||
elif self.__isIptables:
|
||||
if protocol == 'tcp':
|
||||
mw.execShell(
|
||||
'iptables -D INPUT -p tcp -m state --state NEW -m tcp --dport ' + port + ' -j ACCEPT')
|
||||
if protocol == 'udp':
|
||||
mw.execShell(
|
||||
'iptables -D INPUT -p udp -m state --state NEW -m udp --dport ' + port + ' -j ACCEPT')
|
||||
if protocol == 'tcp/udp':
|
||||
mw.execShell(
|
||||
'iptables -D INPUT -p tcp -m state --state NEW -m tcp --dport ' + port + ' -j ACCEPT')
|
||||
mw.execShell(
|
||||
'iptables -D INPUT -p udp -m state --state NEW -m udp --dport ' + port + ' -j ACCEPT')
|
||||
else:
|
||||
pass
|
||||
|
||||
mw.M('firewall').where("port=?", (port,)).delete()
|
||||
|
||||
msg = mw.getInfo('删除防火墙放行端口[{1}][{2}]成功!', (port, protocol,))
|
||||
mw.writeLog("防火墙管理", msg)
|
||||
|
||||
self.firewallReload()
|
||||
return True
|
||||
|
||||
# 删除放行端口
|
||||
def delAcceptPortApi(self):
|
||||
port = request.form.get('port', '').strip()
|
||||
protocol = request.form.get('protocol', 'tcp').strip()
|
||||
sid = request.form.get('id', '').strip()
|
||||
mw_port = mw.readFile('data/port.pl')
|
||||
|
||||
if(port == mw_port):
|
||||
return mw.returnJson(False, '失败,不能删除当前面板端口!')
|
||||
try:
|
||||
self.delAcceptPortArgs(port, protocol)
|
||||
mw.M('firewall').where("id=?", (sid,)).delete()
|
||||
|
||||
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 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:
|
||||
if pass_status and pass_status.groups(0)[0].strip() == 'no':
|
||||
data['pass_prohibit_status'] = True
|
||||
else:
|
||||
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)
|
||||
|
||||
self.addAcceptPortArgs(port, 'SSH端口修改', 'port')
|
||||
if self.__isUfw:
|
||||
mw.execShell("service ssh restart")
|
||||
elif self.__isIptables:
|
||||
mw.execShell("/etc/init.d/sshd restart")
|
||||
elif self.__isFirewalld:
|
||||
mw.execShell("systemctl restart sshd.service")
|
||||
else:
|
||||
return mw.returnJson(False, '修改失败!')
|
||||
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)
|
||||
|
||||
pass_rep = "PasswordAuthentication\s+(\w*)\s*\n"
|
||||
pass_status = re.search(pass_rep, conf)
|
||||
if not pass_status:
|
||||
rep = "(#)?PasswordAuthentication\s+(\w*)\s*\n"
|
||||
conf = re.sub(rep, "PasswordAuthentication yes\n", conf)
|
||||
|
||||
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 setFwIptables(self, status):
|
||||
# iptables特殊处理
|
||||
if status == '1':
|
||||
mw.execShell('service iptables save')
|
||||
mw.execShell('service iptables stop')
|
||||
else:
|
||||
# 重新导入数据
|
||||
_list = mw.M('firewall').field('id,port,ps,addtime').limit(
|
||||
'0,1000').order('id desc').select()
|
||||
|
||||
mw.execShell('iptables -P INPUT DROP')
|
||||
mw.execShell('iptables -P OUTPUT ACCEPT')
|
||||
for x in _list:
|
||||
port = x['port']
|
||||
if mw.isIpAddr(port):
|
||||
cmd = 'iptables -I INPUT -s ' + port + ' -j DROP'
|
||||
mw.execShell(cmd)
|
||||
else:
|
||||
self.addAcceptPort(port)
|
||||
|
||||
mw.execShell('service iptables save')
|
||||
mw.execShell('service iptables start')
|
||||
|
||||
def setFw(self, status):
|
||||
|
||||
if self.__isIptables:
|
||||
self.setFwIptables(status)
|
||||
return mw.returnData(True, '设置成功!')
|
||||
|
||||
if status == '1':
|
||||
if self.__isUfw:
|
||||
mw.execShell('/usr/sbin/ufw disable')
|
||||
|
||||
elif self.__isFirewalld:
|
||||
mw.execShell('systemctl stop firewalld.service')
|
||||
mw.execShell('systemctl disable firewalld.service')
|
||||
else:
|
||||
pass
|
||||
else:
|
||||
if self.__isUfw:
|
||||
mw.execShell("echo 'y'| ufw enable")
|
||||
elif self.__isFirewalld:
|
||||
mw.execShell('systemctl start firewalld.service')
|
||||
mw.execShell('systemctl enable firewalld.service')
|
||||
else:
|
||||
pass
|
||||
|
||||
return mw.returnData(True, '设置成功!')
|
||||
|
||||
##### ----- start ----- ###
|
||||
|
||||
def getList(self, page, limit):
|
||||
|
||||
start = (page - 1) * limit
|
||||
|
||||
_list = mw.M('firewall').field('id,port,protocol,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 addAcceptPort(self, port, protocol='tcp'):
|
||||
if self.__isUfw:
|
||||
if protocol == 'tcp':
|
||||
mw.execShell('ufw allow ' + port + '/tcp')
|
||||
if protocol == 'udp':
|
||||
mw.execShell('ufw allow ' + port + '/udp')
|
||||
if protocol == 'tcp/udp':
|
||||
mw.execShell('ufw allow ' + port + '/tcp')
|
||||
mw.execShell('ufw allow ' + port + '/udp')
|
||||
elif self.__isFirewalld:
|
||||
port = port.replace(':', '-')
|
||||
if protocol == 'tcp':
|
||||
cmd = 'firewall-cmd --permanent --zone=public --add-port=' + port + '/tcp'
|
||||
mw.execShell(cmd)
|
||||
if protocol == 'udp':
|
||||
cmd = 'firewall-cmd --permanent --zone=public --add-port=' + port + '/udp'
|
||||
mw.execShell(cmd)
|
||||
if protocol == 'tcp/udp':
|
||||
cmd = 'firewall-cmd --permanent --zone=public --add-port=' + port + '/tcp'
|
||||
mw.execShell(cmd)
|
||||
cmd = 'firewall-cmd --permanent --zone=public --add-port=' + port + '/udp'
|
||||
mw.execShell(cmd)
|
||||
elif self.__isIptables:
|
||||
if protocol == 'tcp':
|
||||
cmd = 'iptables -I INPUT -p tcp -m state --state NEW -m tcp --dport ' + port + ' -j ACCEPT'
|
||||
mw.execShell(cmd)
|
||||
if protocol == 'udp':
|
||||
cmd = 'iptables -I INPUT -p udp -m state --state NEW -m udp --dport ' + port + ' -j ACCEPT'
|
||||
mw.execShell(cmd)
|
||||
if protocol == 'tcp/udp':
|
||||
cmd = 'iptables -I INPUT -p tcp -m state --state NEW -m tcp --dport ' + port + ' -j ACCEPT'
|
||||
mw.execShell(cmd)
|
||||
cmd = 'iptables -I INPUT -p udp -m state --state NEW -m udp --dport ' + port + ' -j ACCEPT'
|
||||
mw.execShell(cmd)
|
||||
else:
|
||||
pass
|
||||
return True
|
||||
|
||||
def firewallReload(self):
|
||||
if self.__isUfw:
|
||||
mw.execShell('/usr/sbin/ufw reload')
|
||||
return
|
||||
elif self.__isIptables:
|
||||
mw.execShell('service iptables save')
|
||||
mw.execShell('service iptables restart')
|
||||
elif self.__isFirewalld:
|
||||
mw.execShell('firewall-cmd --reload')
|
||||
else:
|
||||
pass
|
||||
|
||||
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
|
||||
elif self.__isIptables:
|
||||
cmd = "systemctl status iptables | grep 'inactive'"
|
||||
data = mw.execShell(cmd)
|
||||
if data[0] != '':
|
||||
return False
|
||||
return True
|
||||
elif self.__isFirewalld:
|
||||
cmd = "ps -ef|grep firewalld |grep -v grep | awk '{print $2}'"
|
||||
data = mw.execShell(cmd)
|
||||
if data[0] == '':
|
||||
return False
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
@@ -1,354 +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 datetime import datetime
|
||||
from flask import request
|
||||
|
||||
|
||||
class logs_api:
|
||||
|
||||
__months = {'Jan': '01', 'Feb': '02', 'Mar': '03', 'Apr': '04', 'May': '05', 'Jun': '06',
|
||||
'Jul': '07', 'Aug': '08', 'Sep': '09', 'Sept': '09', 'Oct': '10', 'Nov': '11', 'Dec': '12'}
|
||||
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
def delPanelLogsApi(self):
|
||||
mw.M('logs').where('id>?', (0,)).delete()
|
||||
mw.writeLog('面板设置', '面板操作日志已清空!')
|
||||
return mw.returnJson(True, '面板操作日志已清空!')
|
||||
|
||||
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 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 getLogsTitle(self, log_name):
|
||||
log_name = log_name.replace('.1', '')
|
||||
if log_name in ['mw-update.log']:
|
||||
return '面板更新日志'
|
||||
if log_name in ['mw-install.log']:
|
||||
return '面板安装日志'
|
||||
if log_name in ['auth.log', 'secure'] or log_name.find('auth.') == 0:
|
||||
return '授权日志'
|
||||
if log_name in ['dmesg'] or log_name.find('dmesg') == 0:
|
||||
return '内核缓冲区日志'
|
||||
if log_name in ['syslog'] or log_name.find('syslog') == 0:
|
||||
return '系统日志'
|
||||
if log_name in ['rsyncd.log']:
|
||||
return '远程同步日志'
|
||||
if log_name in ['btmp']:
|
||||
return '失败的登录记录'
|
||||
if log_name in ['utmp', 'wtmp']:
|
||||
return '登录和重启记录'
|
||||
if log_name in ['lastlog']:
|
||||
return '用户最后登录'
|
||||
if log_name in ['yum.log']:
|
||||
return 'yum包管理器日志'
|
||||
if log_name in ['anaconda.log']:
|
||||
return 'Anaconda日志'
|
||||
if log_name in ['dpkg.log']:
|
||||
return 'dpkg日志'
|
||||
if log_name in ['daemon.log']:
|
||||
return '系统后台守护进程日志'
|
||||
if log_name in ['boot.log']:
|
||||
return '启动日志'
|
||||
if log_name in ['kern.log']:
|
||||
return '内核日志'
|
||||
if log_name in ['maillog', 'mail.log']:
|
||||
return '邮件日志'
|
||||
if log_name.find('Xorg') == 0:
|
||||
return 'Xorg日志'
|
||||
if log_name in ['cron.log']:
|
||||
return '定时任务日志'
|
||||
if log_name in ['alternatives.log']:
|
||||
return '更新替代信息'
|
||||
if log_name in ['debug']:
|
||||
return '调试信息'
|
||||
if log_name.find('apt') == 0:
|
||||
return 'apt-get相关日志'
|
||||
if log_name.find('installer') == 0:
|
||||
return '系统安装相关日志'
|
||||
if log_name in ['messages']:
|
||||
return '综合日志'
|
||||
return '{}日志'.format(log_name.split('.')[0])
|
||||
|
||||
def getAuditLogsFilesApi(self):
|
||||
log_dir = '/var/log'
|
||||
log_files = []
|
||||
for log_file in os.listdir(log_dir):
|
||||
log_suffix = log_file.split('.')[-1:]
|
||||
if log_suffix[0] in ['gz', 'xz', 'bz2', 'asl']:
|
||||
continue
|
||||
|
||||
if log_file in ['.', '..', 'faillog', 'fontconfig.log', 'unattended-upgrades', 'tallylog']:
|
||||
continue
|
||||
|
||||
filename = os.path.join(log_dir, log_file)
|
||||
if os.path.isfile(filename):
|
||||
file_size = os.path.getsize(filename)
|
||||
if not file_size:
|
||||
continue
|
||||
|
||||
tmp = {
|
||||
'name': log_file,
|
||||
'size': file_size,
|
||||
'log_file': filename,
|
||||
'title': self.getLogsTitle(log_file),
|
||||
'uptime': os.path.getmtime(filename)
|
||||
}
|
||||
|
||||
log_files.append(tmp)
|
||||
else:
|
||||
for next_name in os.listdir(filename):
|
||||
if next_name[-3:] in ['.gz', '.xz']:
|
||||
continue
|
||||
next_file = os.path.join(filename, next_name)
|
||||
if not os.path.isfile(next_file):
|
||||
continue
|
||||
file_size = os.path.getsize(next_file)
|
||||
if not file_size:
|
||||
continue
|
||||
log_name = '{}/{}'.format(log_file, next_name)
|
||||
tmp = {
|
||||
'name': log_name,
|
||||
'size': file_size,
|
||||
'log_file': next_file,
|
||||
'title': self.getLogsTitle(log_name),
|
||||
'uptime': os.path.getmtime(next_file)
|
||||
}
|
||||
log_files.append(tmp)
|
||||
log_files = sorted(log_files, key=lambda x: x['name'], reverse=True)
|
||||
return mw.getJson(log_files)
|
||||
|
||||
def getAuditFileApi(self):
|
||||
log_name = request.form.get('log_name', '').strip()
|
||||
return self.getAuditLogsName(log_name)
|
||||
|
||||
def __to_date2(self, date_str):
|
||||
tmp = date_str.split()
|
||||
s_date = str(tmp[-1]) + '-' + self.__months.get(tmp[1],
|
||||
tmp[1]) + '-' + tmp[2] + ' ' + tmp[3]
|
||||
return s_date
|
||||
|
||||
def __to_date3(self, date_str):
|
||||
tmp = date_str.split()
|
||||
s_date = str(datetime.now().year) + '-' + \
|
||||
self.__months.get(tmp[1], tmp[1]) + '-' + tmp[2] + ' ' + tmp[3]
|
||||
return s_date
|
||||
|
||||
def __to_date4(self, date_str):
|
||||
tmp = date_str.split()
|
||||
s_date = str(datetime.now().year) + '-' + \
|
||||
self.__months.get(tmp[0], tmp[0]) + '-' + tmp[1] + ' ' + tmp[2]
|
||||
return s_date
|
||||
|
||||
def getAuditLast(self, log_name):
|
||||
# 获取日志
|
||||
cmd = '''LANG=en_US.UTF-8 last -n 200 -x -f {} |grep -v 127.0.0.1|grep -v " begins"'''.format(
|
||||
'/var/log/' + log_name)
|
||||
result = mw.execShell(cmd)
|
||||
lastlog_list = []
|
||||
for _line in result[0].split("\n"):
|
||||
if not _line:
|
||||
continue
|
||||
tmp = {}
|
||||
sp_arr = _line.split()
|
||||
tmp['用户'] = sp_arr[0]
|
||||
if sp_arr[0] == 'runlevel':
|
||||
tmp['来源'] = sp_arr[4]
|
||||
tmp['端口'] = ' '.join(sp_arr[1:4])
|
||||
tmp['时间'] = self.__to_date3(
|
||||
' '.join(sp_arr[5:])) + ' ' + ' '.join(sp_arr[-2:])
|
||||
elif sp_arr[0] in ['reboot', 'shutdown']:
|
||||
tmp['来源'] = sp_arr[3]
|
||||
tmp['端口'] = ' '.join(sp_arr[1:3])
|
||||
if sp_arr[-3] == '-':
|
||||
tmp['时间'] = self.__to_date3(
|
||||
' '.join(sp_arr[4:])) + ' ' + ' '.join(sp_arr[-3:])
|
||||
else:
|
||||
tmp['时间'] = self.__to_date3(
|
||||
' '.join(sp_arr[4:])) + ' ' + ' '.join(sp_arr[-2:])
|
||||
elif sp_arr[1] in ['tty1', 'tty', 'tty2', 'tty3', 'hvc0', 'hvc1', 'hvc2'] or len(sp_arr) == 9:
|
||||
tmp['来源'] = ''
|
||||
tmp['端口'] = sp_arr[1]
|
||||
tmp['时间'] = self.__to_date3(
|
||||
' '.join(sp_arr[2:])) + ' ' + ' '.join(sp_arr[-3:])
|
||||
else:
|
||||
tmp['来源'] = sp_arr[2]
|
||||
tmp['端口'] = sp_arr[1]
|
||||
tmp['时间'] = self.__to_date3(
|
||||
' '.join(sp_arr[3:])) + ' ' + ' '.join(sp_arr[-3:])
|
||||
|
||||
# tmp['_line'] = _line
|
||||
lastlog_list.append(tmp)
|
||||
# lastlog_list = sorted(lastlog_list,key=lambda x:x['时间'],reverse=True)
|
||||
return mw.returnData(True, 'ok!', lastlog_list)
|
||||
|
||||
def getAuditLastLog(self):
|
||||
cmd = '''LANG=en_US.UTF-8 lastlog|grep -v Username'''
|
||||
result = mw.execShell(cmd)
|
||||
lastlog_list = []
|
||||
for _line in result[0].split("\n"):
|
||||
if not _line:
|
||||
continue
|
||||
tmp = {}
|
||||
sp_arr = _line.split()
|
||||
tmp['用户'] = sp_arr[0]
|
||||
# tmp['_line'] = _line
|
||||
if _line.find('Never logged in') != -1:
|
||||
tmp['最后登录时间'] = '0'
|
||||
tmp['最后登录来源'] = '-'
|
||||
tmp['最后登录端口'] = '-'
|
||||
lastlog_list.append(tmp)
|
||||
continue
|
||||
tmp['最后登录来源'] = sp_arr[2]
|
||||
tmp['最后登录端口'] = sp_arr[1]
|
||||
tmp['最后登录时间'] = self.__to_date2(' '.join(sp_arr[3:]))
|
||||
|
||||
lastlog_list.append(tmp)
|
||||
lastlog_list = sorted(lastlog_list, key=lambda x: x[
|
||||
'最后登录时间'], reverse=True)
|
||||
for i in range(len(lastlog_list)):
|
||||
if lastlog_list[i]['最后登录时间'] == '0':
|
||||
lastlog_list[i]['最后登录时间'] = '从未登录过'
|
||||
return mw.returnData(True, 'ok!', lastlog_list)
|
||||
|
||||
def parseAuditFile(self, log_name, result):
|
||||
log_list = []
|
||||
is_string = True
|
||||
for _line in result.split("\n"):
|
||||
if not _line.strip():
|
||||
continue
|
||||
if log_name.find('sa/sa') == -1:
|
||||
if _line[:3] in self.__months:
|
||||
_msg = _line[16:]
|
||||
_tmp = _msg.split(": ")
|
||||
_act = ''
|
||||
if len(_tmp) > 1:
|
||||
_act = _tmp[0]
|
||||
_msg = _tmp[1]
|
||||
else:
|
||||
_msg = _tmp[0]
|
||||
_line = {
|
||||
"时间": self.__to_date4(_line[:16].strip()),
|
||||
"角色": _act,
|
||||
"事件": _msg
|
||||
}
|
||||
is_string = False
|
||||
elif _line[:2] in ['19', '20', '21', '22', '23', '24']:
|
||||
_msg = _line[19:]
|
||||
_tmp = _msg.split(" ")
|
||||
_act = _tmp[1]
|
||||
_msg = ' '.join(_tmp[2:])
|
||||
_line = {
|
||||
"时间": _line[:19].strip(),
|
||||
"角色": _act,
|
||||
"事件": _msg
|
||||
}
|
||||
is_string = False
|
||||
elif log_name.find('alternatives') == 0:
|
||||
_tmp = _line.split(": ")
|
||||
_last = _tmp[0].split(" ")
|
||||
_act = _last[0]
|
||||
_msg = ' '.join(_tmp[1:])
|
||||
_line = {
|
||||
"时间": ' '.join(_last[1:]).strip(),
|
||||
"角色": _act,
|
||||
"事件": _msg
|
||||
}
|
||||
is_string = False
|
||||
else:
|
||||
if not is_string:
|
||||
if type(_line) != dict:
|
||||
continue
|
||||
|
||||
log_list.append(_line)
|
||||
|
||||
return log_list
|
||||
|
||||
def getAuditLogsName(self, log_name):
|
||||
# print(log_name)
|
||||
if log_name in ['wtmp', 'btmp', 'utmp'] or log_name.find('wtmp') == 0 or log_name.find('btmp') == 0 or log_name.find('utmp') == 0:
|
||||
return self.getAuditLast(log_name)
|
||||
|
||||
if log_name.find('lastlog') == 0:
|
||||
return self.getAuditLastLog()
|
||||
|
||||
if log_name.find('sa/sa') == 0:
|
||||
if log_name.find('sa/sar') == -1:
|
||||
return mw.execShell("sar -f /var/log/{}".format(log_name))[0]
|
||||
log_dir = '/var/log'
|
||||
log_file = log_dir + '/' + log_name
|
||||
if not os.path.exists(log_file):
|
||||
return mw.returnData(False, '日志文件不存在!')
|
||||
result = mw.getLastLine(log_file, 100)
|
||||
try:
|
||||
log_list = self.parseAuditFile(log_name, result)
|
||||
_string = []
|
||||
_dict = []
|
||||
_list = []
|
||||
for _line in log_list:
|
||||
if isinstance(_line, str):
|
||||
_string.append(_line.strip())
|
||||
elif isinstance(_line, dict):
|
||||
_dict.append(_line)
|
||||
elif isinstance(_line, list):
|
||||
_list.append(_line)
|
||||
else:
|
||||
continue
|
||||
_str_len = len(_string)
|
||||
_dict_len = len(_dict)
|
||||
_list_len = len(_list)
|
||||
if _str_len > _dict_len + _list_len:
|
||||
return "\n".join(_string)
|
||||
elif _dict_len > _str_len + _list_len:
|
||||
return mw.returnData(True, 'ok!', _dict)
|
||||
else:
|
||||
return mw.returnData(True, 'ok!', _list)
|
||||
|
||||
except:
|
||||
return mw.returnData(True, 'ok!', result)
|
||||
2213
class/core/mw.py
2213
class/core/mw.py
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -1,456 +0,0 @@
|
||||
# coding: utf-8
|
||||
|
||||
# ---------------------------------------------------------------------------------
|
||||
# MW-Linux面板
|
||||
# ---------------------------------------------------------------------------------
|
||||
# copyright (c) 2018-∞(https://github.com/midoks/mdserver-web) All rights reserved.
|
||||
# ---------------------------------------------------------------------------------
|
||||
# Author: midoks <midoks@163.com>
|
||||
# ---------------------------------------------------------------------------------
|
||||
|
||||
# ---------------------------------------------------------------------------------
|
||||
# SSH终端操作
|
||||
# ---------------------------------------------------------------------------------
|
||||
|
||||
import json
|
||||
import time
|
||||
import os
|
||||
import sys
|
||||
import socket
|
||||
import threading
|
||||
import re
|
||||
|
||||
from io import BytesIO, StringIO
|
||||
|
||||
import mw
|
||||
import paramiko
|
||||
|
||||
from flask_socketio import SocketIO, emit, send
|
||||
|
||||
|
||||
class ssh_terminal:
|
||||
|
||||
__debug_file = 'logs/terminal.log'
|
||||
__log_type = 'SSH终端'
|
||||
|
||||
# websocketio 唯一标识
|
||||
__sid = ''
|
||||
|
||||
__host = None
|
||||
__type = '0'
|
||||
__port = 22
|
||||
__user = None
|
||||
__pass = None
|
||||
__pkey = None
|
||||
__key_passwd = None
|
||||
|
||||
__rep_ssh_config = False
|
||||
__rep_ssh_service = False
|
||||
__sshd_config_backup = None
|
||||
|
||||
__ssh = None
|
||||
__tp = None
|
||||
__ps = None
|
||||
|
||||
__ssh_list = {}
|
||||
__ssh_last_request_time = {}
|
||||
|
||||
def __init__(self):
|
||||
ht = threading.Thread(target=self.heartbeat)
|
||||
ht.start()
|
||||
|
||||
def debug(self, msg):
|
||||
msg = "{} - {}:{} => {} \n".format(mw.formatDate(),
|
||||
self.__host, self.__port, msg)
|
||||
if not mw.isDebugMode():
|
||||
return
|
||||
mw.writeFile(self.__debug_file, msg, 'a+')
|
||||
|
||||
def returnMsg(self, status, msg):
|
||||
return {'status': status, 'msg': msg}
|
||||
|
||||
def restartSsh(self, act='reload'):
|
||||
'''
|
||||
重启ssh 无参数传递
|
||||
'''
|
||||
version = mw.readFile('/etc/redhat-release')
|
||||
if not os.path.exists('/etc/redhat-release'):
|
||||
mw.execShell('service ssh ' + act)
|
||||
elif version.find(' 7.') != -1 or version.find(' 8.') != -1:
|
||||
mw.execShell("systemctl " + act + " sshd.service")
|
||||
else:
|
||||
mw.execShell("/etc/init.d/sshd " + act)
|
||||
|
||||
def isRunning(self, rep=False):
|
||||
try:
|
||||
if rep and self.__rep_ssh_service:
|
||||
self.restartSsh('stop')
|
||||
return True
|
||||
|
||||
status = self.getSshStatus()
|
||||
if not status:
|
||||
self.restartSsh('start')
|
||||
self.__rep_ssh_service = True
|
||||
return True
|
||||
return False
|
||||
except:
|
||||
return False
|
||||
|
||||
def setSshdConfig(self, rep=False):
|
||||
self.isRunning(rep)
|
||||
if rep and not self.__rep_ssh_config:
|
||||
return False
|
||||
|
||||
try:
|
||||
sshd_config_file = '/etc/ssh/sshd_config'
|
||||
if not os.path.exists(sshd_config_file):
|
||||
return False
|
||||
|
||||
sshd_config = mw.readFile(sshd_config_file)
|
||||
if not sshd_config:
|
||||
return False
|
||||
|
||||
if rep:
|
||||
if self.__sshd_config_backup:
|
||||
mw.writeFile(sshd_config_file, self.__sshd_config_backup)
|
||||
self.restartSsh()
|
||||
return True
|
||||
|
||||
pin = r'^\s*PubkeyAuthentication\s+(yes|no)'
|
||||
pubkey_status = re.findall(pin, sshd_config, re.I)
|
||||
if pubkey_status:
|
||||
if pubkey_status[0] == 'yes':
|
||||
pubkey_status = True
|
||||
else:
|
||||
pubkey_status = False
|
||||
|
||||
pin = r'^\s*RSAAuthentication\s+(yes|no)'
|
||||
rsa_status = re.findall(pin, sshd_config, re.I)
|
||||
if rsa_status:
|
||||
if rsa_status[0] == 'yes':
|
||||
rsa_status = True
|
||||
else:
|
||||
rsa_status = False
|
||||
|
||||
self._sshd_config_backup = sshd_config
|
||||
is_write = False
|
||||
if not pubkey_status:
|
||||
sshd_config = re.sub(
|
||||
r'\n#?PubkeyAuthentication\s\w+', '\nPubkeyAuthentication yes', sshd_config)
|
||||
is_write = True
|
||||
if not rsa_status:
|
||||
sshd_config = re.sub(
|
||||
r'\n#?RSAAuthentication\s\w+', '\nRSAAuthentication yes', sshd_config)
|
||||
is_write = True
|
||||
|
||||
if is_write:
|
||||
mw.writeFile(sshd_config_file, sshd_config)
|
||||
self.__rep_ssh_config = True
|
||||
self.restartSsh()
|
||||
else:
|
||||
self.__sshd_config_backup = None
|
||||
return True
|
||||
except:
|
||||
return False
|
||||
|
||||
def setSid(self, sid):
|
||||
self.__sid = sid
|
||||
|
||||
def connect(self, sid):
|
||||
# self.connectBySocket()
|
||||
if self.__host in ['127.0.0.1', 'localhost']:
|
||||
return self.connectLocalSsh(sid)
|
||||
else:
|
||||
return self.connectBySocket(sid)
|
||||
|
||||
def connectLocalSsh(self, sid):
|
||||
mw.createSshInfo()
|
||||
self.__ps = paramiko.SSHClient()
|
||||
self.__ps.set_missing_host_key_policy(paramiko.AutoAddPolicy())
|
||||
|
||||
self.__port = mw.getSSHPort()
|
||||
try:
|
||||
self.__ps.connect(self.__host, self.__port, timeout=60)
|
||||
except Exception as e:
|
||||
self.__ps.connect('127.0.0.1', self.__port)
|
||||
except Exception as e:
|
||||
self.__ps.connect('localhost', self.__port)
|
||||
except Exception as e:
|
||||
self.setSshdConfig(True)
|
||||
self.__ps.close()
|
||||
e = str(e)
|
||||
if e.find('websocket error!') != -1:
|
||||
return self.returnMsg(True, '连接成功')
|
||||
if e.find('Authentication timeout') != -1:
|
||||
self.debug("认证超时{}".format(e))
|
||||
return self.returnMsg(False, '认证超时,请按回车重试!{}'.format(e))
|
||||
if e.find('Connection reset by peer') != -1:
|
||||
self.debug('目标服务器主动拒绝连接')
|
||||
return self.returnMsg(False, '目标服务器主动拒绝连接')
|
||||
if e.find('Error reading SSH protocol banner') != -1:
|
||||
self.debug('协议头响应超时')
|
||||
return self.returnMsg(False, '协议头响应超时,与目标服务器之间的网络质量太糟糕:' + e)
|
||||
if not e:
|
||||
self.debug('SSH协议握手超时')
|
||||
return self.returnMsg(False, "SSH协议握手超时,与目标服务器之间的网络质量太糟糕")
|
||||
err = mw.getTracebackInfo()
|
||||
self.debug(err)
|
||||
return self.returnMsg(False, "未知错误: {}".format(err))
|
||||
|
||||
self.debug('local-ssh:认证成功,正在构建会话通道')
|
||||
ssh = self.__ps.invoke_shell(
|
||||
term='xterm', width=83, height=21)
|
||||
ssh.setblocking(0)
|
||||
self.__ssh_list[sid] = ssh
|
||||
mw.writeLog(self.__log_type, '成功登录到SSH服务器 [{}:{}]'.format(
|
||||
self.__host, self.__port))
|
||||
self.debug('local-ssh:通道已构建')
|
||||
return self.returnMsg(True, '连接成功!')
|
||||
|
||||
def connectBySocket(self, sid):
|
||||
if not self.__host:
|
||||
return self.returnMsg(False, '错误的连接地址')
|
||||
if not self.__user:
|
||||
self.__user = 'root'
|
||||
if not self.__port:
|
||||
self.__port = 22
|
||||
|
||||
self.setSshdConfig(True)
|
||||
num = 0
|
||||
while num < 5:
|
||||
num += 1
|
||||
try:
|
||||
self.debug('正在尝试第{}次连接'.format(num))
|
||||
if self.__rep_ssh_config:
|
||||
time.sleep(0.1)
|
||||
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
sock.settimeout(2 + num)
|
||||
sock.setsockopt(socket.SOL_SOCKET, socket.SO_SNDBUF, 8192)
|
||||
sock.connect((self.__host, self.__port))
|
||||
break
|
||||
except Exception as e:
|
||||
if num == 5:
|
||||
self.setSshdConfig(True)
|
||||
self.debug('重试连接失败,{}'.format(e))
|
||||
return self.returnMsg(False, '连接目标服务器失败, {}:{}'.format(self.__host, self.__port))
|
||||
else:
|
||||
time.sleep(0.2)
|
||||
|
||||
# print(self.__host, sock)
|
||||
self.__tp = paramiko.Transport(sock)
|
||||
try:
|
||||
self.__tp.start_client()
|
||||
self.__tp.banner_timeout = 60
|
||||
if not self.__pass and not self.__pkey:
|
||||
return self.returnMsg(False, '密码或私钥不能都为空: {}:{}'.format(self.__host, self.__port))
|
||||
|
||||
if self.__pkey != '' and self.__type != '0':
|
||||
self.debug('正在认证私钥')
|
||||
p_file = StringIO(str(self.__pkey.replace('\\n', '\n')))
|
||||
# p_file = "/tmp/t_ssh_pkey.txt"
|
||||
# mw.writeFile(p_file, self.__pkey.replace('\\n', '\n'))
|
||||
# mw.execShell('chmod 600 ' + p_file)
|
||||
try:
|
||||
p_file.seek(0)
|
||||
pkey = paramiko.RSAKey.from_private_key(p_file)
|
||||
except:
|
||||
try:
|
||||
p_file.seek(0) # 重置游标
|
||||
pkey = paramiko.Ed25519Key.from_private_key(
|
||||
p_file)
|
||||
except:
|
||||
try:
|
||||
p_file.seek(0)
|
||||
pkey = paramiko.ECDSAKey.from_private_key(p_file)
|
||||
except:
|
||||
p_file.seek(0)
|
||||
pkey = paramiko.DSSKey.from_private_key(p_file)
|
||||
|
||||
self.__tp.auth_publickey(username=self.__user, key=pkey)
|
||||
else:
|
||||
try:
|
||||
self.__tp.auth_none(self.__user)
|
||||
except Exception as e:
|
||||
e = str(e)
|
||||
if e.find('keyboard-interactive') >= 0:
|
||||
self._auth_interactive()
|
||||
else:
|
||||
self.debug('正在认证密码')
|
||||
self.__tp.auth_password(
|
||||
username=self.__user, password=self.__pass)
|
||||
except Exception as e:
|
||||
self.setSshdConfig(True)
|
||||
self.__tp.close()
|
||||
e = str(e)
|
||||
# print(e)
|
||||
if e.find('Authentication timeout') != -1:
|
||||
self.debug("认证超时{}".format(e))
|
||||
return self.returnMsg(False, '认证超时,请按回车重试!{}'.format(e))
|
||||
if e.find('Authentication failed') != -1:
|
||||
self.debug('认证失败{}'.format(e))
|
||||
return self.returnMsg(False, '帐号或密码错误: {}'.format(e + "," + self.__user + "@" + self.__host + ":" + str(self.__port)))
|
||||
if e.find('Bad authentication type; allowed types') != -1:
|
||||
self.debug('认证失败{}'.format(e))
|
||||
if self.__host in ['127.0.0.1', 'localhost'] and self.__pass == 'none':
|
||||
return self.returnMsg(False, '帐号或密码错误: {}'.format("Authentication failed ," + self.__user + "@" + self.__host + ":" + str(self.__port)))
|
||||
return self.returnMsg(False, '不支持的身份验证类型: {}'.format(e))
|
||||
if e.find('Connection reset by peer') != -1:
|
||||
self.debug('目标服务器主动拒绝连接')
|
||||
return self.returnMsg(False, '目标服务器主动拒绝连接')
|
||||
if e.find('Error reading SSH protocol banner') != -1:
|
||||
self.debug('协议头响应超时')
|
||||
return self.returnMsg(False, '协议头响应超时,与目标服务器之间的网络质量太糟糕:' + e)
|
||||
if not e:
|
||||
self.debug('SSH协议握手超时')
|
||||
return self.returnMsg(False, "SSH协议握手超时,与目标服务器之间的网络质量太糟糕")
|
||||
err = mw.getTracebackInfo()
|
||||
self.debug(err)
|
||||
return self.returnMsg(False, "未知错误: {}".format(err))
|
||||
|
||||
self.debug('认证成功,正在构建会话通道')
|
||||
|
||||
ssh = self.__tp.open_session()
|
||||
ssh.get_pty(term='xterm', width=100, height=34)
|
||||
ssh.invoke_shell()
|
||||
self.__ssh_list[sid] = ssh
|
||||
mw.writeLog(self.__log_type, '成功登录到SSH服务器 [{}:{}]'.format(
|
||||
self.__host, self.__port))
|
||||
self.debug('通道已构建')
|
||||
return self.returnMsg(True, '连接成功.')
|
||||
|
||||
def getSshInfo(self, file):
|
||||
rdata = mw.readFile(file)
|
||||
destr = mw.enDoubleCrypt('mdserver-web', rdata)
|
||||
return json.loads(destr)
|
||||
|
||||
def setAttr(self, sid, info):
|
||||
self.__host = info['host'].strip()
|
||||
|
||||
# 外部连接获取
|
||||
if not self.__host in ['127.0.0.1', 'localhost']:
|
||||
dst_info = mw.getServerDir() + '/webssh/host/' + self.__host + '/info.json'
|
||||
if os.path.exists(dst_info):
|
||||
info = self.getSshInfo(dst_info)
|
||||
|
||||
if 'type' in info:
|
||||
self.__type = info['type']
|
||||
|
||||
if 'port' in info:
|
||||
self.__port = int(info['port'])
|
||||
if 'username' in info:
|
||||
self.__user = info['username']
|
||||
if 'pkey' in info:
|
||||
self.__pkey = info['pkey']
|
||||
if 'password' in info:
|
||||
self.__pass = info['password']
|
||||
if 'pkey_passwd' in info:
|
||||
self.__key_passwd = info['pkey_passwd']
|
||||
|
||||
# print(self.__host, self.__pass, self.__key_passwd)
|
||||
try:
|
||||
result = self.connect(sid)
|
||||
# print(result)
|
||||
except Exception as ex:
|
||||
if str(ex).find("NoneType") == -1:
|
||||
raise ex
|
||||
return result
|
||||
|
||||
def send(self):
|
||||
pass
|
||||
|
||||
def close(self):
|
||||
try:
|
||||
if self.__ssh:
|
||||
self.__ssh.close()
|
||||
if self.__tp: # 关闭宿主服务
|
||||
self.__tp.close()
|
||||
if self.__ps:
|
||||
self.__ps.close()
|
||||
except:
|
||||
pass
|
||||
|
||||
def resize(self, sid, data):
|
||||
try:
|
||||
self.__ssh_list[sid].resize_pty(
|
||||
width=data['cols'], height=data['rows'])
|
||||
return True
|
||||
except:
|
||||
return False
|
||||
|
||||
def wsSend(self, recv):
|
||||
try:
|
||||
t = recv.decode("utf-8")
|
||||
return emit('server_response', {'data': t})
|
||||
except Exception as e:
|
||||
return emit('server_response', {'data': recv})
|
||||
|
||||
def wsSendConnect(self):
|
||||
return emit('connect', {'data': 'ok'})
|
||||
|
||||
def wsSendReConnect(self):
|
||||
return emit('reconnect', {'data': 'ok'})
|
||||
|
||||
def heartbeat(self):
|
||||
# limit_cos = 10
|
||||
while True:
|
||||
time.sleep(3)
|
||||
cur_time = time.time()
|
||||
for x in list(self.__ssh_list.keys()):
|
||||
ssh_last_time = self.__ssh_last_request_time[x]
|
||||
sid_off_cos = cur_time - ssh_last_time
|
||||
|
||||
# print("heartbeat off cos :", x, sid_off_cos)
|
||||
if sid_off_cos > 3:
|
||||
cur_ssh = self.__ssh_list[x]
|
||||
if not cur_ssh:
|
||||
del(self.__ssh_list[x])
|
||||
del(self.__ssh_last_request_time[x])
|
||||
continue
|
||||
|
||||
if cur_ssh.exit_status_ready():
|
||||
del(self.__ssh_list[x])
|
||||
del(self.__ssh_last_request_time[x])
|
||||
continue
|
||||
|
||||
cur_ssh.send("exit\r\n")
|
||||
del(self.__ssh_list[x])
|
||||
del(self.__ssh_last_request_time[x])
|
||||
|
||||
def run(self, sid, info):
|
||||
self.__sid = sid
|
||||
if not self.__sid:
|
||||
return self.wsSend('WebSocketIO无效')
|
||||
|
||||
self.__ssh_last_request_time[sid] = time.time()
|
||||
if not sid in self.__ssh_list:
|
||||
if type(info) == dict and 'host' in info:
|
||||
result = self.setAttr(sid, info)
|
||||
if result['status']:
|
||||
return self.wsSendConnect()
|
||||
else:
|
||||
return self.wsSend(result['msg'])
|
||||
else:
|
||||
return self.wsSendReConnect()
|
||||
|
||||
result = self.returnMsg(False, '')
|
||||
if sid in self.__ssh_list:
|
||||
if 'resize' in info:
|
||||
self.resize(sid, info)
|
||||
result = self.returnMsg(True, '已连接')
|
||||
if result['status']:
|
||||
if type(info) == str:
|
||||
time.sleep(0.1)
|
||||
cur_ssh = self.__ssh_list[sid]
|
||||
if cur_ssh.exit_status_ready():
|
||||
self.wsSend("logout\r\n")
|
||||
del(self.__ssh_list[sid])
|
||||
return
|
||||
cur_ssh.send(info)
|
||||
try:
|
||||
time.sleep(0.005)
|
||||
recv = cur_ssh.recv(8192)
|
||||
return self.wsSend(recv)
|
||||
except Exception as ex:
|
||||
return self.wsSend('')
|
||||
else:
|
||||
return self.wsSend(result['msg'])
|
||||
@@ -1,978 +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
|
||||
cache = {}
|
||||
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 getEnvInfoApi(self, get=None):
|
||||
serverInfo = {}
|
||||
serverInfo['status'] = True
|
||||
sdir = mw.getServerDir()
|
||||
|
||||
serverInfo['webserver'] = '未安装'
|
||||
if os.path.exists(sdir + '/openresty/nginx/sbin/nginx'):
|
||||
serverInfo['webserver'] = 'OpenResty'
|
||||
serverInfo['php'] = []
|
||||
phpversions = ['52', '53', '54', '55', '56', '70', '71',
|
||||
'72', '73', '74', '80', '81', '82', '83', '84']
|
||||
phpPath = sdir + '/php/'
|
||||
for pv in phpversions:
|
||||
if not os.path.exists(phpPath + pv + '/bin/php'):
|
||||
continue
|
||||
serverInfo['php'].append(pv)
|
||||
serverInfo['mysql'] = False
|
||||
if os.path.exists(sdir + '/mysql/bin/mysql'):
|
||||
serverInfo['mysql'] = True
|
||||
import psutil
|
||||
try:
|
||||
diskInfo = psutil.disk_usage('/www')
|
||||
except:
|
||||
diskInfo = psutil.disk_usage('/')
|
||||
serverInfo['disk'] = diskInfo[2]
|
||||
return mw.returnJson(True, 'ok', serverInfo)
|
||||
|
||||
def getPanelInfo(self, get=None):
|
||||
# 取面板配置
|
||||
address = mw.GetLocalIp()
|
||||
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 getSystemDeviceTemperature(self):
|
||||
if not hasattr(psutil, "sensors_temperatures"):
|
||||
return False, "platform not supported"
|
||||
temps = psutil.sensors_temperatures()
|
||||
if not temps:
|
||||
return False, "can't read any temperature"
|
||||
for name, entries in temps.items():
|
||||
for entry in entries:
|
||||
return True, entry.label
|
||||
# print("%-20s %s °C (high = %s °C, critical = %s °C)" % (
|
||||
# entry.label or name, entry.current, entry.high,
|
||||
# entry.critical))
|
||||
return False, ""
|
||||
|
||||
def getSystemVersion(self):
|
||||
#
|
||||
# 取操作系统版本
|
||||
current_os = mw.getOs()
|
||||
# sys_temper = self.getSystemDeviceTemperature()
|
||||
# print(sys_temper)
|
||||
# mac
|
||||
if current_os == 'darwin':
|
||||
data = mw.execShell('sw_vers')[0]
|
||||
data_list = data.strip().split("\n")
|
||||
mac_version = ''
|
||||
for x in data_list:
|
||||
xlist = x.split("\t")
|
||||
mac_version += xlist[len(xlist)-1] + ' '
|
||||
|
||||
arch_ver = mw.execShell("arch")
|
||||
return mac_version + " (" + arch_ver[0].strip() + ")"
|
||||
|
||||
# freebsd
|
||||
if current_os.startswith('freebsd'):
|
||||
version = mw.execShell(
|
||||
"cat /etc/*-release | grep PRETTY_NAME | awk -F = '{print $2}' | awk -F '\"' '{print $2}'")
|
||||
arch_ver = mw.execShell(
|
||||
"sysctl -a | egrep -i 'hw.machine_arch' | awk -F ':' '{print $2}'")
|
||||
return version[0].strip() + " (" + arch_ver[0].strip() + ")"
|
||||
|
||||
redhat_series = '/etc/redhat-release'
|
||||
if os.path.exists(redhat_series):
|
||||
version = mw.readFile('/etc/redhat-release')
|
||||
version = version.replace('release ', '').strip()
|
||||
|
||||
arch_ver = mw.execShell("arch")
|
||||
return version + " (" + arch_ver[0].strip() + ")"
|
||||
|
||||
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}'")
|
||||
|
||||
arch_ver = mw.execShell("arch")
|
||||
return version[0].strip() + " (" + arch_ver[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)
|
||||
cpuLogicalNum = 0
|
||||
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,
|
||||
}
|
||||
memInfo['memRealUsed'] = memInfo['memTotal'] * (mem.percent / 100)
|
||||
else:
|
||||
memInfo = {
|
||||
'memTotal': mem.total,
|
||||
'memFree': mem.free,
|
||||
'memBuffers': mem.buffers,
|
||||
'memCached': mem.cached
|
||||
}
|
||||
|
||||
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:
|
||||
networkInfo = {}
|
||||
# 取网络流量信息
|
||||
# networkIo = self.psutilNetIoCounters()
|
||||
# if not "otime" in session:
|
||||
# session['up'] = networkIo[0]
|
||||
# session['down'] = networkIo[1]
|
||||
# session['otime'] = time.time()
|
||||
|
||||
# ntime = time.time()
|
||||
# print("source",ntime - session['otime'])
|
||||
|
||||
# networkInfo['upTotal'] = networkIo[0]
|
||||
# networkInfo['downTotal'] = networkIo[1]
|
||||
# networkInfo['up'] = round(float(
|
||||
# networkIo[0] - session['up']) / (ntime - session['otime']), 2)
|
||||
# networkInfo['down'] = round(
|
||||
# float(networkIo[1] - session['down']) / (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()
|
||||
networkInfo['iostat'] = self.getDiskIostat()
|
||||
networkInfo['network'] = self.getNetWorkIostat()
|
||||
|
||||
return networkInfo
|
||||
except Exception as e:
|
||||
print(mw.getTracebackInfo())
|
||||
return None
|
||||
|
||||
def getNetWorkIostat(self):
|
||||
netInfo = {}
|
||||
|
||||
netInfo['ALL'] = {}
|
||||
netInfo['ALL']['up'] = 0
|
||||
netInfo['ALL']['down'] = 0
|
||||
netInfo['ALL']['upTotal'] = 0
|
||||
netInfo['ALL']['downTotal'] = 0
|
||||
netInfo['ALL']['upPackets'] = 0
|
||||
netInfo['ALL']['downPackets'] = 0
|
||||
|
||||
mtime = time.time()
|
||||
iokey = 'netstat'
|
||||
netio = None
|
||||
if iokey in self.cache:
|
||||
netio = self.cache[iokey]
|
||||
|
||||
if not netio:
|
||||
netio = {}
|
||||
netio['info'] = None
|
||||
netio['all_io'] = None
|
||||
netio['time'] = mtime
|
||||
|
||||
stime = mtime - netio['time']
|
||||
if not stime: stime = 1
|
||||
|
||||
# print("new:",stime)
|
||||
netio_group = psutil.net_io_counters(pernic=True).keys()
|
||||
|
||||
netio_cache = netio['info']
|
||||
allio_cache = netio['all_io']
|
||||
if not netio_cache:
|
||||
netio_cache = {}
|
||||
|
||||
netio_group_t = {}
|
||||
for name in netio_group:
|
||||
netInfo[name] = {}
|
||||
|
||||
io_data = psutil.net_io_counters(pernic=True).get(name)
|
||||
if not name in netio_cache:
|
||||
netio_cache[name] = io_data
|
||||
|
||||
netio_group_t[name] = io_data
|
||||
|
||||
netInfo[name]['up'] = round(float((io_data[0] - netio_cache[name][0]) / stime), 2)
|
||||
netInfo[name]['down'] = round(float((io_data[1] - netio_cache[name][1])/ stime), 2)
|
||||
|
||||
netInfo[name]['upTotal'] = io_data[0]
|
||||
netInfo[name]['downTotal'] = io_data[1]
|
||||
netInfo[name]['upPackets'] = io_data[2]
|
||||
netInfo[name]['downPackets'] = io_data[3]
|
||||
|
||||
all_io = psutil.net_io_counters()[:4]
|
||||
if not allio_cache:
|
||||
allio_cache = all_io
|
||||
|
||||
netInfo['ALL']['up'] = round(float((all_io[0] - allio_cache[0]) /stime), 2)
|
||||
netInfo['ALL']['down'] = round(float((all_io[1] - allio_cache[1]) /stime), 2)
|
||||
netInfo['ALL']['upTotal'] = all_io[0]
|
||||
netInfo['ALL']['downTotal'] = all_io[1]
|
||||
netInfo['ALL']['upPackets'] = all_io[2]
|
||||
netInfo['ALL']['downPackets'] = all_io[3]
|
||||
|
||||
self.cache[iokey] = {'info':netio_group_t,'all_io':all_io,'time':mtime}
|
||||
return netInfo
|
||||
|
||||
|
||||
def getDiskIostat(self):
|
||||
iokey = 'iostat'
|
||||
|
||||
diskInfo = {}
|
||||
diskInfo['ALL'] = {}
|
||||
diskInfo['ALL']['read_count'] = 0
|
||||
diskInfo['ALL']['write_count'] = 0
|
||||
diskInfo['ALL']['read_bytes'] = 0
|
||||
diskInfo['ALL']['write_bytes'] = 0
|
||||
diskInfo['ALL']['read_time'] = 0
|
||||
diskInfo['ALL']['write_time'] = 0
|
||||
diskInfo['ALL']['read_merged_count'] = 0
|
||||
diskInfo['ALL']['write_merged_count'] = 0
|
||||
|
||||
try:
|
||||
diskio = None
|
||||
if iokey in self.cache:
|
||||
diskio = self.cache[iokey]
|
||||
|
||||
mtime = int(time.time())
|
||||
if not diskio:
|
||||
diskio = {}
|
||||
diskio['info'] = None
|
||||
diskio['time'] = mtime
|
||||
|
||||
diskio_cache = diskio['info']
|
||||
stime = mtime - diskio['time']
|
||||
if not stime: stime = 1
|
||||
|
||||
diskio_group = psutil.disk_io_counters(perdisk=True)
|
||||
if not diskio_cache:
|
||||
diskio_cache = diskio_group
|
||||
|
||||
for disk_name in diskio_group.keys():
|
||||
diskInfo[disk_name] = {}
|
||||
# print('disk_name',disk_name)
|
||||
# print(diskio_group[disk_name].write_time , diskio_cache[disk_name].write_time)
|
||||
# print(diskio_group[disk_name].write_count , diskio_cache[disk_name].write_count)
|
||||
|
||||
diskInfo[disk_name]['read_count'] = int((diskio_group[disk_name].read_count - diskio_cache[disk_name].read_count) / stime)
|
||||
diskInfo[disk_name]['write_count'] = int((diskio_group[disk_name].write_count - diskio_cache[disk_name].write_count) / stime)
|
||||
diskInfo[disk_name]['read_bytes'] = int((diskio_group[disk_name].read_bytes - diskio_cache[disk_name].read_bytes) / stime)
|
||||
diskInfo[disk_name]['write_bytes'] = int((diskio_group[disk_name].write_bytes - diskio_cache[disk_name].write_bytes) / stime)
|
||||
diskInfo[disk_name]['read_time'] = int((diskio_group[disk_name].read_time - diskio_cache[disk_name].read_time) / stime)
|
||||
diskInfo[disk_name]['write_time'] = int((diskio_group[disk_name].write_time - diskio_cache[disk_name].write_time) / stime)
|
||||
|
||||
if 'read_merged_count' in diskio_group[disk_name] and 'read_merged_count' in diskio_cache[disk_name]:
|
||||
diskInfo[disk_name]['read_merged_count'] = int((diskio_group[disk_name].read_merged_count - diskio_cache[disk_name].read_merged_count) / stime)
|
||||
if 'write_merged_count' in diskio_group[disk_name] and 'write_merged_count' in diskio_cache[disk_name]:
|
||||
diskInfo[disk_name]['write_merged_count'] = int((diskio_group[disk_name].write_merged_count - diskio_cache[disk_name].write_merged_count) / stime)
|
||||
|
||||
diskInfo['ALL']['read_count'] += diskInfo[disk_name]['read_count']
|
||||
diskInfo['ALL']['write_count'] += diskInfo[disk_name]['write_count']
|
||||
diskInfo['ALL']['read_bytes'] += diskInfo[disk_name]['read_bytes']
|
||||
diskInfo['ALL']['write_bytes'] += diskInfo[disk_name]['write_bytes']
|
||||
if diskInfo['ALL']['read_time'] < diskInfo[disk_name]['read_time']:
|
||||
diskInfo['ALL']['read_time'] = diskInfo[disk_name]['read_time']
|
||||
if diskInfo['ALL']['write_time'] < diskInfo[disk_name]['write_time']:
|
||||
diskInfo['ALL']['write_time'] = diskInfo[disk_name]['write_time']
|
||||
|
||||
if 'read_merged_count' in diskInfo[disk_name] and 'read_merged_count' in diskInfo[disk_name]:
|
||||
diskInfo['ALL']['read_merged_count'] += diskInfo[disk_name]['read_merged_count']
|
||||
if 'write_merged_count' in diskInfo[disk_name] and 'write_merged_count' in diskInfo[disk_name]:
|
||||
diskInfo['ALL']['write_merged_count'] += diskInfo[disk_name]['write_merged_count']
|
||||
|
||||
self.cache[iokey] = {'info':diskio_group,'time':mtime}
|
||||
except Exception as e:
|
||||
# print(mw.getTracebackInfo())
|
||||
pass
|
||||
|
||||
return diskInfo
|
||||
|
||||
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')
|
||||
|
||||
update_env = '''
|
||||
#!/bin/bash
|
||||
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
|
||||
|
||||
P_VER=`python3 -V | awk '{print $2}'`
|
||||
|
||||
if [ ! -f /www/server/mdserver-web/bin/activate ];then
|
||||
cd /www/server/mdserver-web && python3 -m venv .
|
||||
cd /www/server/mdserver-web && source /www/server/mdserver-web/bin/activate
|
||||
else
|
||||
cd /www/server/mdserver-web && source /www/server/mdserver-web/bin/activate
|
||||
fi
|
||||
|
||||
cn=$(curl -fsSL -m 10 http://ipinfo.io/json | grep "\"country\": \"CN\"")
|
||||
PIPSRC="https://pypi.python.org/simple"
|
||||
if [ ! -z "$cn" ];then
|
||||
PIPSRC="https://pypi.tuna.tsinghua.edu.cn/simple"
|
||||
fi
|
||||
|
||||
cd /www/server/mdserver-web && pip3 install -r /www/server/mdserver-web/requirements.txt -i $PIPSRC
|
||||
|
||||
P_VER_D=`echo "$P_VER"|awk -F '.' '{print $1}'`
|
||||
P_VER_M=`echo "$P_VER"|awk -F '.' '{print $2}'`
|
||||
NEW_P_VER=${P_VER_D}.${P_VER_M}
|
||||
|
||||
if [ -f /www/server/mdserver-web/version/r${NEW_P_VER}.txt ];then
|
||||
cd /www/server/mdserver-web && pip3 install -r /www/server/mdserver-web/version/r${NEW_P_VER}.txt -i $PIPSRC
|
||||
fi
|
||||
'''
|
||||
os.system(update_env)
|
||||
self.restartMw()
|
||||
return mw.returnJson(True, '安装更新成功!')
|
||||
|
||||
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,48 +0,0 @@
|
||||
# coding:utf-8
|
||||
|
||||
# ---------------------------------------------------------------------------------
|
||||
# MW-Linux面板
|
||||
# ---------------------------------------------------------------------------------
|
||||
# copyright (c) 2018-∞(https://github.com/midoks/mdserver-web) All rights reserved.
|
||||
# ---------------------------------------------------------------------------------
|
||||
# Author: midoks <midoks@163.com>
|
||||
# ---------------------------------------------------------------------------------
|
||||
|
||||
# ---------------------------------------------------------------------------------
|
||||
# VIP用户管理
|
||||
# ---------------------------------------------------------------------------------
|
||||
|
||||
import mw
|
||||
|
||||
from flask import request
|
||||
import requests
|
||||
|
||||
|
||||
class vip_api:
|
||||
|
||||
api_url = 'https://wo.midoks.icu/api/wp-json/vip'
|
||||
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
def loginApi(self):
|
||||
username = request.form.get('username', '')
|
||||
password = request.form.get('password', '')
|
||||
password = mw.aesEncrypt(password)
|
||||
|
||||
headers = {
|
||||
"Content-Type": "application/x-www-form-urlencoded",
|
||||
}
|
||||
|
||||
print("name:", str(username))
|
||||
print("pwd:", str(password))
|
||||
args = {
|
||||
'name': username,
|
||||
'pass': password
|
||||
}
|
||||
data = requests.post(self.api_url + '/v1/login',
|
||||
data=args, headers=headers)
|
||||
|
||||
print(data.text)
|
||||
|
||||
return mw.returnJson(False, "测试中!")
|
||||
@@ -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,43 +0,0 @@
|
||||
# coding: utf-8
|
||||
|
||||
import smtplib
|
||||
|
||||
from email import encoders
|
||||
from email.header import Header
|
||||
from email.mime.text import MIMEText
|
||||
from email.utils import parseaddr, formataddr
|
||||
|
||||
|
||||
def _format_addr(s):
|
||||
name, addr = parseaddr(s)
|
||||
return formataddr((Header(name, 'utf-8').encode(), addr))
|
||||
|
||||
|
||||
def send(smtp_host, smtp_port, username, password, to_mail, subject, content):
|
||||
|
||||
smtp = smtplib.SMTP()
|
||||
smtp.connect(smtp_host, port=smtp_port)
|
||||
smtp.login(user=username, password=password)
|
||||
|
||||
msg = MIMEText(content, 'plain', 'utf-8')
|
||||
msg['From'] = _format_addr(username)
|
||||
msg['To'] = _format_addr(to_mail)
|
||||
msg['Subject'] = Header(subject, 'utf-8').encode()
|
||||
|
||||
smtp.sendmail(from_addr=username, to_addrs=to_mail, msg=msg.as_string())
|
||||
return True
|
||||
|
||||
|
||||
def sendSSL(smtp_host, smtp_port, username, password, to_mail, subject, content):
|
||||
|
||||
smtp = smtplib.SMTP_SSL(smtp_host, port=smtp_port)
|
||||
smtp.login(user=username, password=password)
|
||||
|
||||
msg = MIMEText(content, 'plain', 'utf-8')
|
||||
msg['From'] = _format_addr(username)
|
||||
msg['To'] = _format_addr(to_mail)
|
||||
msg['Subject'] = Header(subject, 'utf-8').encode()
|
||||
|
||||
smtp.sendmail(from_addr=username, to_addrs=to_mail, msg=msg.as_string())
|
||||
smtp.quit()
|
||||
return True
|
||||
@@ -1,140 +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_TIMEOUT=1
|
||||
__DB_SOCKET = '/www/server/mysql/mysql.sock'
|
||||
|
||||
__DB_CHARSET = "utf8"
|
||||
|
||||
def __Conn(self):
|
||||
# print(self.__DB_HOST, self.__DB_USER)
|
||||
'''连接数据库'''
|
||||
try:
|
||||
|
||||
if self.__DB_HOST != 'localhost':
|
||||
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=self.__DB_TIMEOUT,
|
||||
cursorclass=pymysql.cursors.DictCursor)
|
||||
except Exception as e:
|
||||
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=self.__DB_TIMEOUT,
|
||||
cursorclass=pymysql.cursors.DictCursor)
|
||||
elif 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=self.__DB_TIMEOUT,
|
||||
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=self.__DB_TIMEOUT,
|
||||
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=self.__DB_TIMEOUT,
|
||||
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=self.__DB_TIMEOUT,
|
||||
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 setHost(self, host):
|
||||
self.__DB_HOST = host
|
||||
|
||||
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 setTimeout(self, timeout = 1):
|
||||
self.__DB_TIMEOUT = timeout
|
||||
return True
|
||||
|
||||
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 ping(self):
|
||||
try:
|
||||
self.__DB_CONN.ping()
|
||||
except Exception as e:
|
||||
print(e)
|
||||
return True
|
||||
|
||||
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
clear.sh
1
clear.sh
@@ -3,5 +3,6 @@ PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin:/usr/loc
|
||||
|
||||
|
||||
|
||||
find . -name .DS_Store -print0 | xargs -0 git rm -f --ignore-unmatch
|
||||
find . -name .DS_Store | xargs rm -rf
|
||||
find . -type d -name "*.pyc" | xargs rm -rf
|
||||
87
cli.sh
87
cli.sh
@@ -3,31 +3,31 @@ PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin:/opt/hom
|
||||
DIR=$(cd "$(dirname "$0")"; pwd)
|
||||
MDIR=$(dirname "$DIR")
|
||||
|
||||
export LC_ALL="en_US.UTF-8"
|
||||
|
||||
# echo $DIR
|
||||
|
||||
PATH=$PATH:$DIR/bin
|
||||
if [ -f bin/activate ];then
|
||||
source bin/activate
|
||||
if [ -f ${DIR}/bin/activate ];then
|
||||
source ${DIR}/bin/activate
|
||||
|
||||
if [ "$?" != "0" ];then
|
||||
echo "load local python env fail!"
|
||||
fi
|
||||
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
|
||||
isStart=$(ps aux |grep 'panel_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 &
|
||||
cd $DIR && python3 panel_task.py >> ${DIR}/logs/panel_task.log 2>&1 &
|
||||
sleep 0.3
|
||||
isStart=$(ps aux |grep 'task.py'|grep -v grep|awk '{print $2}')
|
||||
if [ "$isStart" == '' ];then
|
||||
isStart=$(ps aux |grep 'panel_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
|
||||
tail -n 20 $DIR/logs/panel_task.log
|
||||
echo '------------------------------------------------------'
|
||||
echo -e "\033[31mError: mw-tasks service startup failed.\033[0m"
|
||||
return;
|
||||
@@ -39,46 +39,69 @@ mw_start_task()
|
||||
}
|
||||
|
||||
mw_start(){
|
||||
gunicorn -c setting.py app:app
|
||||
#安全启动
|
||||
# 后台任务
|
||||
mw_start_task
|
||||
|
||||
cd ${DIR}/web && gunicorn -c setting.py app:app
|
||||
}
|
||||
|
||||
|
||||
mw_start_debug(){
|
||||
if [ ! -f $DIR/logs/task.log ];then
|
||||
echo '' > $DIR/logs/task.log
|
||||
if [ ! -f $DIR/logs/panel_task.log ];then
|
||||
echo '' > $DIR/logs/panel_task.log
|
||||
fi
|
||||
|
||||
python3 task.py >> $DIR/logs/task.log 2>&1 &
|
||||
port=7200
|
||||
python3 panel_task.py >> $DIR/logs/panel_task.log 2>&1 &
|
||||
port=7200
|
||||
if [ -f /www/server/mdserver-web/data/port.pl ];then
|
||||
port=$(cat /www/server/mdserver-web/data/port.pl)
|
||||
fi
|
||||
# gunicorn -b :${port} -k gevent -w 1 app:app
|
||||
gunicorn -b :${port} -k geventwebsocket.gunicorn.workers.GeventWebSocketWorker -w 1 app:app
|
||||
|
||||
if [ -f ${DIR}/data/port.pl ];then
|
||||
port=$(cat ${DIR}/data/port.pl)
|
||||
fi
|
||||
cd ${DIR}/web && gunicorn -b :${port} -k gthread -w 1 app:app
|
||||
# cd ${DIR}/web && gunicorn -b :${port} -k geventwebsocket.gunicorn.workers.GeventWebSocketWorker -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_start_panel(){
|
||||
port=7200
|
||||
if [ -f ${DIR}/data/port.pl ];then
|
||||
port=$(cat ${DIR}/data/port.pl)
|
||||
fi
|
||||
cd ${DIR}/web && gunicorn -b :${port} -k gthread -w 1 app:app
|
||||
# cd ${DIR}/web && gunicorn -b :${port} -k geventwebsocket.gunicorn.workers.GeventWebSocketWorker -w 1 app:app
|
||||
|
||||
}
|
||||
|
||||
mw_start_bgtask(){
|
||||
cd ${DIR}/web && gunicorn -c setting.py app:app
|
||||
cd ${DIR} && python3 panel_task.py
|
||||
}
|
||||
|
||||
|
||||
mw_stop()
|
||||
{
|
||||
PLIST=`ps -ef|grep app:app |grep -v grep|awk '{print $2}'`
|
||||
for i in $PLIST
|
||||
APP_LIST=`ps -ef|grep app:app |grep -v grep|awk '{print $2}'`
|
||||
APP_LIST=($APP_LIST)
|
||||
for p in ${APP_LIST[@]}
|
||||
do
|
||||
kill -9 $i > /dev/null 2>&1
|
||||
kill -9 $p > /dev/null 2>&1
|
||||
done
|
||||
|
||||
pids=`ps -ef|grep task.py | grep -v grep |awk '{print $2}'`
|
||||
arr=($pids)
|
||||
for p in ${arr[@]}
|
||||
TASK_LIST=`ps -ef|grep panel_task.py | grep -v grep |awk '{print $2}'`
|
||||
TASK_LIST=($TASK_LIST)
|
||||
for p in ${TASK_LIST[@]}
|
||||
do
|
||||
kill -9 $p > /dev/null 2>&1
|
||||
done
|
||||
|
||||
zzpids=`ps -A -o stat,ppid,pid | grep -e '^[Zz]' | awk '{print $2}'`
|
||||
zzpids=($zzpids)
|
||||
for p in ${zzpids[@]}
|
||||
do
|
||||
kill -9 ${p} > /dev/null 2>&1
|
||||
done
|
||||
}
|
||||
|
||||
case "$1" in
|
||||
@@ -92,8 +115,12 @@ case "$1" in
|
||||
mw_stop
|
||||
mw_start_debug
|
||||
;;
|
||||
'debug2')
|
||||
'panel')
|
||||
mw_stop
|
||||
mw_start_debug2
|
||||
mw_start_panel
|
||||
;;
|
||||
'task')
|
||||
# mw_stop
|
||||
mw_start_bgtask
|
||||
;;
|
||||
esac
|
||||
15
cmd.md
15
cmd.md
@@ -4,9 +4,16 @@
|
||||
- 面板相关命令
|
||||
|
||||
```
|
||||
/etc/init.d/mw default | 显示登录信息
|
||||
/etc/init.d/mw db | 快捷连接MySQL
|
||||
/etc/init.d/mw redis | 快捷连接Redis
|
||||
mw start | 启动面板服务器
|
||||
mw stop | 停止面板服务器
|
||||
mw restart | 重启面板服务器
|
||||
mw default | 显示登录信息
|
||||
mw db | 快捷连接MySQL
|
||||
mw redis | 快捷连接Redis
|
||||
mw valkey | 快捷连接valkey
|
||||
mw mongodb | 快捷连接MongoDB
|
||||
mw pgdb | 快捷连接PostgreSQL
|
||||
mw ssh | 快捷连接ssh
|
||||
----------------------------------------
|
||||
mw open | 开启面板
|
||||
mw close | 关闭面板
|
||||
@@ -16,7 +23,7 @@ mw venv | 进入虚拟环境
|
||||
mw mirror | 切换镜像
|
||||
mw install_app | 快捷安装常用软件
|
||||
mw update | 更新到正式
|
||||
mw update_dev | 更新到开发
|
||||
mw dev/update_dev | 更新到开发
|
||||
|
||||
service mw [start|stop|reload|restart|status]
|
||||
```
|
||||
|
||||
@@ -29,24 +29,24 @@ cd /www/server/mdserver-web/plugins/php/versions && bash all_test.sh
|
||||
|
||||
|
||||
|
||||
| 系统名称 |PHP53 |PHP54 |PHP55 |PHP56 |PHP70 |PHP71 |PHP72 |PHP73 |PHP74 |PHP80 |PHP81 |PHP82 |PHP83 |
|
||||
| ----------------- |-------|-------|-------|-------|-------|-------|-------|-------|-------|-------|-------|-------|-------|
|
||||
| CentOS 7.9 |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |
|
||||
| CentOS 8.4 |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |
|
||||
| CentOS 8 Stream |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |
|
||||
| CentOS 9 Stream |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |
|
||||
| Debian 10.3 |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |
|
||||
| Debian 11.3 |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |
|
||||
| Debian 12 |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |
|
||||
| Ubuntu 18.04 |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |
|
||||
| Ubuntu 20.04 |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |
|
||||
| Ubuntu 22.04 |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |
|
||||
| Fedora 31 |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |
|
||||
| Fedora 32 |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |
|
||||
| AlmaLinux 9 |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |
|
||||
| RockyLinux 8.6 |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |
|
||||
| Arch Linux |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |
|
||||
| openSUSE 15.4 |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |
|
||||
| 系统名称 |PHP53 |PHP54 |PHP55 |PHP56 |PHP70 |PHP71 |PHP72 |PHP73 |PHP74 |PHP80 |PHP81 |PHP82 |PHP83 |PHP84 |
|
||||
| ----------------- |-------|-------|-------|-------|-------|-------|-------|-------|-------|-------|-------|-------|-------|-------|
|
||||
| CentOS 7.9 |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |
|
||||
| CentOS 8.4 |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |
|
||||
| CentOS 8 Stream |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |
|
||||
| CentOS 9 Stream |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |
|
||||
| Debian 10.3 |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |
|
||||
| Debian 11.3 |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |
|
||||
| Debian 12 |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |
|
||||
| Ubuntu 18.04 |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |
|
||||
| Ubuntu 20.04 |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |
|
||||
| Ubuntu 22.04 |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |
|
||||
| Fedora 31 |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |
|
||||
| Fedora 32 |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |
|
||||
| AlmaLinux 9 |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |
|
||||
| RockyLinux 8.6 |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |
|
||||
| Arch Linux |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |
|
||||
| openSUSE 15.4 |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |
|
||||
|
||||
|
||||
|
||||
|
||||
4
data/README.md
Normal file
4
data/README.md
Normal file
@@ -0,0 +1,4 @@
|
||||
# 自动生成
|
||||
|
||||
certificate.pem - 证书信息
|
||||
privateKey.pem - 私钥
|
||||
@@ -1 +0,0 @@
|
||||
30
|
||||
@@ -1,42 +0,0 @@
|
||||
[
|
||||
{
|
||||
"title":"全部",
|
||||
"type":0,
|
||||
"ps":""
|
||||
},
|
||||
{
|
||||
"title":"已安装",
|
||||
"type":-1,
|
||||
"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,135 +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,
|
||||
`protocol` TEXT DEFAULT 'tcp',
|
||||
`ps` TEXT,
|
||||
`addtime` TEXT
|
||||
);
|
||||
|
||||
ALTER TABLE `firewall` ADD COLUMN `protocol` TEXT DEFAULT 'tcp';
|
||||
|
||||
INSERT INTO `firewall` (`id`, `port`, `protocol`, `ps`, `addtime`) VALUES
|
||||
(1, '80', 'tcp','网站默认端口', '0000-00-00 00:00:00'),
|
||||
(2, '443', 'tcp/udp', 'HTTPS', '0000-00-00 00:00:00');
|
||||
|
||||
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `logs` (
|
||||
`id` INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
`type` TEXT,
|
||||
`log` TEXT,
|
||||
`uid` INTEGER DEFAULT '1',
|
||||
`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,52 +0,0 @@
|
||||
server
|
||||
{
|
||||
# reuseport 只能在一个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|js|css)$
|
||||
{
|
||||
expires 30d;
|
||||
error_log /dev/null;
|
||||
access_log /dev/null;
|
||||
}
|
||||
|
||||
access_log {$LOGPATH}/{$SERVER_NAME}.log main;
|
||||
error_log {$LOGPATH}/{$SERVER_NAME}.error.log;
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
存放VIP关键数据
|
||||
@@ -40,7 +40,7 @@ RUN apt update -y && \
|
||||
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 && \
|
||||
bash install.sh install 1.21.4 && \
|
||||
cd /www/server/mdserver-web/plugins/mysql && \
|
||||
bash install.sh install 5.6 && \
|
||||
cd /www/server/mdserver-web/plugins/phpmyadmin && \
|
||||
|
||||
1
logs/README.md
Normal file
1
logs/README.md
Normal file
@@ -0,0 +1 @@
|
||||
logs
|
||||
362
panel_task.py
Executable file
362
panel_task.py
Executable file
@@ -0,0 +1,362 @@
|
||||
# coding: utf-8
|
||||
|
||||
# ---------------------------------------------------------------------------------
|
||||
# MW-Linux面板
|
||||
# ---------------------------------------------------------------------------------
|
||||
# copyright (c) 2018-∞(https://github.com/midoks/mdserver-web) All rights reserved.
|
||||
# ---------------------------------------------------------------------------------
|
||||
# Author: midoks <midoks@163.com>
|
||||
# ---------------------------------------------------------------------------------
|
||||
|
||||
# ---------------------------------------------------------------------------------
|
||||
# 计划任务
|
||||
# ---------------------------------------------------------------------------------
|
||||
|
||||
import sys
|
||||
import os
|
||||
import json
|
||||
import time
|
||||
import threading
|
||||
import psutil
|
||||
|
||||
|
||||
web_dir = os.getcwd() + "/web"
|
||||
os.chdir(web_dir)
|
||||
sys.path.append(web_dir)
|
||||
|
||||
import core.mw as mw
|
||||
import thisdb
|
||||
|
||||
from admin import setup
|
||||
setup.init()
|
||||
|
||||
g_log_file = mw.getPanelTaskExecLog()
|
||||
if not os.path.exists(g_log_file):
|
||||
os.system("touch " + g_log_file)
|
||||
|
||||
def execShell(cmdstring, cwd=None, timeout=None, shell=True):
|
||||
cmd = cmdstring + ' > ' + g_log_file + ' 2>&1'
|
||||
return mw.execShell(cmd)
|
||||
|
||||
def writeLogs(data):
|
||||
# 写输出日志
|
||||
try:
|
||||
fp = open(g_log_file, 'w+')
|
||||
fp.write(data)
|
||||
fp.close()
|
||||
except:
|
||||
pass
|
||||
|
||||
def downloadFile(url, filename):
|
||||
# 下载文件
|
||||
try:
|
||||
import urllib
|
||||
import socket
|
||||
socket.setdefaulttimeout(300)
|
||||
|
||||
headers = ('User-Agent', 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36')
|
||||
opener = urllib.request.build_opener()
|
||||
opener.addheaders = [headers]
|
||||
urllib.request.install_opener(opener)
|
||||
|
||||
urllib.request.urlretrieve(url, filename=filename, reporthook=downloadHook)
|
||||
|
||||
if not mw.isAppleSystem():
|
||||
os.system('chown www.www ' + filename)
|
||||
|
||||
writeLogs(filename + ' download success!')
|
||||
except Exception as e:
|
||||
writeLogs(str(e))
|
||||
return True
|
||||
|
||||
def downloadHook(count, blockSize, totalSize):
|
||||
# 下载文件进度回调
|
||||
used = count * blockSize
|
||||
pre = int((100.0 * used / totalSize))
|
||||
speed = {'total': totalSize, 'used': used, 'pre': pre}
|
||||
writeLogs(json.dumps(speed))
|
||||
|
||||
def runPanelTask():
|
||||
# 站点过期检查
|
||||
siteEdateCheck()
|
||||
|
||||
# echo "True" > /www/server/mdserver-web/logs/panel_task.lock
|
||||
lock_file = mw.getTriggerTaskLockFile()
|
||||
try:
|
||||
if os.path.exists(lock_file):
|
||||
bash_list = thisdb.getTaskList(status=-1)
|
||||
for task in bash_list:
|
||||
thisdb.setTaskStatus(task['id'], 0)
|
||||
|
||||
run_list = thisdb.getTaskList(status=0)
|
||||
for run_task in run_list:
|
||||
start = int(time.time())
|
||||
thisdb.setTaskData(run_task['id'], start=start)
|
||||
thisdb.setTaskStatus(run_task['id'], -1)
|
||||
|
||||
if run_task['type'] == 'download':
|
||||
argv = run_task['cmd'].split('|mw|')
|
||||
downloadFile(argv[0], argv[1])
|
||||
elif run_task['type'] == 'execshell':
|
||||
execShell(run_task['cmd'])
|
||||
|
||||
end = int(time.time())
|
||||
thisdb.setTaskData(run_task['id'], end=end)
|
||||
thisdb.setTaskStatus(run_task['id'], 1)
|
||||
|
||||
if thisdb.getTaskUnexecutedCount() < 1:
|
||||
os.remove(lock_file)
|
||||
except Exception as e:
|
||||
print('runPanelTask:',mw.getTracebackInfo())
|
||||
|
||||
# 网站到期处理
|
||||
def siteEdateCheck():
|
||||
try:
|
||||
from utils.site import sites as MwSites
|
||||
website_edate = thisdb.getOption('website_edate', default='0000-00-00')
|
||||
now_time_ymd = time.strftime('%Y-%m-%d', time.localtime())
|
||||
|
||||
if website_edate == now_time_ymd:
|
||||
return False
|
||||
site_list = thisdb.getSitesEdateList(now_time_ymd)
|
||||
for site in site_list:
|
||||
MwSites.instance().stop(site['id'])
|
||||
thisdb.setOption('website_edate', now_time_ymd)
|
||||
except Exception as e:
|
||||
print('siteEdateCheck:',mw.getTracebackInfo())
|
||||
|
||||
# 任务队列
|
||||
def startPanelTask():
|
||||
try:
|
||||
while True:
|
||||
runPanelTask()
|
||||
time.sleep(5)
|
||||
except Exception as e:
|
||||
print('startPanelTask:',mw.getTracebackInfo())
|
||||
time.sleep(30)
|
||||
startPanelTask()
|
||||
|
||||
def systemTask():
|
||||
# 系统监控任务
|
||||
from utils.system import monitor
|
||||
try:
|
||||
while True:
|
||||
monitor_status = thisdb.getOption('monitor_status',type='monitor',default='open')
|
||||
if monitor_status == 'open':
|
||||
monitor.instance().run()
|
||||
time.sleep(5)
|
||||
except Exception as ex:
|
||||
print('systemTask:',mw.getTracebackInfo())
|
||||
time.sleep(30)
|
||||
systemTask()
|
||||
|
||||
|
||||
def panelPluginStatusCheck():
|
||||
# 系统监控任务
|
||||
from utils.plugin import plugin
|
||||
try:
|
||||
while True:
|
||||
# start_t = time.time()
|
||||
plugin.instance().autoCachePluginStatus()
|
||||
# end_t = time.time()
|
||||
time.sleep(60)
|
||||
except Exception as ex:
|
||||
print('panelPluginStatusCheck:',mw.getTracebackInfo())
|
||||
time.sleep(120)
|
||||
panelPluginStatusCheck()
|
||||
|
||||
# -------------------------------------- PHP监控 start --------------------------------------------- #
|
||||
# 502错误检查线程
|
||||
def check502Task():
|
||||
try:
|
||||
check_file = mw.getPanelDir() + '/data/502Task.pl'
|
||||
while True:
|
||||
if os.path.exists(check_file):
|
||||
check502()
|
||||
time.sleep(10)
|
||||
except:
|
||||
time.sleep(30)
|
||||
check502Task()
|
||||
|
||||
|
||||
def check502():
|
||||
try:
|
||||
verlist = [
|
||||
'52', '53', '54', '55', '56', '70',
|
||||
'71', '72', '73', '74', '80', '81',
|
||||
'82', '83', '84', '85'
|
||||
]
|
||||
for ver in verlist:
|
||||
server_dir = mw.getServerDir()
|
||||
php_path = server_dir + '/php/' + ver + '/sbin/php-fpm'
|
||||
if not os.path.exists(php_path):
|
||||
continue
|
||||
if checkPHPVersion(ver):
|
||||
continue
|
||||
if startPHPVersion(ver):
|
||||
print('检测到PHP-' + ver + '处理异常,已自动修复!')
|
||||
mw.writeLog('PHP守护程序', '检测到PHP-' + ver + '处理异常,已自动修复!')
|
||||
|
||||
except Exception as e:
|
||||
mw.writeLog('PHP守护程序', '自动修复异常:'+str(e))
|
||||
|
||||
|
||||
# 处理指定PHP版本
|
||||
def startPHPVersion(version):
|
||||
server_dir = mw.getServerDir()
|
||||
try:
|
||||
# system
|
||||
phpService = mw.systemdCfgDir() + '/php' + version + '.service'
|
||||
if os.path.exists(phpService):
|
||||
mw.execShell("systemctl restart php" + version)
|
||||
if checkPHPVersion(version):
|
||||
return True
|
||||
|
||||
# initd
|
||||
fpm = server_dir + '/php/init.d/php' + version
|
||||
php_path = server_dir + '/php/' + version + '/sbin/php-fpm'
|
||||
if not os.path.exists(php_path):
|
||||
if os.path.exists(fpm):
|
||||
os.remove(fpm)
|
||||
return False
|
||||
|
||||
if not os.path.exists(fpm):
|
||||
return False
|
||||
|
||||
# 尝试重载服务
|
||||
os.system(fpm + ' reload')
|
||||
if checkPHPVersion(version):
|
||||
return True
|
||||
# 尝试重启服务
|
||||
cgi = '/tmp/php-cgi-' + version + '.sock'
|
||||
pid = server_dir + '/php/' + version + '/var/run/php-fpm.pid'
|
||||
data = mw.execShell("ps -ef | grep php/" + version +" | grep -v grep|grep -v python |awk '{print $2}'")
|
||||
if data[0] != '':
|
||||
os.system("ps -ef | grep php/" + version + " | grep -v grep|grep -v python |awk '{print $2}' | xargs kill ")
|
||||
time.sleep(0.5)
|
||||
if not os.path.exists(cgi):
|
||||
os.system('rm -f ' + cgi)
|
||||
if not os.path.exists(pid):
|
||||
os.system('rm -f ' + pid)
|
||||
os.system(fpm + ' start')
|
||||
if checkPHPVersion(version):
|
||||
return True
|
||||
|
||||
# 检查是否正确启动
|
||||
if os.path.exists(cgi):
|
||||
return True
|
||||
except Exception as e:
|
||||
print('startPHPVersion:',mw.getTracebackInfo())
|
||||
mw.writeLog('PHP守护程序', '自动修复异常:'+str(e))
|
||||
return True
|
||||
|
||||
|
||||
def checkPHPVersion(version):
|
||||
# 检查指定PHP版本
|
||||
try:
|
||||
sock = mw.getFpmAddress(version)
|
||||
data = mw.requestFcgiPHP(sock, '/phpfpm_status_' + version + '?json')
|
||||
result = str(data, encoding='utf-8')
|
||||
except Exception as e:
|
||||
result = 'Bad Gateway'
|
||||
# 检查openresty
|
||||
if result.find('Bad Gateway') != -1:
|
||||
return False
|
||||
if result.find('HTTP Error 404: Not Found') != -1:
|
||||
return False
|
||||
|
||||
# 检查Web服务是否启动
|
||||
if result.find('Connection refused') != -1:
|
||||
return False
|
||||
return True
|
||||
|
||||
# -------------------------------------- PHP监控 end --------------------------------------------- #
|
||||
|
||||
|
||||
# --------------------------------------OpenResty Auto Restart Start --------------------------------------------- #
|
||||
# 解决acme.sh续签后,未起效。
|
||||
def openrestyAutoRestart():
|
||||
try:
|
||||
while True:
|
||||
# 检查是否安装
|
||||
odir = mw.getServerDir() + '/openresty'
|
||||
if not os.path.exists(odir):
|
||||
time.sleep(86400)
|
||||
continue
|
||||
mw.opWeb('reload')
|
||||
time.sleep(86400)
|
||||
except Exception as e:
|
||||
mw.writeLog('OpenResty检测', '自动修复异常:'+str(e))
|
||||
time.sleep(86400)
|
||||
|
||||
# --------------------------------------OpenResty Auto Restart End --------------------------------------------- #
|
||||
|
||||
# ------------------------------------ OpenResty Restart At Once Start ------------------------------------------ #
|
||||
|
||||
|
||||
def openrestyRestartAtOnce():
|
||||
restart_nginx_tip = mw.getPanelDir()+'/data/restart_nginx.pl'
|
||||
while True:
|
||||
if os.path.exists(restart_nginx_tip):
|
||||
os.remove(restart_nginx_tip)
|
||||
mw.opWeb('reload')
|
||||
time.sleep(1)
|
||||
# ----------------------------------- OpenResty Restart At Once End ------------------------------------------ #
|
||||
|
||||
|
||||
# --------------------------------------Panel Restart Start --------------------------------------------- #
|
||||
def restartPanelService():
|
||||
restart_tip = mw.getPanelDir()+'/data/restart.pl'
|
||||
while True:
|
||||
if os.path.exists(restart_tip):
|
||||
print("restart panel")
|
||||
os.remove(restart_tip)
|
||||
mw.panelCmd('restart_panel')
|
||||
time.sleep(1)
|
||||
# --------------------------------------Panel Restart End --------------------------------------------- #
|
||||
|
||||
|
||||
def setDaemon(t):
|
||||
if sys.version_info.major == 3 and sys.version_info.minor >= 10:
|
||||
t.daemon = True
|
||||
else:
|
||||
t.setDaemon(True)
|
||||
return t
|
||||
|
||||
def run():
|
||||
# 系统监控
|
||||
sysTask = threading.Thread(target=systemTask)
|
||||
sysTask = setDaemon(sysTask)
|
||||
sysTask.start()
|
||||
|
||||
# PHP 502错误检查线程
|
||||
php502 = threading.Thread(target=check502Task)
|
||||
php502 = setDaemon(php502)
|
||||
php502.start()
|
||||
|
||||
# OpenResty Restart At Once Start
|
||||
oraos = threading.Thread(target=openrestyRestartAtOnce)
|
||||
oraos = setDaemon(oraos)
|
||||
oraos.start()
|
||||
|
||||
# OpenResty Auto Restart Start
|
||||
oar = threading.Thread(target=openrestyAutoRestart)
|
||||
oar = setDaemon(oar)
|
||||
oar.start()
|
||||
|
||||
# Panel Plugin Status Check
|
||||
pps = threading.Thread(target=panelPluginStatusCheck)
|
||||
pps = setDaemon(pps)
|
||||
pps.start()
|
||||
|
||||
# Panel Restart Start
|
||||
rps = threading.Thread(target=restartPanelService)
|
||||
rps = setDaemon(rps)
|
||||
rps.start()
|
||||
|
||||
# 面板后台任务
|
||||
startPanelTask()
|
||||
|
||||
if __name__ == "__main__":
|
||||
run()
|
||||
|
||||
433
panel_tools.py
Executable file
433
panel_tools.py
Executable file
@@ -0,0 +1,433 @@
|
||||
# coding=utf-8
|
||||
|
||||
# ---------------------------------------------------------------------------------
|
||||
# MW-Linux面板
|
||||
# ---------------------------------------------------------------------------------
|
||||
# copyright (c) 2018-∞(https://github.com/midoks/mdserver-web) All rights reserved.
|
||||
# ---------------------------------------------------------------------------------
|
||||
# Author: midoks <midoks@163.com>
|
||||
# ---------------------------------------------------------------------------------
|
||||
|
||||
# ---------------------------------------------------------------------------------
|
||||
# 工具箱
|
||||
# ---------------------------------------------------------------------------------
|
||||
|
||||
|
||||
import sys
|
||||
import os
|
||||
import json
|
||||
import time
|
||||
import re
|
||||
|
||||
web_dir = os.getcwd() + "/web"
|
||||
os.chdir(web_dir)
|
||||
sys.path.append(web_dir)
|
||||
|
||||
from utils.firewall import Firewall as MwFirewall
|
||||
import core.mw as mw
|
||||
import thisdb
|
||||
|
||||
INIT_DIR = "/etc/rc.d/init.d"
|
||||
if mw.isAppleSystem():
|
||||
INIT_DIR = mw.getPanelDir() + "/scripts/init.d"
|
||||
|
||||
INIT_CMD = INIT_DIR + "/mw"
|
||||
|
||||
|
||||
def mw_input_cmd(msg):
|
||||
if sys.version_info[0] == 2:
|
||||
in_val = raw_input(msg)
|
||||
else:
|
||||
in_val = input(msg)
|
||||
return in_val
|
||||
|
||||
def getRemainLen(cmd, max_length=100):
|
||||
cmd_len = len(cmd)
|
||||
cmd_u8_len = len(cmd.encode('utf-8'))
|
||||
return max_length-int((cmd_u8_len - cmd_len)/2+cmd_len)
|
||||
|
||||
def mwcli(mw_input=0):
|
||||
panel_dir = mw.getPanelDir()
|
||||
|
||||
raw_tip = "========================================================================"
|
||||
if not mw_input:
|
||||
print("========================mdserver-web cli tools==========================")
|
||||
cmd_list = [
|
||||
'(1) 重启面板服务',
|
||||
'(2) 停止面板服务',
|
||||
'(3) 启动面板服务',
|
||||
'(4) 重载面板服务',
|
||||
'(5) 修改面板IP',
|
||||
'(6) 修改面板端口',
|
||||
'(7) 关闭安全入口',
|
||||
'(10) 查看面板默认信息',
|
||||
'(11) 修改面板密码',
|
||||
'(12) 修改面板用户名',
|
||||
'(13) 显示面板错误日志',
|
||||
'(14) 关闭面板访问',
|
||||
'(15) 开启面板访问',
|
||||
'(20) 关闭BasicAuth认证',
|
||||
'(21) 解除域名绑定',
|
||||
'(22) 解除面板SSL绑定',
|
||||
'(23) 开启IPV6支持',
|
||||
'(24) 关闭IPV6支持',
|
||||
'(25) 开启防火墙SSH端口',
|
||||
'(26) 关闭二次验证',
|
||||
'(27) 查看防火墙信息',
|
||||
'(28) 自动识别防火墙端口到面板',
|
||||
'(29) 自动识别配置站点信息',
|
||||
'(100) 开启PHP52显示',
|
||||
'(101) 关闭PHP52显示',
|
||||
'(200) 切换Linux系统软件源',
|
||||
'(201) 简单速度测试',
|
||||
'(202) SSH终端管理',
|
||||
'(0) 取消'
|
||||
]
|
||||
cmd_list_num = len(cmd_list)
|
||||
for index in range(cmd_list_num):
|
||||
cmd = cmd_list[index]
|
||||
if index % 2 == 0:
|
||||
if index == (cmd_list_num-1):
|
||||
print(cmd)
|
||||
else:
|
||||
print(cmd + " " * getRemainLen(cmd, 40), end="")
|
||||
if index % 2 == 1:
|
||||
print(cmd)
|
||||
print(raw_tip)
|
||||
try:
|
||||
mw_input = input("请输入命令编号:")
|
||||
if sys.version_info[0] == 3:
|
||||
mw_input = int(mw_input)
|
||||
except:
|
||||
mw_input = 0
|
||||
|
||||
nums = [
|
||||
1, 2, 3, 4, 5, 6, 7,
|
||||
10, 11, 12, 13, 14, 15,
|
||||
20, 21, 22, 23, 24, 25, 26, 27, 28, 29,
|
||||
100, 101,
|
||||
200, 201, 202
|
||||
]
|
||||
if not mw_input in nums:
|
||||
print(raw_tip)
|
||||
print("已取消!")
|
||||
exit()
|
||||
|
||||
if mw_input == 1:
|
||||
os.system(INIT_CMD + " restart")
|
||||
elif mw_input == 2:
|
||||
os.system(INIT_CMD + " stop")
|
||||
elif mw_input == 3:
|
||||
os.system(INIT_CMD + " start")
|
||||
elif mw_input == 4:
|
||||
os.system(INIT_CMD + " reload")
|
||||
elif mw_input == 5:
|
||||
in_ip = mw_input_cmd("请输入设置的面板IP:")
|
||||
in_ip = in_ip.strip()
|
||||
ip_text = panel_dir + '/data/iplist.txt'
|
||||
if not mw.isVaildIp(in_ip):
|
||||
mw.echoInfo("【"+in_ip+"】: IP不合法")
|
||||
return
|
||||
mw.writeFile(ip_text, in_ip)
|
||||
thisdb.setOption('server_ip', in_ip)
|
||||
mw.echoInfo("设置面板IP: " + in_ip)
|
||||
elif mw_input == 6:
|
||||
in_port = mw_input_cmd("请输入新的面板端口:")
|
||||
in_port_int = int(in_port.strip())
|
||||
if in_port_int < 65536 and in_port_int > 0:
|
||||
MwFirewall.instance().addAcceptPort(in_port, 'WEB面板[TOOLS修改]', 'port')
|
||||
panel_port = panel_dir + '/data/port.pl'
|
||||
mw.writeFile(panel_port, in_port)
|
||||
os.system(INIT_CMD + " restart_panel")
|
||||
os.system(INIT_CMD + " default")
|
||||
else:
|
||||
mw.echoInfo("端口范围在0-65536之间")
|
||||
return
|
||||
elif mw_input == 7:
|
||||
thisdb.setOption('admin_path', '')
|
||||
mw.echoInfo("关闭安全入口成功!")
|
||||
elif mw_input == 10:
|
||||
os.system(INIT_CMD + " default")
|
||||
elif mw_input == 11:
|
||||
input_pwd = mw_input_cmd("请输入新的面板密码:")
|
||||
if len(input_pwd.strip()) < 5:
|
||||
mw.echoInfo("错误,密码长度不能小于5位")
|
||||
return
|
||||
set_panel_pwd(input_pwd.strip(), True)
|
||||
elif mw_input == 12:
|
||||
input_user = mw_input_cmd("请输入新的面板用户名(>=5位):")
|
||||
set_panel_username(input_user.strip())
|
||||
elif mw_input == 13:
|
||||
os.system('tail -100 ' + panel_dir + '/logs/panel_error.log')
|
||||
elif mw_input == 14:
|
||||
admin_close = thisdb.getOption('admin_close')
|
||||
if admin_close == 'no':
|
||||
thisdb.setOption('admin_close', 'yes')
|
||||
mw.echoInfo("关闭面板访问成功!")
|
||||
else:
|
||||
mw.echoInfo("已关闭面板访问!")
|
||||
elif mw_input == 15:
|
||||
admin_close = thisdb.getOption('admin_close')
|
||||
if admin_close == 'yes':
|
||||
thisdb.setOption('admin_close', 'no')
|
||||
mw.echoInfo("开启面板访问成功!")
|
||||
else:
|
||||
mw.echoInfo("已开启面板访问!")
|
||||
elif mw_input == 20:
|
||||
basic_auth = thisdb.getOptionByJson('basic_auth', default={'open':False})
|
||||
if basic_auth['open']:
|
||||
basic_auth['open'] = False
|
||||
thisdb.setOption('basic_auth', json.dumps(basic_auth))
|
||||
os.system(INIT_CMD + " restart")
|
||||
mw.echoInfo("关闭basic_auth成功")
|
||||
elif mw_input == 21:
|
||||
panel_domain = thisdb.getOption('panel_domain', default='')
|
||||
if panel_domain != '':
|
||||
thisdb.setOption('panel_domain', '')
|
||||
os.system(INIT_CMD + " unbind_domain")
|
||||
mw.echoInfo("解除域名绑定成功")
|
||||
else:
|
||||
mw.echoInfo("面板未绑定域名!")
|
||||
elif mw_input == 22:
|
||||
panel_ssl = thisdb.getOptionByJson('panel_ssl', default={'open':False})
|
||||
if panel_ssl['open']:
|
||||
panel_ssl['open'] = False
|
||||
thisdb.setOption('panel_ssl', json.dumps(panel_ssl))
|
||||
os.system(INIT_CMD + " unbind_ssl")
|
||||
mw.echoInfo("解除面板SSL绑定成功")
|
||||
elif mw_input == 23:
|
||||
listen_ipv6 = panel_dir + '/data/ipv6.pl'
|
||||
if not os.path.exists(listen_ipv6):
|
||||
mw.writeFile(listen_ipv6, 'True')
|
||||
os.system(INIT_CMD + " restart")
|
||||
mw.echoInfo("开启IPv6支持了")
|
||||
else:
|
||||
mw.echoInfo("已开启IPv6支持!")
|
||||
elif mw_input == 24:
|
||||
listen_ipv6 = panel_dir + '/data/ipv6.pl'
|
||||
if not os.path.exists(listen_ipv6):
|
||||
mw.echoInfo("已关闭IPv6支持!")
|
||||
else:
|
||||
os.remove(listen_ipv6)
|
||||
os.system(INIT_CMD + " restart")
|
||||
mw.echoInfo("关闭IPv6支持了")
|
||||
elif mw_input == 25:
|
||||
open_ssh_port()
|
||||
mw.echoInfo("已开启!")
|
||||
elif mw_input == 26:
|
||||
two_step_verification = thisdb.getOptionByJson('two_step_verification', default={'open':False})
|
||||
if two_step_verification['open']:
|
||||
two_step_verification['open'] = False
|
||||
thisdb.setOption('two_step_verification', json.dumps(two_step_verification))
|
||||
mw.echoInfo("关闭二次验证成功!")
|
||||
else:
|
||||
mw.echoInfo("二次验证已关闭!")
|
||||
elif mw_input == 27:
|
||||
cmd = 'which ufw'
|
||||
run_cmd = False
|
||||
find_cmd = mw.execShell(cmd)
|
||||
if find_cmd[0].strip() != '':
|
||||
run_cmd = True
|
||||
os.system('ufw status')
|
||||
|
||||
cmd = 'which firewall-cmd'
|
||||
find_cmd = mw.execShell(cmd)
|
||||
if find_cmd[0].strip() != '':
|
||||
run_cmd = True
|
||||
os.system('firewall-cmd --list-all')
|
||||
if not run_cmd:
|
||||
mw.echoInfo("未检测到防火墙!")
|
||||
elif mw_input == 28:
|
||||
MwFirewall.instance().aIF()
|
||||
mw.echoInfo("执行自动识别防火墙端口到面板成功!")
|
||||
elif mw_input == 29:
|
||||
from utils.site_reflect import parse as MwParse
|
||||
MwParse()
|
||||
mw.echoInfo("自动识别配置站点信息成功!")
|
||||
elif mw_input == 100:
|
||||
php_conf = panel_dir + '/plugins/php/info.json'
|
||||
if os.path.exists(php_conf):
|
||||
cont = mw.readFile(php_conf)
|
||||
cont = re.sub("\"53\"", "\"52\",\"53\"", cont)
|
||||
cont = re.sub("\"5.3.29\"", "\"5.2.17\",\"5.3.29\"", cont)
|
||||
mw.writeFile(php_conf, cont)
|
||||
mw.echoInfo("执行PHP52显示成功!")
|
||||
elif mw_input == 101:
|
||||
php_conf = panel_dir + '/plugins/php/info.json'
|
||||
if os.path.exists(php_conf):
|
||||
cont = mw.readFile(php_conf)
|
||||
cont = re.sub("\"52\",", "", cont)
|
||||
cont = re.sub("\"5.2.17\",", cont)
|
||||
mw.writeFile(php_conf, cont)
|
||||
mw.echoInfo("执行PHP52隐藏成功!")
|
||||
elif mw_input == 200:
|
||||
os.system("bash <(curl -sSL https://linuxmirrors.cn/main.sh)")
|
||||
# os.system(INIT_CMD + " mirror")
|
||||
elif mw_input == 201:
|
||||
os.system('curl -Lso- bench.sh | bash')
|
||||
elif mw_input == 202:
|
||||
package = mw.getPanelDir()+'/plugins'
|
||||
|
||||
dst_plugin = mw.getServerDir() + "/webssh"
|
||||
if not os.path.exists(dst_plugin):
|
||||
mw.echoInfo("未安装!")
|
||||
exit(1)
|
||||
|
||||
if not package in sys.path:
|
||||
sys.path.append(package)
|
||||
from webssh.index import App
|
||||
|
||||
obj = App()
|
||||
data = obj.get_server_list()
|
||||
data = json.loads(data)
|
||||
|
||||
if data['status']:
|
||||
wlist = data['data']
|
||||
wlist_len = len(wlist)
|
||||
if wlist_len == 0:
|
||||
mw.echoInfo("SSH终端管理,数据为空!")
|
||||
exit(1)
|
||||
|
||||
if len(sys.argv) == 4:
|
||||
pos = int(sys.argv[3])
|
||||
if pos >= wlist_len:
|
||||
mw.echoInfo("SSH终端管理,超过限制!")
|
||||
exit(1)
|
||||
dst_data = wlist[pos]
|
||||
tmp_host = dst_data["host"]
|
||||
info = obj.get_server_by_host_data(tmp_host)
|
||||
pp_info = tmp_host+"|"+info["port"]+"|"+info['username']+"|"+info['password']+""
|
||||
print(pp_info)
|
||||
exit(0)
|
||||
|
||||
mw.echoInfo("请选择:")
|
||||
for x in range(wlist_len):
|
||||
dst_data = wlist[x]
|
||||
tag = dst_data['host']
|
||||
if dst_data['ps'] != "":
|
||||
tag = dst_data['ps']
|
||||
print(str(x) +") " + tag)
|
||||
|
||||
def open_ssh_port():
|
||||
|
||||
find_ssh_port_cmd = "cat /etc/ssh/sshd_config | grep '^Port \\d*' | tail -1"
|
||||
cmd_data = mw.execShell(find_ssh_port_cmd)
|
||||
ssh_port = cmd_data[0].replace("Port ", '').strip()
|
||||
if ssh_port == '':
|
||||
ssh_port = '22'
|
||||
|
||||
mw.echoInfo("SSH端口: "+ str(ssh_port))
|
||||
MwFirewall.instance().addAcceptPort(ssh_port, 'SSH远程管理服务', 'port')
|
||||
return True
|
||||
|
||||
|
||||
def set_panel_pwd(password, ncli=False):
|
||||
info = thisdb.getUserByRoot()
|
||||
thisdb.setUserByRoot(password=password)
|
||||
if ncli:
|
||||
mw.echoInfo("username: " + info['name'])
|
||||
mw.echoInfo("password: " + password)
|
||||
else:
|
||||
print(username)
|
||||
|
||||
|
||||
def show_panel_pwd():
|
||||
# 面板密码展示
|
||||
info = thisdb.getUserByRoot()
|
||||
defailt_pwd_file = mw.getPanelDir()+'/data/default.pl'
|
||||
pwd = ''
|
||||
if os.path.exists(defailt_pwd_file):
|
||||
pwd = mw.readFile(defailt_pwd_file).strip()
|
||||
|
||||
if mw.md5(pwd) == info['password']:
|
||||
mw.echoInfo('password: ' + pwd)
|
||||
return
|
||||
print("*-password has been changed!")
|
||||
|
||||
def show_panel_adminpath():
|
||||
admin_path = thisdb.getOption('admin_path')
|
||||
if admin_path == '':
|
||||
print('/login')
|
||||
else:
|
||||
print('/'+admin_path)
|
||||
|
||||
|
||||
def set_panel_username(username=None):
|
||||
# 随机面板用户名
|
||||
if username:
|
||||
if len(username) < 5:
|
||||
mw.echoInfo("错误,用户名长度不能少于5位")
|
||||
return
|
||||
if username in ['admin', 'root']:
|
||||
mw.echoInfo("错误,不能使用过于简单的用户名")
|
||||
return
|
||||
|
||||
thisdb.setUserByRoot(name=username)
|
||||
mw.echoInfo("username: %s" % username)
|
||||
return
|
||||
|
||||
info = thisdb.getUserByRoot()
|
||||
if info['name'] == 'admin':
|
||||
username = mw.getRandomString(8).lower()
|
||||
thisdb.setUserByRoot(name=username)
|
||||
mw.echoInfo('username: ' + info['name'])
|
||||
|
||||
|
||||
def getServerIp():
|
||||
version = sys.argv[2]
|
||||
# ip = mw.execShell(
|
||||
# "curl --insecure -{} -sS --connect-timeout 5 -m 60 https://v6r.ipip.net/?format=text".format(version))
|
||||
ip = mw.execShell(
|
||||
"curl --insecure -{} -sS --connect-timeout 5 -m 60 https://ip.cachecha.com/?format=text".format(version))
|
||||
# print(ip[0])
|
||||
return ip[0]
|
||||
|
||||
|
||||
def getPanelSslType():
|
||||
scheme = 'http'
|
||||
panel_ssl = thisdb.getOptionByJson('panel_ssl', default={'open':False})
|
||||
if panel_ssl['open']:
|
||||
scheme = 'https'
|
||||
return scheme
|
||||
|
||||
def getPanelBindDomain():
|
||||
return thisdb.getOption('panel_domain', default='')
|
||||
|
||||
|
||||
def main():
|
||||
if len(sys.argv) == 1:
|
||||
print('ERROR: Parameter error!')
|
||||
exit(-2)
|
||||
method = sys.argv[1]
|
||||
if method == 'panel':
|
||||
set_panel_pwd(sys.argv[2])
|
||||
elif method == 'username':
|
||||
if len(sys.argv) > 2:
|
||||
set_panel_username(sys.argv[2])
|
||||
else:
|
||||
set_panel_username()
|
||||
elif method == 'password':
|
||||
show_panel_pwd()
|
||||
elif method == 'test':
|
||||
thisdb.getOption('admin_path')
|
||||
elif method == 'admin_path':
|
||||
show_panel_adminpath()
|
||||
elif method == 'getServerIp':
|
||||
print(getServerIp())
|
||||
elif method == 'panel_ssl_type':
|
||||
print(getPanelSslType())
|
||||
elif method == 'panel_bind_domain':
|
||||
print(getPanelBindDomain())
|
||||
elif method == "cli":
|
||||
clinum = 0
|
||||
try:
|
||||
if len(sys.argv) > 2:
|
||||
clinum = int(sys.argv[2]) if sys.argv[2][:6] else sys.argv[2]
|
||||
except:
|
||||
clinum = sys.argv[2]
|
||||
mwcli(clinum)
|
||||
else:
|
||||
print('ERROR: Parameter error')
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
34
plugins/acme_pandominassl_apply/conf/acme.sql
Normal file
34
plugins/acme_pandominassl_apply/conf/acme.sql
Normal file
@@ -0,0 +1,34 @@
|
||||
CREATE TABLE IF NOT EXISTS `dnsapi` (
|
||||
`id` INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
`name` TEXT,
|
||||
`type` TEXT,
|
||||
`val` TEXT,
|
||||
`remark` TEXT,
|
||||
`addtime` TEXT
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `email` (
|
||||
`id` INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
`addr` TEXT,
|
||||
`remark` TEXT,
|
||||
`addtime` TEXT
|
||||
);
|
||||
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `domain` (
|
||||
`id` INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
`domain` TEXT,
|
||||
`dnsapi_id` TEXT,
|
||||
`email` TEXT,
|
||||
`remark` TEXT,
|
||||
`effective_date` TEXT default '',
|
||||
`expiration_date` TEXT default '',
|
||||
`error` TEXT,
|
||||
`status` INTEGER default '0',
|
||||
`addtime` TEXT
|
||||
);
|
||||
|
||||
-- ALTER TABLE `domain` ADD COLUMN `effective_date` TEXT DEFAULT '';
|
||||
-- ALTER TABLE `domain` ADD COLUMN `expiration_date` TEXT DEFAULT '';
|
||||
-- ALTER TABLE `domain` ADD COLUMN `error` TEXT DEFAULT '';
|
||||
-- ALTER TABLE `domain` ADD COLUMN `status` INTEGER DEFAULT '0';
|
||||
136
plugins/acme_pandominassl_apply/hooks/goedge.py
Normal file
136
plugins/acme_pandominassl_apply/hooks/goedge.py
Normal file
@@ -0,0 +1,136 @@
|
||||
# coding:utf-8
|
||||
|
||||
import sys
|
||||
import io
|
||||
import os
|
||||
import time
|
||||
import re
|
||||
import requests
|
||||
import base64
|
||||
|
||||
|
||||
# 8001 / 7788
|
||||
goedge_addr = 'http://127.0.0.2:8009'
|
||||
access_keyid = "xxx"
|
||||
access_key = "xxx"
|
||||
|
||||
# 指定用户
|
||||
userId = 1
|
||||
|
||||
if sys.platform != "darwin":
|
||||
os.chdir("/www/server/mdserver-web")
|
||||
|
||||
|
||||
web_dir = os.getcwd() + "/web"
|
||||
if os.path.exists(web_dir):
|
||||
sys.path.append(web_dir)
|
||||
os.chdir(web_dir)
|
||||
|
||||
import core.mw as mw
|
||||
|
||||
domain = sys.argv[1]
|
||||
ssl_path = sys.argv[2]
|
||||
|
||||
|
||||
def getToken():
|
||||
api_url = goedge_addr+'/APIAccessTokenService/getAPIAccessToken'
|
||||
post_data = {
|
||||
"type": "admin",
|
||||
"accessKeyId": access_keyid,
|
||||
"accessKey": access_key
|
||||
}
|
||||
data = requests.post(api_url,json=post_data)
|
||||
data_obj = data.json()
|
||||
|
||||
return data_obj['data']['token']
|
||||
|
||||
token = getToken()
|
||||
|
||||
def commonReq(url, data):
|
||||
headers = {
|
||||
'X-Edge-Access-Token': token
|
||||
}
|
||||
api_url = goedge_addr+url
|
||||
resp_data = requests.post(api_url,json=data, headers=headers)
|
||||
return resp_data.json()
|
||||
|
||||
def listSSLCerts(domain):
|
||||
request_data = {
|
||||
"userId":userId,
|
||||
"isCA":False,
|
||||
"keyword": "ACME泛域名自动上传",
|
||||
"domains":[domain,"*."+domain],
|
||||
"size":1
|
||||
}
|
||||
response_data = commonReq('/SSLCertService/listSSLCerts', request_data)
|
||||
|
||||
data = response_data['data']['sslCertsJSON']
|
||||
data = mw.base64StrDecode(data)
|
||||
data = mw.getObjectByJson(data)
|
||||
# print(data)
|
||||
return data
|
||||
|
||||
|
||||
|
||||
# createSSLCert(domain)
|
||||
def createSSLCert(domain, did=0):
|
||||
|
||||
ssl_cer_file = ssl_path + '/'+domain+'.cer'
|
||||
|
||||
if not os.path.exists(ssl_cer_file):
|
||||
print("没有有效证书!")
|
||||
return ''
|
||||
# print(ssl_cer_file)
|
||||
ssl_info = mw.getCertName(ssl_cer_file)
|
||||
cer_data = mw.readFile(ssl_cer_file)
|
||||
cer_data = mw.base64StrEncode(cer_data)
|
||||
# print('cer',cer_data)
|
||||
|
||||
ssl_key_file = ssl_path + '/'+domain+'.key'
|
||||
key_data = mw.readFile(ssl_key_file)
|
||||
key_data = mw.base64StrEncode(key_data)
|
||||
# print('ssl_info',ssl_info)
|
||||
|
||||
timeBeginAt = int(time.mktime(time.strptime(ssl_info['notBefore'], "%Y-%m-%d")))
|
||||
timeEndAt = int(time.mktime(time.strptime(ssl_info['notAfter'], "%Y-%m-%d")))
|
||||
|
||||
request_data = {
|
||||
"isOn":True,
|
||||
"userId":userId,
|
||||
"name": "ACME泛域名自动上传",
|
||||
"isCA":False,
|
||||
"description":domain,
|
||||
"serverName":domain,
|
||||
"certData":cer_data,
|
||||
"keyData":key_data,
|
||||
"timeBeginAt":timeBeginAt,
|
||||
"timeEndAt": timeEndAt,
|
||||
"dnsNames":[domain,"*."+domain],
|
||||
"commonNames":[ssl_info['issuer']]
|
||||
}
|
||||
|
||||
if did>0:
|
||||
request_data['sslCertId'] = did
|
||||
# print(request_data)
|
||||
response_data = commonReq('/SSLCertService/updateSSLCert', request_data)
|
||||
print('更新成功',domain,response_data)
|
||||
return response_data
|
||||
else:
|
||||
# print(request_data)
|
||||
response_data = commonReq('/SSLCertService/createSSLCert', request_data)
|
||||
print('创建成功',domain,response_data)
|
||||
return response_data
|
||||
return response_data
|
||||
|
||||
def autoSyncDomain(domain):
|
||||
data = listSSLCerts(domain)
|
||||
if len(data) > 0 :
|
||||
did = data[0]['id']
|
||||
createSSLCert(domain,did)
|
||||
else:
|
||||
createSSLCert(domain)
|
||||
print(data)
|
||||
|
||||
|
||||
autoSyncDomain(domain)
|
||||
print(domain,ssl_path)
|
||||
12
plugins/acme_pandominassl_apply/hooks/init.py
Normal file
12
plugins/acme_pandominassl_apply/hooks/init.py
Normal file
@@ -0,0 +1,12 @@
|
||||
# coding:utf-8
|
||||
|
||||
import sys
|
||||
import io
|
||||
import os
|
||||
import time
|
||||
import re
|
||||
|
||||
domain = sys.argv[1]
|
||||
path = sys.argv[2]
|
||||
|
||||
print(domain,path)
|
||||
BIN
plugins/acme_pandominassl_apply/ico.png
Normal file
BIN
plugins/acme_pandominassl_apply/ico.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 4.0 KiB |
34
plugins/acme_pandominassl_apply/index.html
Executable file
34
plugins/acme_pandominassl_apply/index.html
Executable file
@@ -0,0 +1,34 @@
|
||||
<style>
|
||||
.overflow_hide {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
vertical-align: middle;
|
||||
}
|
||||
</style>
|
||||
|
||||
<div class="bt-form">
|
||||
<div class='plugin_version'></div>
|
||||
<div class="bt-w-main">
|
||||
<div class="bt-w-menu">
|
||||
<p class="bgw" onclick="pluginService('acme_pandominassl_apply');">服务</p>
|
||||
<p onclick="dnsapiList();">DNSAPI *</p>
|
||||
<p onclick="emailList();">邮件地址 </p>
|
||||
<p onclick="domainList()">域名SSL *</p>
|
||||
<p onclick="pluginConfigTpl('acme_pandominassl_apply',$('.plugin_version').attr('version'));">HOOK</p>
|
||||
<p onclick="pluginLogs('acme_pandominassl_apply','','run_log');">日志</p>
|
||||
<p onclick="apaReadme();">相关说明</p>
|
||||
|
||||
</div>
|
||||
<div class="bt-w-con pd15">
|
||||
<div class="soft-man-con"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script type="text/javascript">
|
||||
resetPluginWinWidth(1000);
|
||||
resetPluginWinHeight(550);
|
||||
$.getScript( "/plugins/file?name=acme_pandominassl_apply&f=js/common.js", function(){
|
||||
pluginService('acme_pandominassl_apply', $('.plugin_version').attr('version'));
|
||||
});
|
||||
</script>
|
||||
863
plugins/acme_pandominassl_apply/index.py
Executable file
863
plugins/acme_pandominassl_apply/index.py
Executable file
@@ -0,0 +1,863 @@
|
||||
# coding:utf-8
|
||||
|
||||
import sys
|
||||
import io
|
||||
import os
|
||||
import time
|
||||
import re
|
||||
import requests
|
||||
import json
|
||||
|
||||
web_dir = os.getcwd() + "/web"
|
||||
if os.path.exists(web_dir):
|
||||
sys.path.append(web_dir)
|
||||
os.chdir(web_dir)
|
||||
|
||||
import core.mw as mw
|
||||
|
||||
app_debug = False
|
||||
if mw.isAppleSystem():
|
||||
app_debug = True
|
||||
|
||||
|
||||
def getPluginName():
|
||||
return 'acme_pandominassl_apply'
|
||||
|
||||
|
||||
def getPluginDir():
|
||||
return mw.getPluginDir() + '/' + getPluginName()
|
||||
|
||||
|
||||
def getServerDir():
|
||||
return mw.getServerDir() + '/' + getPluginName()
|
||||
|
||||
|
||||
def getInitDFile():
|
||||
current_os = mw.getOs()
|
||||
if current_os == 'darwin':
|
||||
return '/tmp/' + getPluginName()
|
||||
|
||||
if current_os.startswith('freebsd'):
|
||||
return '/etc/rc.d/' + getPluginName()
|
||||
|
||||
return '/etc/init.d/' + getPluginName()
|
||||
|
||||
|
||||
def getConf():
|
||||
path = getServerDir() + "/hook.py"
|
||||
return path
|
||||
|
||||
|
||||
def getInitDTpl():
|
||||
path = getPluginDir() + "/init.d/" + getPluginName() + ".tpl"
|
||||
return path
|
||||
|
||||
def runLog():
|
||||
return getServerDir() + '/hook.log'
|
||||
|
||||
def getArgs():
|
||||
args = sys.argv[3:]
|
||||
# print(args)
|
||||
tmp = {}
|
||||
args_len = len(args)
|
||||
|
||||
if args_len == 1:
|
||||
t = args[0].strip('{').strip('}')
|
||||
if t.strip() == '':
|
||||
tmp = []
|
||||
else:
|
||||
t = t.split(':')
|
||||
tmp[t[0]] = t[1]
|
||||
tmp[t[0]] = t[1]
|
||||
elif args_len > 1:
|
||||
for i in range(len(args)):
|
||||
t = args[i].split(':')
|
||||
tmp[t[0]] = t[1]
|
||||
return tmp
|
||||
|
||||
def checkArgs(data, ck=[]):
|
||||
for i in range(len(ck)):
|
||||
if not ck[i] in data:
|
||||
return (False, mw.returnJson(False, '参数:(' + ck[i] + ')没有!'))
|
||||
return (True, mw.returnJson(True, 'ok'))
|
||||
|
||||
|
||||
def 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'
|
||||
|
||||
def configTpl():
|
||||
path = getPluginDir() + '/hooks'
|
||||
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 getPidFile():
|
||||
file = getConf()
|
||||
content = mw.readFile(file)
|
||||
rep = r'pidfile\s*(.*)'
|
||||
tmp = re.search(rep, content)
|
||||
return tmp.groups()[0].strip()
|
||||
|
||||
def status():
|
||||
return 'start'
|
||||
|
||||
def contentReplace(content):
|
||||
service_path = mw.getServerDir()
|
||||
content = content.replace('{$ROOT_PATH}', mw.getFatherDir())
|
||||
content = content.replace('{$SERVER_PATH}', service_path)
|
||||
content = content.replace('{$SERVER_APP}', service_path + '/redis')
|
||||
content = content.replace('{$REDIS_PASS}', mw.getRandomString(10))
|
||||
return content
|
||||
|
||||
|
||||
def pSqliteDb(dbname='dnsapi'):
|
||||
file = getServerDir() + '/acme.db'
|
||||
name = 'acme'
|
||||
|
||||
import_sql = mw.readFile(getPluginDir() + '/conf/acme.sql')
|
||||
md5_sql = mw.md5(import_sql)
|
||||
|
||||
import_sign = False
|
||||
save_md5_file = getServerDir() + '/acme.md5'
|
||||
if os.path.exists(save_md5_file):
|
||||
save_md5_sql = mw.readFile(save_md5_file)
|
||||
if save_md5_sql != md5_sql:
|
||||
import_sign = True
|
||||
mw.writeFile(save_md5_file, md5_sql)
|
||||
else:
|
||||
mw.writeFile(save_md5_file, md5_sql)
|
||||
|
||||
if not os.path.exists(file) or import_sql:
|
||||
conn = mw.M(dbname).dbPos(getServerDir(), name)
|
||||
csql_list = import_sql.split(';')
|
||||
for index in range(len(csql_list)):
|
||||
conn.execute(csql_list[index], ())
|
||||
|
||||
conn = mw.M(dbname).dbPos(getServerDir(), name)
|
||||
return conn
|
||||
|
||||
def initDreplace():
|
||||
|
||||
file_tpl = getInitDTpl()
|
||||
|
||||
initD_path = getServerDir() + '/init.d'
|
||||
if not os.path.exists(initD_path):
|
||||
os.mkdir(initD_path)
|
||||
file_bin = initD_path + '/' + getPluginName()
|
||||
|
||||
run_log_file = runLog()
|
||||
if not os.path.exists(run_log_file):
|
||||
mw.writeFile(run_log_file,'')
|
||||
|
||||
hook_file = getConf()
|
||||
if not os.path.exists(hook_file):
|
||||
mw.writeFile(hook_file,'')
|
||||
|
||||
return file_bin
|
||||
|
||||
|
||||
def apaOp(method):
|
||||
file = initDreplace()
|
||||
return 'ok'
|
||||
|
||||
# current_os = mw.getOs()
|
||||
# if current_os == "darwin":
|
||||
# data = mw.execShell(file + ' ' + method)
|
||||
# if data[1] == '':
|
||||
# return 'ok'
|
||||
# return data[1]
|
||||
|
||||
# if current_os.startswith("freebsd"):
|
||||
# data = mw.execShell('service ' + getPluginName() + ' ' + method)
|
||||
# if data[1] == '':
|
||||
# return 'ok'
|
||||
# return data[1]
|
||||
|
||||
# data = mw.execShell('systemctl ' + method + ' ' + getPluginName())
|
||||
# if data[1] == '':
|
||||
# return 'ok'
|
||||
# return data[1]
|
||||
|
||||
|
||||
def start():
|
||||
import tool_cron
|
||||
tool_cron.createBgTask()
|
||||
|
||||
return apaOp('start')
|
||||
|
||||
|
||||
def stop():
|
||||
import tool_cron
|
||||
tool_cron.removeBgTask()
|
||||
return apaOp('stop')
|
||||
|
||||
|
||||
def restart():
|
||||
status = apaOp('restart')
|
||||
return status
|
||||
|
||||
|
||||
def reload():
|
||||
return apaOp('reload')
|
||||
|
||||
|
||||
def dnsapiAdd():
|
||||
conn = pSqliteDb('dnsapi')
|
||||
args = getArgs()
|
||||
data = checkArgs(args,['id','name', 'type', 'remark'])
|
||||
if not data[0]:
|
||||
return data[1]
|
||||
|
||||
name = args['name'].strip()
|
||||
remark = args['remark'].strip()
|
||||
stype = args['type'].strip()
|
||||
val = args['val'].strip()
|
||||
sid = args['id'].strip()
|
||||
|
||||
if name == '':
|
||||
return mw.returnJson(False, '名称不能为空!')
|
||||
|
||||
if sid != '0' : #修改操作
|
||||
conn.where("id=?", (sid,)).update({
|
||||
'name':name,
|
||||
'type':stype,
|
||||
'val':val,
|
||||
'remark':remark,
|
||||
})
|
||||
return mw.returnJson(True, '修改成功!')
|
||||
|
||||
if conn.where("name=?", (name,)).count():
|
||||
return mw.returnJson(False, name+'已存在!')
|
||||
|
||||
addTime = time.strftime('%Y-%m-%d %X', time.localtime())
|
||||
err = conn.add('name,type,val,remark,addtime', (name, stype, val,remark, addTime))
|
||||
# print(err)
|
||||
return mw.returnJson(True, '添加成功!')
|
||||
|
||||
def dnsapiDel():
|
||||
args = getArgs()
|
||||
data = checkArgs(args, ['id', 'name'])
|
||||
if not data[0]:
|
||||
return data[1]
|
||||
|
||||
conn = pSqliteDb('dnsapi')
|
||||
try:
|
||||
sid = args['id']
|
||||
name = args['name']
|
||||
conn.where("id=?", (sid,)).delete()
|
||||
return mw.returnJson(True, '删除成功!')
|
||||
except Exception as ex:
|
||||
return mw.returnJson(False, '删除失败!' + str(ex))
|
||||
|
||||
def dnsapiList():
|
||||
args = getArgs()
|
||||
page = 1
|
||||
page_size = 10
|
||||
search = ''
|
||||
data = {}
|
||||
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']
|
||||
|
||||
conn = pSqliteDb('dnsapi')
|
||||
limit = str((page - 1) * page_size) + ',' + str(page_size)
|
||||
condition = ''
|
||||
if not search == '':
|
||||
condition = "name like '%" + search + "%'"
|
||||
field = 'id,name,type,val,remark,addtime'
|
||||
clist = conn.where(condition, ()).field(
|
||||
field).limit(limit).order('id desc').select()
|
||||
|
||||
count = conn.where(condition, ()).count()
|
||||
_page = {}
|
||||
_page['count'] = count
|
||||
_page['p'] = page
|
||||
_page['row'] = page_size
|
||||
_page['tojs'] = 'dbList'
|
||||
data['page'] = mw.getPage(_page)
|
||||
data['data'] = clist
|
||||
|
||||
return mw.getJson(data)
|
||||
|
||||
def dnsapiListAll():
|
||||
conn = pSqliteDb('dnsapi')
|
||||
field = 'id,name,type,val,remark,addtime'
|
||||
data = conn.field(field).limit('1000').select()
|
||||
return mw.getJson(data)
|
||||
|
||||
def emailList():
|
||||
args = getArgs()
|
||||
page = 1
|
||||
page_size = 10
|
||||
search = ''
|
||||
data = {}
|
||||
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']
|
||||
|
||||
conn = pSqliteDb('email')
|
||||
limit = str((page - 1) * page_size) + ',' + str(page_size)
|
||||
condition = ''
|
||||
if not search == '':
|
||||
condition = "addr like '%" + search + "%'"
|
||||
field = 'id,addr,remark,addtime'
|
||||
clist = conn.where(condition, ()).field(
|
||||
field).limit(limit).order('id desc').select()
|
||||
|
||||
count = conn.where(condition, ()).count()
|
||||
_page = {}
|
||||
_page['count'] = count
|
||||
_page['p'] = page
|
||||
_page['row'] = page_size
|
||||
_page['tojs'] = 'dbList'
|
||||
data['page'] = mw.getPage(_page)
|
||||
data['data'] = clist
|
||||
|
||||
return mw.getJson(data)
|
||||
|
||||
def emailAdd():
|
||||
args = getArgs()
|
||||
data = checkArgs(args,['addr', 'remark'])
|
||||
if not data[0]:
|
||||
return data[1]
|
||||
|
||||
addr = args['addr'].strip()
|
||||
remark = args['remark'].strip()
|
||||
|
||||
if addr == '':
|
||||
return mw.returnJson(False, '邮件地址不能为空!')
|
||||
|
||||
conn = pSqliteDb('email')
|
||||
|
||||
addTime = time.strftime('%Y-%m-%d %X', time.localtime())
|
||||
conn.add('addr,remark,addtime', (addr, remark, addTime))
|
||||
return mw.returnJson(True, '添加成功!')
|
||||
|
||||
def emailDel():
|
||||
args = getArgs()
|
||||
data = checkArgs(args, ['id', 'name'])
|
||||
if not data[0]:
|
||||
return data[1]
|
||||
|
||||
conn = pSqliteDb('email')
|
||||
try:
|
||||
sid = args['id']
|
||||
name = args['name']
|
||||
conn.where("id=?", (sid,)).delete()
|
||||
return mw.returnJson(True, '删除成功!')
|
||||
except Exception as ex:
|
||||
return mw.returnJson(False, '删除失败!' + str(ex))
|
||||
|
||||
def domainAdd():
|
||||
args = getArgs()
|
||||
data = checkArgs(args,['id','domain', 'dnsapi_id','email','remark'])
|
||||
if not data[0]:
|
||||
return data[1]
|
||||
|
||||
sid = args['id'].strip()
|
||||
domain = args['domain'].strip()
|
||||
remark = args['remark'].strip()
|
||||
email = args['email'].strip()
|
||||
dnsapi_id = args['dnsapi_id'].strip()
|
||||
|
||||
if domain == '':
|
||||
return mw.returnJson(False, '域名不能为空!')
|
||||
if email == '':
|
||||
return mw.returnJson(False, '邮件不能为空!')
|
||||
|
||||
conn = pSqliteDb('domain')
|
||||
|
||||
if sid != '0' : #修改操作
|
||||
conn.where("id=?", (sid,)).update({
|
||||
'domain':domain,
|
||||
'dnsapi_id':dnsapi_id,
|
||||
'email':email,
|
||||
'remark':remark,
|
||||
})
|
||||
return mw.returnJson(True, '修改成功!')
|
||||
|
||||
if conn.where("domain=?", (domain,)).count():
|
||||
return mw.returnJson(False, domain+'已存在!')
|
||||
|
||||
|
||||
|
||||
addTime = time.strftime('%Y-%m-%d %X', time.localtime())
|
||||
conn.add('domain,dnsapi_id,email,remark,addtime', (domain, dnsapi_id,email,remark, addTime))
|
||||
return mw.returnJson(True, '添加成功!')
|
||||
|
||||
def domainDel():
|
||||
args = getArgs()
|
||||
data = checkArgs(args, ['id', 'name'])
|
||||
if not data[0]:
|
||||
return data[1]
|
||||
|
||||
conn = pSqliteDb('domain')
|
||||
try:
|
||||
sid = args['id']
|
||||
name = args['name']
|
||||
conn.where("id=?", (sid,)).delete()
|
||||
return mw.returnJson(True, '删除成功!')
|
||||
except Exception as ex:
|
||||
return mw.returnJson(False, '删除失败!' + str(ex))
|
||||
|
||||
def domainStatusToggle():
|
||||
args = getArgs()
|
||||
data = checkArgs(args, ['id'])
|
||||
if not data[0]:
|
||||
return data[1]
|
||||
|
||||
conn = pSqliteDb('domain')
|
||||
|
||||
field = 'id,status'
|
||||
fdata = conn.field(field).where('id=?',(args['id'],)).find()
|
||||
|
||||
fstatus = fdata['status']
|
||||
if fstatus == 0:
|
||||
fstatus = 1
|
||||
else:
|
||||
fstatus = 0
|
||||
# print(fdata['status'],fstatus)
|
||||
conn.where("id=?", (args['id'],)).update({
|
||||
'status':fstatus,
|
||||
})
|
||||
return mw.returnJson(True, '切换成功!')
|
||||
|
||||
def domainList():
|
||||
args = getArgs()
|
||||
page = 1
|
||||
page_size = 10
|
||||
search = ''
|
||||
data = {}
|
||||
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']
|
||||
|
||||
conn = pSqliteDb('domain')
|
||||
conn_dnsapi = pSqliteDb('dnsapi')
|
||||
limit = str((page - 1) * page_size) + ',' + str(page_size)
|
||||
condition = ''
|
||||
if not search == '':
|
||||
condition = "domain like '%" + search + "%'"
|
||||
field = 'id,domain,dnsapi_id,email,status,effective_date,expiration_date,remark,addtime'
|
||||
clist = conn.where(condition, ()).field(
|
||||
field).limit(limit).order('id desc').select()
|
||||
|
||||
for i in range(len(clist)):
|
||||
tdata = conn_dnsapi.field('id,name').where('id=?',(clist[i]['dnsapi_id'],)).select()
|
||||
if len(tdata) > 0:
|
||||
# print(tdata[0]['name'])
|
||||
clist[i]['dnsapi_id_alias'] = clist[i]['dnsapi_id']+'('+ tdata[0]['name'] +')'
|
||||
else:
|
||||
clist[i]['dnsapi_id_alias'] = clist[i]['dnsapi_id']+' (无)'
|
||||
|
||||
count = conn.where(condition, ()).count()
|
||||
_page = {}
|
||||
_page['count'] = count
|
||||
_page['p'] = page
|
||||
_page['row'] = page_size
|
||||
_page['tojs'] = 'dbList'
|
||||
data['page'] = mw.getPage(_page)
|
||||
data['data'] = clist
|
||||
|
||||
return mw.getJson(data)
|
||||
|
||||
def getDnsapiData(dnsapi_id):
|
||||
if dnsapi_id == '0':
|
||||
return {}
|
||||
conn_dnsapi = pSqliteDb('dnsapi')
|
||||
tdata = conn_dnsapi.field('id,name,type,val').where('id=?',(dnsapi_id,)).select()
|
||||
|
||||
if len(tdata)>0:
|
||||
return tdata[0]
|
||||
return {}
|
||||
|
||||
def getDnsapiExportVar(val):
|
||||
cmd_list = val.split('~')
|
||||
def_var = ''
|
||||
for x in range(len(cmd_list)):
|
||||
v = cmd_list[x]
|
||||
vlist = v.split('|')
|
||||
def_var += 'export '+vlist[0]+'="'+vlist[1]+'"\n'
|
||||
return def_var
|
||||
|
||||
def getDnsapiKv(val):
|
||||
cmd_list = val.split('~')
|
||||
def_var = {}
|
||||
for x in range(len(cmd_list)):
|
||||
v = cmd_list[x]
|
||||
vlist = v.split('|')
|
||||
kk = vlist[0]
|
||||
vv = vlist[1]
|
||||
def_var[kk] = vv
|
||||
return def_var
|
||||
|
||||
def domainApplyPathJudge(domain):
|
||||
acme_dir = "/root/.acme.sh"
|
||||
if mw.isAppleSystem():
|
||||
user = getRunUser()
|
||||
acme_dir = "/Users/"+user+"/.acme.sh"
|
||||
|
||||
abs_domain_path = acme_dir+'/'+domain+'_ecc'
|
||||
# print(abs_domain_path)
|
||||
if os.path.exists(abs_domain_path):
|
||||
return True, abs_domain_path
|
||||
|
||||
abs_domain_path = acme_dir+'/'+domain
|
||||
# print(abs_domain_path)
|
||||
if os.path.exists(abs_domain_path):
|
||||
return True, abs_domain_path
|
||||
|
||||
return False,''
|
||||
|
||||
|
||||
def hookWriteLog(line):
|
||||
hook_file = runLog()
|
||||
mdate = time.strftime('%Y-%m-%d %X', time.localtime())
|
||||
log = "["+mdate+"]:"+line
|
||||
print(log)
|
||||
return mw.writeFile(hook_file,log+"\n",'a+')
|
||||
|
||||
def runHookPy(domain,path):
|
||||
# print(domain,path)
|
||||
run_log = runLog()
|
||||
hook_file = getConf()
|
||||
cmd = 'cd '+mw.getPanelDir()
|
||||
cmd += ' && python3 '+hook_file + ' ' + domain + ' ' + path
|
||||
cmd += ' >> '+ run_log
|
||||
print(cmd)
|
||||
return mw.execShell(cmd)
|
||||
|
||||
def autoUpdateSslData(domain, path):
|
||||
ssl_cer_file = path + '/'+domain+'.cer'
|
||||
ssl_info = mw.getCertName(ssl_cer_file)
|
||||
|
||||
time_begin = int(time.mktime(time.strptime(ssl_info['notBefore'], "%Y-%m-%d")))
|
||||
time_end = int(time.mktime(time.strptime(ssl_info['notAfter'], "%Y-%m-%d")))
|
||||
# print(ssl_info)
|
||||
# print(time_begin,time_end)
|
||||
conn = pSqliteDb('domain')
|
||||
conn.where('domain=?', (domain,)).update({
|
||||
'effective_date':str(time_begin),
|
||||
'expiration_date':str(time_end),
|
||||
})
|
||||
|
||||
return True
|
||||
|
||||
def runHookDstDomain(row):
|
||||
domain = row['domain']
|
||||
email = str(row['email'])
|
||||
|
||||
if row['effective_date'] != '':
|
||||
effective_date = int(row['effective_date'])
|
||||
now_int = int(time.time())
|
||||
day = (now_int - effective_date)/86400
|
||||
if int(day) < 8:
|
||||
common_log = '【'+domain+'】未过期'
|
||||
hookWriteLog(common_log)
|
||||
return
|
||||
|
||||
hookWriteLog('开始申请【'+domain+'】SSL证书')
|
||||
cmd = "#!/bin/bash\n"
|
||||
cmd += "PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin:/opt/homebrew/bin:/root/.acme.sh\n"
|
||||
cmd += "export PATH\n"
|
||||
|
||||
if mw.isAppleSystem():
|
||||
user = getRunUser()
|
||||
cmd += ". /Users/"+user+"/.zshrc\n"
|
||||
|
||||
cmd_data = getDnsapiData(row['dnsapi_id'])
|
||||
# print(cmd_data)
|
||||
export_val = getDnsapiExportVar(cmd_data['val'])
|
||||
cmd += export_val
|
||||
|
||||
# acme.sh --register-account -m my@example.com
|
||||
cmd_register = 'acme.sh --register-account -m '+ email + '\n'
|
||||
cmd += cmd_register
|
||||
|
||||
|
||||
# acme.sh --issue -d "example.com" -d "*.example.com" --dns dns_cf
|
||||
cmd_apply = 'acme.sh --issue --dns '+str(cmd_data['type'])+' -d '+domain+' -d "*.'+domain+'"'
|
||||
if row['effective_date'] != '':
|
||||
effective_date = int(row['effective_date'])
|
||||
now_int = int(time.time())
|
||||
day = (now_int - effective_date)/86400
|
||||
if int(day) > 7:
|
||||
cmd_apply = 'acme.sh --issue --dns '+str(cmd_data['type'])+' -d '+domain+' -d "*.'+domain+'" --force'
|
||||
cmd += cmd_apply
|
||||
|
||||
run_log = runLog()
|
||||
cmd += ' >> '+ run_log
|
||||
print(cmd)
|
||||
os.system(cmd)
|
||||
hookWriteLog('结束申请【'+domain+'】SSL证书')
|
||||
isok, path = domainApplyPathJudge(domain)
|
||||
print(isok,path)
|
||||
if isok:
|
||||
autoUpdateSslData(domain, path)
|
||||
hookWriteLog('开始执行【'+domain+'】Hook脚本')
|
||||
runHookPy(domain,path)
|
||||
hookWriteLog('结束执行【'+domain+'】Hook脚本')
|
||||
|
||||
def getHookIdCmd():
|
||||
args = getArgs()
|
||||
data = checkArgs(args, ['id'])
|
||||
if not data[0]:
|
||||
return data[1]
|
||||
|
||||
cmd = "cd "+mw.getPanelDir()+" "
|
||||
cmd += '&& python3 plugins/acme_pandominassl_apply/index.py run_hook_id 1.0 {"id":"'+args['id']+'"}'
|
||||
return mw.returnJson(True, 'ok',cmd)
|
||||
|
||||
def runHookId():
|
||||
args = getArgs()
|
||||
data = checkArgs(args, ['id'])
|
||||
if not data[0]:
|
||||
return data[1]
|
||||
|
||||
conn = pSqliteDb('domain')
|
||||
field = 'id,domain,dnsapi_id,email,effective_date,expiration_date,remark'
|
||||
row = conn.field(field).where('id=?',(args['id'],)).find()
|
||||
runHookDstDomain(row)
|
||||
return 'run hook '+args['id']+' end'
|
||||
|
||||
def runHookCmd():
|
||||
cmd = "cd "+mw.getPanelDir()+" "
|
||||
cmd += '&& python3 plugins/acme_pandominassl_apply/index.py run_hook'
|
||||
return mw.returnJson(True, 'ok',cmd)
|
||||
|
||||
def runHook():
|
||||
conn = pSqliteDb('domain')
|
||||
|
||||
field = 'id,domain,dnsapi_id,email,effective_date,expiration_date,remark'
|
||||
clist = conn.field(field).where('status=?',(1,)).limit('1000').order('id desc').select()
|
||||
|
||||
for idx in range(len(clist)):
|
||||
row = clist[idx]
|
||||
# print(row)
|
||||
runHookDstDomain(row)
|
||||
time.sleep(1)
|
||||
return 'run hook end'
|
||||
|
||||
def autoSyncDomain(domain, dnsapi_id, email):
|
||||
conn = pSqliteDb('domain')
|
||||
|
||||
field = 'id,domain,dnsapi_id,email,effective_date,expiration_date,remark'
|
||||
fdata = conn.field(field).where('domain=?',(domain,)).select()
|
||||
# print(fdata)
|
||||
mdate = time.strftime('%Y-%m-%d %X', time.localtime())
|
||||
if len(fdata) == 0:
|
||||
conn.add('domain,dnsapi_id,email,remark,addtime', (domain, dnsapi_id, email,"备注", mdate))
|
||||
print(domain+" 同步成功!")
|
||||
return True
|
||||
|
||||
def runSyncCfDataRow(row, page = 1):
|
||||
# print(row, page)
|
||||
|
||||
cf_key = row['kv']['CF_Key']
|
||||
cf_mail = row['kv']['CF_Email']
|
||||
header = {
|
||||
'X-Auth-Email': cf_mail,
|
||||
'X-Auth-Key': cf_key,
|
||||
'Content-Type': 'application/json'
|
||||
}
|
||||
url = "https://api.cloudflare.com/client/v4/zones?status=active&page="+str(page)+"&per_page=20&order=status&direction=desc&match=all"
|
||||
try:
|
||||
r = requests.get(url, timeout=30, headers=header)
|
||||
r.raise_for_status()
|
||||
r.encoding = r.apparent_encoding
|
||||
# print(r.text)
|
||||
jdata = json.loads(r.text)
|
||||
result = jdata['result']
|
||||
result_info = jdata['result_info']
|
||||
|
||||
# print(len(result))
|
||||
for i in result:
|
||||
autoSyncDomain(i['name'], row['id'], cf_mail)
|
||||
|
||||
if result_info['total_pages'] > page:
|
||||
page += 1
|
||||
runSyncCfDataRow(row, page)
|
||||
# print(result)
|
||||
except Exception as e:
|
||||
print(e)
|
||||
|
||||
# 同步CloudFlare数据
|
||||
def runSyncCfData():
|
||||
print("同步CloudFlare数据开始")
|
||||
conn_dnsapi = pSqliteDb('dnsapi')
|
||||
field = "id,name,type,val"
|
||||
fdata = conn_dnsapi.field(field).where('type=?', ('dns_cf',)).limit('10').select()
|
||||
|
||||
for x in range(len(fdata)):
|
||||
row = fdata[x]
|
||||
dnsapi_kv = getDnsapiKv(row['val'])
|
||||
row['kv'] = dnsapi_kv
|
||||
runSyncCfDataRow(row)
|
||||
|
||||
print("同步CloudFlare数据结束")
|
||||
return ''
|
||||
|
||||
def runSyncCfCmd():
|
||||
cmd = "cd "+mw.getPanelDir()+" "
|
||||
cmd += '&& python3 plugins/acme_pandominassl_apply/index.py run_sync_cf_data'
|
||||
return mw.returnJson(True, 'ok',cmd)
|
||||
|
||||
def runSyncDnspodDataRow(row, page = 1):
|
||||
# print(row, page)
|
||||
|
||||
DPI_Id = row['kv']['DPI_Id']
|
||||
DPI_Key = row['kv']['DPI_Key']
|
||||
data = {
|
||||
'login_token': DPI_Id+','+DPI_Key,
|
||||
'length':5,
|
||||
'format':'json',
|
||||
}
|
||||
url = "https://api.dnspod.com/Domain.List"
|
||||
try:
|
||||
r = requests.post(url, timeout=30, data=data)
|
||||
r.raise_for_status()
|
||||
r.encoding = r.apparent_encoding
|
||||
|
||||
# print(r.text)
|
||||
jdata = json.loads(r.text)
|
||||
info = jdata['info']
|
||||
domains = jdata['domains']
|
||||
|
||||
# print(jdata)
|
||||
for i in domains:
|
||||
autoSyncDomain(i['name'], row['id'], i['owner'])
|
||||
|
||||
page_total = int(info['domain_total']/data['length'])
|
||||
if page_total > page:
|
||||
page += 1
|
||||
runSyncDnspodDataRow(row, page)
|
||||
except Exception as e:
|
||||
print(e)
|
||||
|
||||
# 同步DnsPod数据
|
||||
def runSyncDnsPodData():
|
||||
print("同步DnsPod数据开始")
|
||||
conn_dnsapi = pSqliteDb('dnsapi')
|
||||
field = "id,name,type,val"
|
||||
fdata = conn_dnsapi.field(field).where('type=?', ('dns_dpi',)).limit('10').select()
|
||||
for x in range(len(fdata)):
|
||||
row = fdata[x]
|
||||
dnsapi_kv = getDnsapiKv(row['val'])
|
||||
row['kv'] = dnsapi_kv
|
||||
runSyncDnspodDataRow(row)
|
||||
|
||||
print(fdata)
|
||||
print("同步DnsPod数据结束")
|
||||
return ''
|
||||
|
||||
def runSyncDnsPodCmd():
|
||||
cmd = "cd "+mw.getPanelDir()+" "
|
||||
cmd += '&& python3 plugins/acme_pandominassl_apply/index.py run_sync_dnspod_data'
|
||||
return mw.returnJson(True, 'ok',cmd)
|
||||
|
||||
if __name__ == "__main__":
|
||||
func = sys.argv[1]
|
||||
# print(func)
|
||||
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 == 'run_info':
|
||||
print(runInfo())
|
||||
elif func == 'conf':
|
||||
print(getConf())
|
||||
elif func == 'run_log':
|
||||
print(runLog())
|
||||
elif func == 'config_tpl':
|
||||
print(configTpl())
|
||||
elif func == 'read_config_tpl':
|
||||
print(readConfigTpl())
|
||||
elif func == 'dnsapi_list':
|
||||
print(dnsapiList())
|
||||
elif func == 'dnsapi_list_all':
|
||||
print(dnsapiListAll())
|
||||
elif func == 'dnsapi_add':
|
||||
print(dnsapiAdd())
|
||||
elif func == 'dnsapi_del':
|
||||
print(dnsapiDel())
|
||||
elif func == 'email_list':
|
||||
print(emailList())
|
||||
elif func == 'email_add':
|
||||
print(emailAdd())
|
||||
elif func == 'email_del':
|
||||
print(emailDel())
|
||||
elif func == 'domain_list':
|
||||
print(domainList())
|
||||
elif func == 'domain_add':
|
||||
print(domainAdd())
|
||||
elif func == 'domain_del':
|
||||
print(domainDel())
|
||||
elif func == 'domain_status_toggle':
|
||||
print(domainStatusToggle())
|
||||
elif func == 'run_hook':
|
||||
print(runHook())
|
||||
elif func == 'run_hook_cmd':
|
||||
print(runHookCmd())
|
||||
elif func == 'get_run_hook_id_cmd':
|
||||
print(getHookIdCmd())
|
||||
elif func == 'run_hook_id':
|
||||
print(runHookId())
|
||||
elif func == 'run_sync_cf_data':
|
||||
print(runSyncCfData())
|
||||
elif func == 'run_sync_cf_cmd':
|
||||
print(runSyncCfCmd())
|
||||
elif func == 'run_sync_dnspod_data':
|
||||
print(runSyncDnsPodData())
|
||||
elif func == 'run_sync_dnspod_cmd':
|
||||
print(runSyncDnsPodCmd())
|
||||
else:
|
||||
print('error')
|
||||
17
plugins/acme_pandominassl_apply/info.json
Executable file
17
plugins/acme_pandominassl_apply/info.json
Executable file
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"sort": 7,
|
||||
"ps": "ACME泛域名SSL申请/管理",
|
||||
"name": "acme_pandominassl_apply",
|
||||
"title": "ACME泛域名SSL",
|
||||
"shell": "install.sh",
|
||||
"versions":["1.0"],
|
||||
"tip": "soft",
|
||||
"checks": "server/acme_pandominassl_apply",
|
||||
"path": "server/acme_pandominassl_apply",
|
||||
"display": 1,
|
||||
"author": "ACME",
|
||||
"date": "2024-09-12",
|
||||
"home": "https://github.com/acmesh-official/acme.sh",
|
||||
"type": 0,
|
||||
"pid": "5"
|
||||
}
|
||||
54
plugins/acme_pandominassl_apply/install.sh
Executable file
54
plugins/acme_pandominassl_apply/install.sh
Executable file
@@ -0,0 +1,54 @@
|
||||
#!/bin/bash
|
||||
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin:/opt/homebrew/bin
|
||||
export PATH
|
||||
|
||||
curPath=`pwd`
|
||||
rootPath=$(dirname "$curPath")
|
||||
rootPath=$(dirname "$rootPath")
|
||||
serverPath=$(dirname "$rootPath")
|
||||
|
||||
VERSION=$2
|
||||
|
||||
# curl https://get.acme.sh | sh
|
||||
# acme.sh --uninstall
|
||||
|
||||
|
||||
# . /Users/xxx/.zshrc
|
||||
|
||||
# https://github.com/acmesh-official/acme.sh/wiki/dnsapi
|
||||
# https://docs.dnspod.com/api-legacy/domains.html#get-the-domain-list
|
||||
|
||||
# cd /www/server/mdserver-web && python3 plugins/acme_pandominassl_apply/index.py run_hook
|
||||
# cd /www/server/mdserver-web && python3 plugins/acme_pandominassl_apply/index.py run_sync_cf_data
|
||||
# cd /www/server/mdserver-web && python3 plugins/acme_pandominassl_apply/index.py run_sync_dnspod_data
|
||||
|
||||
|
||||
if [ -f ${rootPath}/bin/activate ];then
|
||||
. ${rootPath}/bin/activate
|
||||
fi
|
||||
|
||||
# pip install cloudflare
|
||||
Install_App()
|
||||
{
|
||||
echo '正在安装脚本文件...'
|
||||
mkdir -p $serverPath/source
|
||||
mkdir -p $serverPath/acme_pandominassl_apply
|
||||
echo "${VERSION}" > $serverPath/acme_pandominassl_apply/version.pl
|
||||
|
||||
cd ${rootPath} && python3 ${rootPath}/plugins/acme_pandominassl_apply/index.py start
|
||||
echo '安装[ACME泛域名SSL]完成'
|
||||
}
|
||||
|
||||
Uninstall_App()
|
||||
{
|
||||
cd ${rootPath} && python3 ${rootPath}/plugins/acme_pandominassl_apply/index.py stop
|
||||
rm -rf $serverPath/acme_pandominassl_apply
|
||||
echo "卸载[ACME泛域名SSL]成功"
|
||||
}
|
||||
|
||||
action=$1
|
||||
if [ "${1}" = 'install' ];then
|
||||
Install_App
|
||||
else
|
||||
Uninstall_App
|
||||
fi
|
||||
801
plugins/acme_pandominassl_apply/js/common.js
Executable file
801
plugins/acme_pandominassl_apply/js/common.js
Executable file
@@ -0,0 +1,801 @@
|
||||
function apaPost(method, args,callback){
|
||||
var loadT = layer.msg('正在获取...', { icon: 16, time: 0, shade: 0.3 });
|
||||
|
||||
var req_data = {};
|
||||
req_data['name'] = 'acme_pandominassl_apply';
|
||||
req_data['func'] = method;
|
||||
req_data['version'] = $('.plugin_version').attr('version');
|
||||
|
||||
if (typeof(args) == 'string'){
|
||||
req_data['args'] = JSON.stringify(toArrayObject(args));
|
||||
} else {
|
||||
req_data['args'] = JSON.stringify(args);
|
||||
}
|
||||
|
||||
$.post('/plugins/run', req_data, function(data) {
|
||||
layer.close(loadT);
|
||||
if (!data.status){
|
||||
//错误展示10S
|
||||
layer.msg(data.msg,{icon:0,time:2000,shade: [10, '#000']});
|
||||
return;
|
||||
}
|
||||
|
||||
if(typeof(callback) == 'function'){
|
||||
callback(data);
|
||||
}
|
||||
},'json');
|
||||
}
|
||||
|
||||
|
||||
function apaPostN(method, args,callback){
|
||||
|
||||
var req_data = {};
|
||||
req_data['name'] = 'acme_pandominassl_apply';
|
||||
req_data['func'] = method;
|
||||
req_data['version'] = $('.plugin_version').attr('version');
|
||||
|
||||
if (typeof(args) == 'string'){
|
||||
req_data['args'] = JSON.stringify(toArrayObject(args));
|
||||
} else {
|
||||
req_data['args'] = JSON.stringify(args);
|
||||
}
|
||||
|
||||
$.post('/plugins/run', req_data, function(data) {
|
||||
if (!data.status){
|
||||
//错误展示10S
|
||||
layer.msg(data.msg,{icon:0,time:2000,shade: [10, '#000']});
|
||||
return;
|
||||
}
|
||||
|
||||
if(typeof(callback) == 'function'){
|
||||
callback(data);
|
||||
}
|
||||
},'json');
|
||||
}
|
||||
|
||||
function apaPostCallbak(method, version, args,callback){
|
||||
var loadT = layer.msg('正在获取...', { icon: 16, time: 0, shade: 0.3 });
|
||||
|
||||
var req_data = {};
|
||||
req_data['name'] = 'acme_pandominassl_apply';
|
||||
req_data['func'] = method;
|
||||
args['version'] = version;
|
||||
|
||||
if (typeof(args) == 'string'){
|
||||
req_data['args'] = JSON.stringify(toArrayObject(args));
|
||||
} else {
|
||||
req_data['args'] = JSON.stringify(args);
|
||||
}
|
||||
|
||||
$.post('/plugins/callback', req_data, function(data) {
|
||||
layer.close(loadT);
|
||||
if (!data.status){
|
||||
layer.msg(data.msg,{icon:0,time:2000,shade: [0.3, '#000']});
|
||||
return;
|
||||
}
|
||||
|
||||
if(typeof(callback) == 'function'){
|
||||
callback(data);
|
||||
}
|
||||
},'json');
|
||||
}
|
||||
|
||||
function apaReadme(){
|
||||
var readme = '<ul class="help-info-text c7">';
|
||||
readme += '<li>ACME泛域名SSL申请/管理/HOOK</li>';
|
||||
readme += '<li>通过DNS验证获取SSL证书!</li>';
|
||||
readme += '<li>HOOK: ssl发生变动时调用!</li>';
|
||||
readme += '<li>暂时仅支持1000个域名管理!</li>';
|
||||
readme += '<li>DNSAPI文档: https://github.com/acmesh-official/acme.sh/wiki/dnsapi</li>';
|
||||
readme += '<li>默认7天强制更新!</li>';
|
||||
|
||||
|
||||
readme += '</ul>';
|
||||
$('.soft-man-con').html(readme);
|
||||
}
|
||||
|
||||
|
||||
function emailDel(id, name){
|
||||
safeMessage('删除['+name+']','您真的要删除['+name+']吗?',function(){
|
||||
var data='id='+id+'&name='+name;
|
||||
apaPost('email_del', data, function(data){
|
||||
var rdata = $.parseJSON(data.data);
|
||||
showMsg(rdata.msg,function(){
|
||||
emailList();
|
||||
},{icon: rdata.status ? 1 : 2}, 600);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function emailAdd(type){
|
||||
layer.open({
|
||||
type: 1,
|
||||
area: '500px',
|
||||
title: '添加邮件地址',
|
||||
closeBtn: 1,
|
||||
shift: 5,
|
||||
shadeClose: true,
|
||||
btn:["提交","关闭"],
|
||||
content: "<form class='bt-form pd20' id='email_add'>\
|
||||
<div class='line'>\
|
||||
<span class='tname'>邮件地址</span>\
|
||||
<div class='info-r'><input name='addr' class='bt-input-text mr5' style='width:100%;' placeholder='邮件地址' type='text'></div>\
|
||||
</div>\
|
||||
<div class='line'>\
|
||||
<span class='tname'>备注</span>\
|
||||
<div class='info-r'><input name='remark' class='bt-input-text mr5' style='width:100%;' placeholder='备注' type='text'></div>\
|
||||
</div>\
|
||||
</form>",
|
||||
success:function(){
|
||||
$("input[name='addr']").keyup(function(){
|
||||
var v = $(this).val();
|
||||
$("input[name='remark']").val(v);
|
||||
});
|
||||
},
|
||||
yes:function(index) {
|
||||
var data = $("#email_add").serialize();
|
||||
data = decodeURIComponent(data);
|
||||
// data = toArrayObject(data);
|
||||
apaPost('email_add', data, function(data){
|
||||
var rdata = $.parseJSON(data.data);
|
||||
showMsg(rdata.msg,function(){
|
||||
if (rdata.status){
|
||||
layer.close(index);
|
||||
emailList();
|
||||
}
|
||||
},{icon: rdata.status ? 1 : 2}, 2000);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
function emailList(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;
|
||||
}
|
||||
|
||||
apaPost('email_list', _data, function(data){
|
||||
var rdata = $.parseJSON(data.data);
|
||||
var list = '';
|
||||
for(i in rdata.data){
|
||||
list += '<tr>';
|
||||
list +='<td><input value="'+rdata.data[i]['id']+'" class="check" onclick="checkSelect();" type="checkbox"></td>';
|
||||
list += '<td>' + rdata.data[i]['addr'] +'</td>';
|
||||
list += '<td>' + rdata.data[i]['remark'] +'</td>';
|
||||
list += '<td style="text-align:right">';
|
||||
list += '<a href="javascript:;" class="btlink" onclick="emailDel(\''+rdata.data[i]['id']+'\',\''+rdata.data[i]['addr']+'\')" title="删除">删除</a>' +
|
||||
'</td>';
|
||||
list += '</tr>';
|
||||
}
|
||||
|
||||
var con = '<div class="safe bgw">\
|
||||
<button onclick="emailAdd()" title="添加邮件地址" class="btn btn-success btn-sm" type="button" style="margin-right: 5px;">添加邮件地址</button>\
|
||||
<span style="float:right"> \
|
||||
<button batch="true" style="float: right;display: none;margin-left:10px;" onclick="delDbBatch();" title="删除选中项" class="btn btn-default btn-sm">删除选中</button>\
|
||||
</span>\
|
||||
<div class="divtable mtb10">\
|
||||
<div class="tablescroll">\
|
||||
<table id="DataBody" class="table table-hover" width="100%" cellspacing="0" cellpadding="0" border="0" style="border: 0 none;">\
|
||||
<thead><tr><th width="30"><input class="check" onclick="checkSelect();" type="checkbox"></th>\
|
||||
<th>邮件地址</th>\
|
||||
<th>备注</th>\
|
||||
<th style="text-align:right;">操作</th></tr></thead>\
|
||||
<tbody>'+ list +'</tbody>\
|
||||
</table>\
|
||||
</div>\
|
||||
<div class="dataTables_paginate paging_bootstrap page"></div>\
|
||||
</div>\
|
||||
</div>';
|
||||
|
||||
$(".soft-man-con").html(con);
|
||||
$('.dataTables_paginate').html(rdata.page);
|
||||
|
||||
readerTableChecked();
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
function dnsapiDel(id, name){
|
||||
safeMessage('删除['+name+']','您真的要删除['+name+']吗?',function(){
|
||||
var data='id='+id+'&name='+name;
|
||||
apaPost('dnsapi_del', data, function(data){
|
||||
var rdata = $.parseJSON(data.data);
|
||||
showMsg(rdata.msg,function(){
|
||||
dnsapiList();
|
||||
},{icon: rdata.status ? 1 : 2}, 600);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
var dnsapi_option = [
|
||||
{"name":"dns_cf", "title":'cloudflare', 'key':'CF_Key:CF_Email'},
|
||||
{"name":"dns_dp", "title":'dnspod/国内', 'key':'DP_Id:DP_Key'},
|
||||
{"name":"dns_dpi", "title":'dnspod/国际', 'key':'DPI_Id:DPI_Key'},
|
||||
{"name":"dns_gd", "title":'GoDaddy', 'key':'GD_Key:GD_Secret'},
|
||||
{"name":"dns_pdns", "title":'PowerDNS', 'key':'PDNS_Url:PDNS_ServerId:PDNS_Token:PDNS_Ttl'},
|
||||
{"name":"dns_lua", "title":'LuaDNS', 'key':'LUA_Key:LUA_Email'},
|
||||
{"name":"dns_me", "title":'DNSMadeEasy', 'key':'ME_Key:ME_Secret'},
|
||||
{"name":"dns_aws", "title":'Amazon Route53', 'key':'AWS_ACCESS_KEY_ID:AWS_SECRET_ACCESS_KEY'},
|
||||
{"name":"dns_ali", "title":'Aliyun', 'key':'Ali_Key:Ali_Secret'},
|
||||
{"name":"dns_ispconfig", "title":'ISPConfig', 'key':'ISPC_User:ISPC_Password:ISPC_Api:ISPC_Api_Insecure'},
|
||||
{"name":"dns_ad", "title":'Alwaysdata', 'key':'AD_API_KEY'},
|
||||
{"name":"dns_linode_v4", "title":'Linode', 'key':'LINODE_V4_API_KEY'},
|
||||
{"name":"dns_freedns", "title":'FreeDNS', 'key':'FREEDNS_User:FREEDNS_Password'},
|
||||
{"name":"dns_cyon", "title":'cyon.ch', 'key':'CY_Username:CY_Password:CY_OTP_Secret'},
|
||||
{"name":"dns_gandi_livedns", "title":'LiveDNS', 'key':'GANDI_LIVEDNS_TOKEN'},
|
||||
{"name":"dns_knot", "title":'Knot', 'key':'KNOT_SERVER:KNOT_KEY'},
|
||||
{"name":"dns_dgon", "title":'DigitalOcean', 'key':'DO_API_KEY'},
|
||||
{"name":"dns_cloudns", "title":'ClouDNS.net', 'key':'CLOUDNS_SUB_AUTH_ID:CLOUDNS_AUTH_PASSWORD'},
|
||||
{"name":"dns_namesilo", "title":'Namesilo', 'key':'Namesilo_Key'},
|
||||
{"name":"dns_azure", "title":'Azure', 'key':'AZUREDNS_SUBSCRIPTIONID:AZUREDNS_TENANTID:AZUREDNS_APPID:AZUREDNS_CLIENTSECRET'},
|
||||
{"name":"dns_selectel", "title":'selectel.com', 'key':'SL_Key'},
|
||||
{"name":"dns_zonomi", "title":'zonomi.com', 'key':'ZM_Key'},
|
||||
{"name":"dns_kinghost", "title":'KingHost', 'key':'KINGHOST_Username:KINGHOST_Password'},
|
||||
{"name":"dns_zilore", "title":'Zilore', 'key':'Zilore_Key'},
|
||||
{"name":"dns_gcloud", "title":'Google Cloud DNS', 'key':'CLOUDSDK_ACTIVE_CONFIG_NAME'},
|
||||
{"name":"dns_mydnsjp", "title":'MyDNS.JP', 'key':'MYDNSJP_MasterID:MYDNSJP_Password'},
|
||||
{"name":"dns_doapi", "title":'do.de', 'key':'DO_LETOKEN'},
|
||||
{"name":"dns_online", "title":'Online', 'key':'ONLINE_API_KEY'},
|
||||
{"name":"dns_cn", "title":'Core-Networks', 'key':'CN_User:CN_Password'},
|
||||
{"name":"dns_ultra", "title":'UltraDNS', 'key':'ULTRA_USR:ULTRA_PWD'},
|
||||
{"name":"dns_hetzner", "title":'Hetzner', 'key':'HETZNER_Token'},
|
||||
{"name":"dns_ddnss", "title":'DDNSS.de', 'key':'DDNSS_Token'},
|
||||
];
|
||||
|
||||
function getDnsapiKey(name){
|
||||
for (var i = 0; i < dnsapi_option.length; i++) {
|
||||
if (dnsapi_option[i]['name'] == name){
|
||||
return dnsapi_option[i]['key'];
|
||||
}
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
function getDnsapiTitle(name){
|
||||
for (var i = 0; i < dnsapi_option.length; i++) {
|
||||
if (dnsapi_option[i]['name'] == name){
|
||||
return dnsapi_option[i]['title'];
|
||||
}
|
||||
}
|
||||
return '其他';
|
||||
}
|
||||
|
||||
|
||||
|
||||
function dnsapiAdd(row){
|
||||
// console.log(row);
|
||||
var option_name = '';
|
||||
var option_remark = '';
|
||||
var option_type = 'dns_cf';
|
||||
var option_val = '';
|
||||
var option_id = 0;
|
||||
if (typeof(row) != 'undefined'){
|
||||
option_name = row['name'];
|
||||
option_remark = row['remark'];
|
||||
option_type = row['type'];
|
||||
option_val = row['val'];
|
||||
option_id = row['id'];
|
||||
|
||||
}
|
||||
|
||||
function renderDnsapiOption(name, val){
|
||||
var vlist = {};
|
||||
if (val != ''){
|
||||
var t = val.split('~');
|
||||
for (var i = 0; i < t.length; i++) {
|
||||
var kv = t[i].split('|');
|
||||
if (kv.length == 2){
|
||||
vlist[kv[0]] = kv[1];
|
||||
} else {
|
||||
vlist[kv[0]] = '';
|
||||
}
|
||||
}
|
||||
// console.log(vlist);
|
||||
}
|
||||
|
||||
|
||||
|
||||
var key = getDnsapiKey(name);
|
||||
var klist = key.split(':');
|
||||
var option_html = '';
|
||||
for (var i = 0; i < klist.length; i++) {
|
||||
var klist_val = '';
|
||||
if (klist[i] in vlist){
|
||||
klist_val = vlist[klist[i]];
|
||||
}
|
||||
option_html += "<span class='tname'>"+klist[i]+"</span>\
|
||||
<div class='info-r'>\
|
||||
<input name='"+klist[i]+"' class='bt-input-text mr5' style='width:100%;' value='"+klist_val+"' placeholder='请输入对应值' type='text'>\
|
||||
</div>";
|
||||
}
|
||||
$('#dnsapi_option').html(option_html);
|
||||
}
|
||||
|
||||
layer.open({
|
||||
type: 1,
|
||||
area: '500px',
|
||||
title: '添加DNSAPI',
|
||||
closeBtn: 1,
|
||||
shift: 5,
|
||||
shadeClose: true,
|
||||
btn:["提交","关闭"],
|
||||
content: "<form class='bt-form pd20' id='dnsapi_add'>\
|
||||
<div class='line'>\
|
||||
<span class='tname'>名称</span>\
|
||||
<div class='info-r'><input name='name' class='bt-input-text mr5' style='width:100%;' placeholder='名称' value='"+option_name+"' type='text'></div>\
|
||||
</div>\
|
||||
<div class='line'>\
|
||||
<span class='tname'>DNSAPI类型</span>\
|
||||
<div class='info-r'>\
|
||||
<select class='bt-input-text mr5' name='type'>\
|
||||
<option name='cf'>cloudflare</option>\
|
||||
</select>\
|
||||
</div>\
|
||||
</div>\
|
||||
<div class='line' id='dnsapi_option'>\
|
||||
<span class='tname'>CF_Key</span>\
|
||||
<div class='info-r'>\
|
||||
<input name='v1' class='bt-input-text mr5' style='width:100%;' placeholder='请输入对应值' type='text'>\
|
||||
</div>\
|
||||
<span class='tname'>CF_Email</span>\
|
||||
<div class='info-r'>\
|
||||
<input name='v2' class='bt-input-text mr5' style='width:100%;' placeholder='请输入对应值' type='text'>\
|
||||
</div>\
|
||||
</div>\
|
||||
<div class='line'>\
|
||||
<span class='tname'>备注</span>\
|
||||
<div class='info-r'><input name='remark' class='bt-input-text mr5' style='width:100%;' placeholder='备注' value='"+option_remark+"' type='text'></div>\
|
||||
</div>\
|
||||
<input name='id' value='"+option_id+"' type='hidden'>\
|
||||
</form>",
|
||||
success:function(){
|
||||
$("input[name='name']").keyup(function(){
|
||||
var v = $(this).val();
|
||||
$("input[name='remark']").val(v);
|
||||
});
|
||||
|
||||
var option = '';
|
||||
for (var i = 0; i<dnsapi_option.length; i++) {
|
||||
|
||||
if (dnsapi_option[i]['name'] == option_type){
|
||||
option += "<option value='"+dnsapi_option[i]['name']+"' key='"+dnsapi_option[i]['key']+"' selected>"+dnsapi_option[i]['title']+"</option>";
|
||||
} else {
|
||||
option += "<option value='"+dnsapi_option[i]['name']+"' key='"+dnsapi_option[i]['key']+"'>"+dnsapi_option[i]['title']+"</option>";
|
||||
}
|
||||
}
|
||||
|
||||
renderDnsapiOption(option_type, option_val);
|
||||
$('select[name="type"]').html(option);
|
||||
$('select[name="type"]').change(function(){
|
||||
var name = $(this).val();
|
||||
if (option_type == name){
|
||||
renderDnsapiOption(name, option_val);
|
||||
} else {
|
||||
renderDnsapiOption(name,'');
|
||||
}
|
||||
|
||||
});
|
||||
},
|
||||
yes:function(index) {
|
||||
var data = $("#dnsapi_add").serialize();
|
||||
data = decodeURIComponent(data);
|
||||
data = toArrayObject(data);
|
||||
|
||||
var key = getDnsapiKey(data['type']);
|
||||
var klist = key.split(':');
|
||||
var val = '';
|
||||
for (var i = 0; i < klist.length; i++) {
|
||||
var k = klist[i];
|
||||
if (k in data){
|
||||
if (klist.length - 1 == i){
|
||||
val += k + '|' + data[k];
|
||||
} else {
|
||||
val += k + '|' + data[k]+'~';
|
||||
}
|
||||
}
|
||||
}
|
||||
data['val'] = val;
|
||||
apaPost('dnsapi_add', data, function(data){
|
||||
var rdata = $.parseJSON(data.data);
|
||||
showMsg(rdata.msg,function(){
|
||||
if (rdata.status){
|
||||
layer.close(index);
|
||||
dnsapiList();
|
||||
}
|
||||
},{icon: rdata.status ? 1 : 2}, 2000);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
function dnsapiList(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;
|
||||
}
|
||||
|
||||
apaPost('dnsapi_list', _data, function(data){
|
||||
var rdata = $.parseJSON(data.data);
|
||||
var list = '';
|
||||
for(i in rdata.data){
|
||||
list += '<tr>';
|
||||
list +='<td><input value="'+rdata.data[i]['id']+'" class="check" onclick="checkSelect();" type="checkbox"></td>';
|
||||
list += '<td>' + rdata.data[i]['name'] +'</td>';
|
||||
list += '<td>' + getDnsapiTitle(rdata.data[i]['type']) +'</td>';
|
||||
list += '<td>' + rdata.data[i]['val'].split('~').join("<br/>") +'</td>';
|
||||
list += '<td>' + rdata.data[i]['remark'] +'</td>';
|
||||
|
||||
list += '<td style="text-align:right">';
|
||||
list += '<a href="javascript:;" index="'+i+'" class="btlink edit" title="编辑">编辑</a> | ';
|
||||
list += '<a href="javascript:;" class="btlink" onclick="dnsapiDel(\''+rdata.data[i]['id']+'\',\''+rdata.data[i]['name']+'\')" title="删除">删除</a>';
|
||||
list += '</td></tr>';
|
||||
}
|
||||
|
||||
var con = '<div class="safe bgw">\
|
||||
<button onclick="dnsapiAdd()" title="DNSAPI" class="btn btn-success btn-sm" type="button" style="margin-right: 5px;">添加DNSAPI</button>\
|
||||
<span style="float:right"> \
|
||||
<button batch="true" style="float: right;display: none;margin-left:10px;" onclick="delDbBatch();" title="删除选中项" class="btn btn-default btn-sm">删除选中</button>\
|
||||
</span>\
|
||||
<div class="divtable mtb10">\
|
||||
<div class="tablescroll">\
|
||||
<table id="DataBody" class="table table-hover" width="100%" cellspacing="0" cellpadding="0" border="0" style="border: 0 none;">\
|
||||
<thead><tr><th width="30"><input class="check" onclick="checkSelect();" type="checkbox"></th>\
|
||||
<th>名称</th>\
|
||||
<th>类型</th>\
|
||||
<th>值</th>\
|
||||
<th>备注</th>\
|
||||
<th style="text-align:right;">操作</th></tr></thead>\
|
||||
<tbody>'+ list +'</tbody>\
|
||||
</table>\
|
||||
</div>\
|
||||
<div class="dataTables_paginate paging_bootstrap page"></div>\
|
||||
</div>\
|
||||
</div>';
|
||||
|
||||
$(".soft-man-con").html(con);
|
||||
$('.dataTables_paginate').html(rdata.page);
|
||||
|
||||
$('.edit').click(function(){
|
||||
var idx = $(this).attr('index');
|
||||
var row = rdata.data[idx];
|
||||
// console.log(row);
|
||||
dnsapiAdd(row);
|
||||
})
|
||||
|
||||
readerTableChecked();
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
function domainDel(id, name){
|
||||
safeMessage('删除['+name+']','您真的要删除['+name+']吗?',function(){
|
||||
var data='id='+id+'&name='+name;
|
||||
apaPost('domain_del', data, function(data){
|
||||
var rdata = $.parseJSON(data.data);
|
||||
showMsg(rdata.msg,function(){
|
||||
domainList();
|
||||
},{icon: rdata.status ? 1 : 2}, 600);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function domainStatusToggle(id){
|
||||
var data='id='+id;
|
||||
apaPost('domain_status_toggle', data, function(data){
|
||||
var rdata = $.parseJSON(data.data);
|
||||
showMsg(rdata.msg,function(){
|
||||
domainList();
|
||||
},{icon: rdata.status ? 1 : 2}, 600);
|
||||
});
|
||||
}
|
||||
|
||||
function domainIdCmd(id){
|
||||
var data='id='+id;
|
||||
apaPost('get_run_hook_id_cmd', data, function(data){
|
||||
var rdata = $.parseJSON(data.data);
|
||||
// console.log(rdata);
|
||||
layer.open({
|
||||
title: "手动同步命令",
|
||||
area: ['600px', '180px'],
|
||||
type:1,
|
||||
closeBtn: 1,
|
||||
shadeClose: false,
|
||||
btn:["复制","取消"],
|
||||
content: '<div class="pd15">\
|
||||
<div class="divtable">\
|
||||
<pre class="layui-code">'+rdata.data+'</pre>\
|
||||
</div>\
|
||||
</div>',
|
||||
success:function(){
|
||||
copyText(rdata.data);
|
||||
},
|
||||
yes:function(){
|
||||
copyText(rdata.data);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function domainHookCmd(){
|
||||
apaPost('run_hook_cmd', {}, function(data){
|
||||
var rdata = $.parseJSON(data.data);
|
||||
layer.open({
|
||||
title: "手动同步全部命令",
|
||||
area: ['600px', '180px'],
|
||||
type:1,
|
||||
closeBtn: 1,
|
||||
shadeClose: false,
|
||||
btn:["复制","取消"],
|
||||
content: '<div class="pd15">\
|
||||
<div class="divtable">\
|
||||
<pre class="layui-code">'+rdata.data+'</pre>\
|
||||
</div>\
|
||||
</div>',
|
||||
success:function(){
|
||||
copyText(rdata.data);
|
||||
},
|
||||
yes:function(){
|
||||
copyText(rdata.data);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function syncCfCmd(){
|
||||
apaPost('run_sync_cf_cmd', {}, function(data){
|
||||
var rdata = $.parseJSON(data.data);
|
||||
layer.open({
|
||||
title: "手动同步CloudFlare全部域名命令",
|
||||
area: ['600px', '180px'],
|
||||
type:1,
|
||||
closeBtn: 1,
|
||||
shadeClose: false,
|
||||
btn:["复制","取消"],
|
||||
content: '<div class="pd15">\
|
||||
<div class="divtable">\
|
||||
<pre class="layui-code">'+rdata.data+'</pre>\
|
||||
</div>\
|
||||
</div>',
|
||||
success:function(){
|
||||
copyText(rdata.data);
|
||||
},
|
||||
yes:function(){
|
||||
copyText(rdata.data);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function syncDnsPodCmd(){
|
||||
apaPost('run_sync_dnspod_cmd', {}, function(data){
|
||||
var rdata = $.parseJSON(data.data);
|
||||
layer.open({
|
||||
title: "手动同步DnsPod全部域名命令",
|
||||
area: ['600px', '180px'],
|
||||
type:1,
|
||||
closeBtn: 1,
|
||||
shadeClose: false,
|
||||
btn:["复制","取消"],
|
||||
content: '<div class="pd15">\
|
||||
<div class="divtable">\
|
||||
<pre class="layui-code">'+rdata.data+'</pre>\
|
||||
</div>\
|
||||
</div>',
|
||||
success:function(){
|
||||
copyText(rdata.data);
|
||||
},
|
||||
yes:function(){
|
||||
copyText(rdata.data);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function domainAdd(row){
|
||||
|
||||
var option_domian = '';
|
||||
var option_remark = '备注';
|
||||
var option_email = '';
|
||||
var option_id = 0;
|
||||
var option_dnsapi_id = 0;
|
||||
if (typeof(row) != 'undefined'){
|
||||
option_domian = row['domain'];
|
||||
option_remark = row['remark'];
|
||||
option_email = row['email'];
|
||||
option_id = row['id'];
|
||||
option_dnsapi_id = row['dnsapi_id'];
|
||||
}
|
||||
|
||||
layer.open({
|
||||
type: 1,
|
||||
area: '500px',
|
||||
title: '添加顶级域名',
|
||||
closeBtn: 1,
|
||||
shift: 5,
|
||||
shadeClose: true,
|
||||
btn:["提交","关闭"],
|
||||
content: "<form class='bt-form pd20' id='domain_add'>\
|
||||
<div class='line'>\
|
||||
<span class='tname'>域名</span>\
|
||||
<div class='info-r'><input name='domain' class='bt-input-text mr5' style='width:100%;' value='"+option_domian+"' placeholder='域名' type='text'></div>\
|
||||
</div>\
|
||||
<div class='line'>\
|
||||
<span class='tname'>DNSAPI</span>\
|
||||
<div class='info-r'>\
|
||||
<select class='bt-input-text mr5' name='dnsapi_id'>\
|
||||
<option name='0'>无设置</option>\
|
||||
</select>\
|
||||
</div>\
|
||||
</div>\
|
||||
<div class='line'>\
|
||||
<span class='tname'>邮件</span>\
|
||||
<div class='info-r'><input name='email' class='bt-input-text mr5' style='width:100%;' value='"+option_email+"' placeholder='邮件' type='text'></div>\
|
||||
</div>\
|
||||
<div class='line'>\
|
||||
<span class='tname'>备注</span>\
|
||||
<div class='info-r'><input name='remark' class='bt-input-text mr5' style='width:100%;' value='"+option_remark+"' placeholder='备注' type='text'></div>\
|
||||
</div>\
|
||||
<input name='id' value='"+option_id+"' type='hidden'>\
|
||||
</form>",
|
||||
success:function(){
|
||||
// $("input[name='domain']").keyup(function(){
|
||||
// var v = $(this).val();
|
||||
// $("input[name='remark']").val(v);
|
||||
// });
|
||||
|
||||
var dnsapi_id_html = "<option value='0'>无设置</option>";
|
||||
apaPostN('dnsapi_list_all', {}, function(data){
|
||||
var rdata = $.parseJSON(data.data);
|
||||
for (var i = 0; i < rdata.length; i++) {
|
||||
if (option_dnsapi_id == rdata[i]['id']){
|
||||
dnsapi_id_html += "<option value='"+rdata[i]['id']+"' val='"+rdata[i]['val']+"' selected>"+rdata[i]['name']+"</option>";
|
||||
} else {
|
||||
dnsapi_id_html += "<option value='"+rdata[i]['id']+"' val='"+rdata[i]['val']+"'>"+rdata[i]['name']+"</option>";
|
||||
}
|
||||
}
|
||||
|
||||
$('select[name="dnsapi_id"]').change(function(){
|
||||
var val = $('select[name="dnsapi_id"]').find("option:selected").attr('val');
|
||||
var val_arr = val.split("|");
|
||||
for (var i = 0; i < val_arr.length; i++) {
|
||||
var param = val_arr[i];
|
||||
var strictEmailRegex = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
|
||||
if(strictEmailRegex.test(param)){
|
||||
$('input[name="email"]').val(param);
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
$('select[name="dnsapi_id"]').html(dnsapi_id_html);
|
||||
});
|
||||
},
|
||||
yes:function(index) {
|
||||
var data = $("#domain_add").serialize();
|
||||
data = decodeURIComponent(data);
|
||||
apaPost('domain_add', data, function(data){
|
||||
var rdata = $.parseJSON(data.data);
|
||||
showMsg(rdata.msg,function(){
|
||||
if (rdata.status){
|
||||
layer.close(index);
|
||||
domainList();
|
||||
}
|
||||
},{icon: rdata.status ? 1 : 2}, 2000);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function domainList(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;
|
||||
}
|
||||
|
||||
apaPost('domain_list', _data, function(data){
|
||||
var rdata = $.parseJSON(data.data);
|
||||
// console.log(rdata);
|
||||
var list = '';
|
||||
for(i in rdata.data){
|
||||
list += '<tr>';
|
||||
list +='<td><input value="'+rdata.data[i]['id']+'" class="check" onclick="checkSelect();" type="checkbox"></td>';
|
||||
list += '<td>' + rdata.data[i]['domain'] +'</td>';
|
||||
list += '<td>' + rdata.data[i]['dnsapi_id_alias'] +'</td>';
|
||||
list += '<td>' + rdata.data[i]['email'] +'</td>';
|
||||
list += '<td>' + rdata.data[i]['remark'] +'</td>';
|
||||
|
||||
|
||||
|
||||
if (rdata.data[i]['effective_date'] == ''){
|
||||
list += '<td>空/未申请</td>';
|
||||
} else {
|
||||
list += '<td>'+getFormatTime(rdata.data[i]['effective_date'],'yyyy/MM/dd')+'</td>';
|
||||
}
|
||||
|
||||
if (rdata.data[i]['expiration_date'] == ''){
|
||||
list += '<td>空/未申请</td>';
|
||||
} else {
|
||||
list += '<td>'+getFormatTime(rdata.data[i]['expiration_date'],'yyyy/MM/dd')+'</td>';
|
||||
}
|
||||
|
||||
if (rdata.data[i]['status'] == '0'){
|
||||
list += '<td><a href="javascript:;" index="'+i+'" class="btlink status">否</a></td>';
|
||||
} else {
|
||||
list += '<td><a href="javascript:;" index="'+i+'" class="btlink status">是</a></td>';
|
||||
}
|
||||
|
||||
list += '<td style="text-align:right">';
|
||||
list += '<a href="javascript:;" index="'+i+'" class="btlink cmd" title="命令">命令</a> | ';
|
||||
list += '<a href="javascript:;" index="'+i+'" class="btlink edit" title="编辑">编辑</a> | ';
|
||||
list += '<a href="javascript:;" class="btlink" onclick="domainDel(\''+rdata.data[i]['id']+'\',\''+rdata.data[i]['domain']+'\')" title="删除">删除</a>';
|
||||
list += '</td></tr>';
|
||||
}
|
||||
|
||||
var con = '<div class="safe bgw">\
|
||||
<button onclick="domainAdd()" title="添加顶级域名" class="btn btn-success btn-sm" type="button" style="margin-right: 5px;">添加域名</button>\
|
||||
<button onclick="domainHookCmd()" title="全部同步命令" class="btn btn-success btn-sm" type="button" style="margin-right: 5px;">全部同步命令</button>\
|
||||
<button onclick="syncCfCmd()" title="cloudflare同步命令" class="btn btn-success btn-sm" type="button" style="margin-right: 5px;">cloudflare同步命令</button>\
|
||||
<button onclick="syncDnsPodCmd()" title="DnsPod国际同步命令" class="btn btn-success btn-sm" type="button" style="margin-right: 5px;">DnsPod国际同步命令</button>\
|
||||
<div class="divtable mtb10">\
|
||||
<div class="tablescroll">\
|
||||
<table id="DataBody" class="table table-hover" width="100%" cellspacing="0" cellpadding="0" border="0" style="border: 0 none;">\
|
||||
<thead><tr><th width="30"><input class="check" onclick="checkSelect();" type="checkbox"></th>\
|
||||
<th>域名</th>\
|
||||
<th>DNSAPI</th>\
|
||||
<th>邮件</th>\
|
||||
<th>备注</th>\
|
||||
<th>开始</th>\
|
||||
<th>结束</th>\
|
||||
<th>同步</th>\
|
||||
<th style="text-align:right;">操作</th></tr></thead>\
|
||||
<tbody>'+ list +'</tbody>\
|
||||
</table>\
|
||||
</div>\
|
||||
<div class="dataTables_paginate paging_bootstrap page"></div>\
|
||||
</div>\
|
||||
</div>';
|
||||
|
||||
$(".soft-man-con").html(con);
|
||||
$('.dataTables_paginate').html(rdata.page);
|
||||
|
||||
$('.edit').click(function(){
|
||||
var idx = $(this).attr('index');
|
||||
var row = rdata.data[idx];
|
||||
domainAdd(row);
|
||||
});
|
||||
|
||||
$('.status').click(function(){
|
||||
var idx = $(this).attr('index');
|
||||
var row = rdata.data[idx];
|
||||
domainStatusToggle(row['id']);
|
||||
});
|
||||
|
||||
$('.cmd').click(function(){
|
||||
var idx = $(this).attr('index');
|
||||
var row = rdata.data[idx];
|
||||
domainIdCmd(row['id']);
|
||||
});
|
||||
|
||||
readerTableChecked();
|
||||
});
|
||||
}
|
||||
142
plugins/acme_pandominassl_apply/tool_cron.py
Normal file
142
plugins/acme_pandominassl_apply/tool_cron.py
Normal file
@@ -0,0 +1,142 @@
|
||||
# coding:utf-8
|
||||
|
||||
import sys
|
||||
import io
|
||||
import os
|
||||
import time
|
||||
import json
|
||||
|
||||
# print(sys.platform)
|
||||
if sys.platform != "darwin":
|
||||
os.chdir("/www/server/mdserver-web")
|
||||
|
||||
|
||||
web_dir = os.getcwd() + "/web"
|
||||
if os.path.exists(web_dir):
|
||||
sys.path.append(web_dir)
|
||||
os.chdir(web_dir)
|
||||
|
||||
import core.mw as mw
|
||||
from utils.crontab import crontab as MwCrontab
|
||||
|
||||
|
||||
app_debug = False
|
||||
if mw.isAppleSystem():
|
||||
app_debug = True
|
||||
|
||||
|
||||
def getPluginName():
|
||||
return 'acme_pandominassl_apply'
|
||||
|
||||
|
||||
def getPluginDir():
|
||||
return mw.getPluginDir() + '/' + getPluginName()
|
||||
|
||||
|
||||
def getServerDir():
|
||||
return mw.getServerDir() + '/' + getPluginName()
|
||||
|
||||
|
||||
def getTaskConf():
|
||||
conf = getServerDir() + "/cron_config.json"
|
||||
return conf
|
||||
|
||||
def getTaskDeltaConf():
|
||||
conf = getServerDir() + "/cron_delta_config.json"
|
||||
return conf
|
||||
|
||||
|
||||
def getConfigData():
|
||||
try:
|
||||
return json.loads(mw.readFile(getTaskConf()))
|
||||
except:
|
||||
pass
|
||||
return {
|
||||
"task_id": -1,
|
||||
"period": "day-n",
|
||||
"where1": "1",
|
||||
"hour": "0",
|
||||
"minute": "15",
|
||||
}
|
||||
|
||||
|
||||
def createBgTask():
|
||||
removeBgTask()
|
||||
createBgTaskByName(getPluginName())
|
||||
return True
|
||||
|
||||
|
||||
def createBgTaskByName(name):
|
||||
args = getConfigData()
|
||||
_name = "[勿删]ACME泛域名SSL[APA]"
|
||||
res = mw.M("crontab").field("id, name").where("name=?", (_name,)).find()
|
||||
if res:
|
||||
return True
|
||||
|
||||
if "task_id" in args and args["task_id"] > 0:
|
||||
res = mw.M("crontab").field("id, name").where(
|
||||
"id=?", (args["task_id"],)).find()
|
||||
if res and res["id"] == args["task_id"]:
|
||||
print("计划任务已经存在!")
|
||||
return True
|
||||
|
||||
mw_dir = mw.getPanelDir()
|
||||
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 run_hook"' + "\n"
|
||||
cmd += 'cd $mw_dir && python3 $script_path/index.py run_hook' + "\n"
|
||||
cmd += 'echo "【`date +"%Y-%m-%d %H:%M:%S"`】 END★" >> $logs_file' + "\n"
|
||||
cmd += 'echo "<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<" >> $logs_file' + "\n"
|
||||
|
||||
params = {
|
||||
'name': _name,
|
||||
'type': args['period'],
|
||||
'week': "",
|
||||
'where1': args['where1'],
|
||||
'hour': args['hour'],
|
||||
'minute': args['minute'],
|
||||
'save': "",
|
||||
'backup_to': "",
|
||||
'stype': "toShell",
|
||||
'sname': '',
|
||||
'sbody': cmd,
|
||||
'url_address': '',
|
||||
}
|
||||
|
||||
task_id = MwCrontab.instance().add(params)
|
||||
if task_id > 0:
|
||||
args["task_id"] = task_id
|
||||
args["name"] = name
|
||||
mw.writeFile(getTaskConf(), json.dumps(args))
|
||||
|
||||
|
||||
def removeBgTask():
|
||||
cfg = getConfigData()
|
||||
if "task_id" in cfg 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
|
||||
mw.writeFile(getTaskConf(), json.dumps(cfg))
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
if len(sys.argv) > 1:
|
||||
action = sys.argv[1]
|
||||
if action == "remove":
|
||||
removeBgTask()
|
||||
elif action == "add":
|
||||
createBgTask()
|
||||
BIN
plugins/alist/ico.png
Normal file
BIN
plugins/alist/ico.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 42 KiB |
31
plugins/alist/index.html
Executable file
31
plugins/alist/index.html
Executable file
@@ -0,0 +1,31 @@
|
||||
<style>
|
||||
.overflow_hide {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
vertical-align: middle;
|
||||
}
|
||||
</style>
|
||||
|
||||
<div class="bt-form">
|
||||
<div class='plugin_version'></div>
|
||||
<div class="bt-w-main">
|
||||
<div class="bt-w-menu">
|
||||
<p class="bgw" onclick="pluginService('alist');">服务</p>
|
||||
<p onclick="pluginInitD('alist');">自启动</p>
|
||||
<p onclick="alistCommonFunc();">常用功能</p>
|
||||
<p onclick="pluginConfigTpl('alist',$('.plugin_version').attr('version'));">配置修改</p>
|
||||
<p onclick="pluginLogs('alist','','run_log');">运行日志</p>
|
||||
<p onclick="alistReadme();">相关说明</p>
|
||||
|
||||
</div>
|
||||
<div class="bt-w-con pd15">
|
||||
<div class="soft-man-con" style="height: 520px; overflow: auto;"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script type="text/javascript">
|
||||
$.getScript( "/plugins/file?name=alist&f=js/alist.js", function(){
|
||||
pluginService('alist', $('.plugin_version').attr('version'));
|
||||
});
|
||||
</script>
|
||||
319
plugins/alist/index.py
Executable file
319
plugins/alist/index.py
Executable file
@@ -0,0 +1,319 @@
|
||||
# coding:utf-8
|
||||
|
||||
import sys
|
||||
import io
|
||||
import os
|
||||
import time
|
||||
import re
|
||||
import json
|
||||
|
||||
web_dir = os.getcwd() + "/web"
|
||||
if os.path.exists(web_dir):
|
||||
sys.path.append(web_dir)
|
||||
os.chdir(web_dir)
|
||||
|
||||
import core.mw as mw
|
||||
|
||||
app_debug = False
|
||||
if mw.isAppleSystem():
|
||||
app_debug = True
|
||||
|
||||
|
||||
def getPluginName():
|
||||
return 'alist'
|
||||
|
||||
|
||||
def getPluginDir():
|
||||
return mw.getPluginDir() + '/' + getPluginName()
|
||||
|
||||
|
||||
def getServerDir():
|
||||
return mw.getServerDir() + '/' + getPluginName()
|
||||
|
||||
|
||||
def getInitDFile():
|
||||
current_os = mw.getOs()
|
||||
if current_os == 'darwin':
|
||||
return '/tmp/' + getPluginName()
|
||||
|
||||
if current_os.startswith('freebsd'):
|
||||
return '/etc/rc.d/' + getPluginName()
|
||||
|
||||
return '/etc/init.d/' + getPluginName()
|
||||
|
||||
|
||||
def getInitDTpl():
|
||||
path = getPluginDir() + "/init.d/" + getPluginName() + ".tpl"
|
||||
return path
|
||||
|
||||
|
||||
def getArgs():
|
||||
args = sys.argv[3:]
|
||||
tmp = {}
|
||||
args_len = len(args)
|
||||
|
||||
if args_len == 1:
|
||||
t = args[0].strip('{').strip('}')
|
||||
if t.strip() == '':
|
||||
tmp = []
|
||||
else:
|
||||
t = t.split(':')
|
||||
tmp[t[0]] = t[1]
|
||||
tmp[t[0]] = t[1]
|
||||
elif args_len > 1:
|
||||
for i in range(len(args)):
|
||||
t = args[i].split(':')
|
||||
tmp[t[0]] = t[1]
|
||||
return tmp
|
||||
|
||||
def checkArgs(data, ck=[]):
|
||||
for i in range(len(ck)):
|
||||
if not ck[i] in data:
|
||||
return (False, mw.returnJson(False, '参数:(' + ck[i] + ')没有!'))
|
||||
return (True, mw.returnJson(True, 'ok'))
|
||||
|
||||
|
||||
def getConf():
|
||||
path = getServerDir() + "/data/config.json"
|
||||
return path
|
||||
|
||||
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 __release_port(port, ps = '开启端口'):
|
||||
try:
|
||||
from utils.firewall import Firewall as MwFirewall
|
||||
MwFirewall.instance().addAcceptPort(port, ps, 'port')
|
||||
return port
|
||||
except Exception as e:
|
||||
return "Release failed {}".format(e)
|
||||
|
||||
def __delete_port(port):
|
||||
try:
|
||||
from utils.firewall import Firewall as MwFirewall
|
||||
MwFirewall.instance().delAcceptPortCmd(port, 'tcp')
|
||||
return port
|
||||
except Exception as e:
|
||||
return "Delete failed {}".format(e)
|
||||
|
||||
|
||||
def openPort():
|
||||
content = mw.readFile(getConf())
|
||||
data = json.loads(content)
|
||||
http_port = data['scheme']['http_port']
|
||||
__release_port(http_port, 'alist')
|
||||
return True
|
||||
|
||||
def status():
|
||||
cmd = "ps -ef|grep alist |grep -v grep | grep -v python | grep -v mdserver-web | awk '{print $2}'"
|
||||
data = mw.execShell(cmd)
|
||||
if data[0] == '':
|
||||
return 'stop'
|
||||
return 'start'
|
||||
|
||||
def contentReplace(content):
|
||||
service_path = mw.getServerDir()
|
||||
content = content.replace('{$ROOT_PATH}', mw.getFatherDir())
|
||||
content = content.replace('{$SERVER_PATH}', service_path)
|
||||
content = content.replace('{$SERVER_APP}', service_path + '/'+getPluginName())
|
||||
return content
|
||||
|
||||
def initDreplace():
|
||||
|
||||
file_tpl = getInitDTpl()
|
||||
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()
|
||||
|
||||
# initd replace
|
||||
if not os.path.exists(file_bin):
|
||||
content = mw.readFile(file_tpl)
|
||||
content = content.replace('{$SERVER_PATH}', service_path)
|
||||
mw.writeFile(file_bin, content)
|
||||
mw.execShell('chmod +x ' + file_bin)
|
||||
|
||||
|
||||
# systemd
|
||||
systemDir = mw.systemdCfgDir()
|
||||
systemService = systemDir + '/' + getPluginName() + '.service'
|
||||
if os.path.exists(systemDir) and not os.path.exists(systemService):
|
||||
systemServiceTpl = getPluginDir() + '/init.d/' + getPluginName() + '.service.tpl'
|
||||
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')
|
||||
time.sleep(1)
|
||||
return file_bin
|
||||
|
||||
|
||||
def alistOp(method):
|
||||
file = initDreplace()
|
||||
|
||||
current_os = mw.getOs()
|
||||
if current_os == "darwin":
|
||||
data = mw.execShell(file + ' ' + method)
|
||||
if data[1] == '':
|
||||
return 'ok'
|
||||
return data[1]
|
||||
|
||||
if current_os.startswith("freebsd"):
|
||||
data = mw.execShell('service ' + getPluginName() + ' ' + method)
|
||||
if data[1] == '':
|
||||
return 'ok'
|
||||
return data[1]
|
||||
|
||||
data = mw.execShell('systemctl ' + method + ' ' + getPluginName())
|
||||
if data[1] == '':
|
||||
return 'ok'
|
||||
return data[1]
|
||||
|
||||
|
||||
def start():
|
||||
return alistOp('start')
|
||||
|
||||
|
||||
def stop():
|
||||
return alistOp('stop')
|
||||
|
||||
|
||||
def restart():
|
||||
status = alistOp('restart')
|
||||
return status
|
||||
|
||||
|
||||
def reload():
|
||||
return alistOp('reload')
|
||||
|
||||
|
||||
def initdStatus():
|
||||
current_os = mw.getOs()
|
||||
if current_os == 'darwin':
|
||||
return "Apple Computer does not support"
|
||||
|
||||
if current_os.startswith('freebsd'):
|
||||
initd_bin = getInitDFile()
|
||||
if os.path.exists(initd_bin):
|
||||
return 'ok'
|
||||
|
||||
shell_cmd = 'systemctl status ' + \
|
||||
getPluginName() + ' | grep loaded | grep "enabled;"'
|
||||
data = mw.execShell(shell_cmd)
|
||||
if data[0] == '':
|
||||
return 'fail'
|
||||
return 'ok'
|
||||
|
||||
|
||||
def initdInstall():
|
||||
current_os = mw.getOs()
|
||||
if current_os == 'darwin':
|
||||
return "Apple Computer does not support"
|
||||
|
||||
# freebsd initd install
|
||||
if current_os.startswith('freebsd'):
|
||||
import shutil
|
||||
source_bin = initDreplace()
|
||||
initd_bin = getInitDFile()
|
||||
shutil.copyfile(source_bin, initd_bin)
|
||||
mw.execShell('chmod +x ' + initd_bin)
|
||||
mw.execShell('sysrc ' + getPluginName() + '_enable="YES"')
|
||||
return 'ok'
|
||||
|
||||
mw.execShell('systemctl enable ' + getPluginName())
|
||||
return 'ok'
|
||||
|
||||
|
||||
def initdUinstall():
|
||||
current_os = mw.getOs()
|
||||
if current_os == 'darwin':
|
||||
return "Apple Computer does not support"
|
||||
|
||||
if current_os.startswith('freebsd'):
|
||||
initd_bin = getInitDFile()
|
||||
os.remove(initd_bin)
|
||||
mw.execShell('sysrc ' + getPluginName() + '_enable="NO"')
|
||||
return 'ok'
|
||||
|
||||
mw.execShell('systemctl disable ' + getPluginName())
|
||||
return 'ok'
|
||||
|
||||
|
||||
def runLog():
|
||||
return getServerDir() + '/data/log/log.log'
|
||||
|
||||
def pSqliteDb(dbname='databases'):
|
||||
pos_file = getServerDir() + '/data/'
|
||||
file = pos_file + '/data.db'
|
||||
name = 'data'
|
||||
conn = mw.M(dbname).dbPos(pos_file, name)
|
||||
return conn
|
||||
|
||||
def clearCopyTask():
|
||||
conn = pSqliteDb('x_task_items')
|
||||
conn.where('key=?', ('copy',)).setField('persist_data','[]')
|
||||
restart()
|
||||
return mw.returnJson(True, '清空成功并重启服务!')
|
||||
|
||||
def homePage():
|
||||
content = mw.readFile(getConf())
|
||||
data = json.loads(content)
|
||||
http_port = data['scheme']['http_port']
|
||||
ip = mw.getLocalIp()
|
||||
if mw.isAppleSystem():
|
||||
ip = '127.0.0.1'
|
||||
url = 'http://'+ip+":"+str(http_port)
|
||||
# print(url)
|
||||
return mw.returnJson(True, 'ok!', url)
|
||||
|
||||
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 == 'run_log':
|
||||
print(runLog())
|
||||
elif func == 'config_tpl':
|
||||
print(configTpl())
|
||||
elif func == 'read_config_tpl':
|
||||
print(readConfigTpl())
|
||||
elif func == 'clear_copy_task':
|
||||
print(clearCopyTask())
|
||||
elif func == 'home_page':
|
||||
print(homePage())
|
||||
else:
|
||||
print('error')
|
||||
17
plugins/alist/info.json
Executable file
17
plugins/alist/info.json
Executable file
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"sort": 7,
|
||||
"ps": "一个支持多种存储的文件列表程序",
|
||||
"name": "alist",
|
||||
"title": "Alist",
|
||||
"shell": "install.sh",
|
||||
"versions":["3.37.4","3.38.0","3.45.0"],
|
||||
"tip": "soft",
|
||||
"checks": "server/alist",
|
||||
"path": "server/alist",
|
||||
"display": 1,
|
||||
"author": "alist",
|
||||
"date": "2022-10-09",
|
||||
"home": "https://alist.nn.ci",
|
||||
"type": 0,
|
||||
"pid": "5"
|
||||
}
|
||||
13
plugins/alist/init.d/alist.service.tpl
Normal file
13
plugins/alist/init.d/alist.service.tpl
Normal file
@@ -0,0 +1,13 @@
|
||||
[Unit]
|
||||
Description=A file list program that supports multiple storage
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
WorkingDirectory={$SERVER_PATH}/alist
|
||||
ExecStart={$SERVER_PATH}/alist/alist server
|
||||
ExecReload=/bin/kill -USR2 $MAINPID
|
||||
Restart=on-failure
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
46
plugins/alist/init.d/alist.tpl
Normal file
46
plugins/alist/init.d/alist.tpl
Normal file
@@ -0,0 +1,46 @@
|
||||
#!/bin/sh
|
||||
# chkconfig: 2345 55 25
|
||||
# description: alist Service
|
||||
|
||||
### BEGIN INIT INFO
|
||||
# Provides: alist
|
||||
# Required-Start: $all
|
||||
# Required-Stop: $all
|
||||
# Default-Start: 2 3 4 5
|
||||
# Default-Stop: 0 1 6
|
||||
# Short-Description: starts alist
|
||||
# Description: starts the MDW-Web
|
||||
### END INIT INFO
|
||||
|
||||
# Simple alist init.d script conceived to work on Linux systems
|
||||
# as it does use of the /proc filesystem.
|
||||
|
||||
app_start(){
|
||||
echo "Starting alist server..."
|
||||
cd {$SERVER_PATH}/alist
|
||||
./alist server >> {$SERVER_PATH}/alist/logs.pl 2>&1 &
|
||||
}
|
||||
|
||||
app_stop(){
|
||||
echo "dztasks stopped"
|
||||
ps -ef| grep alist | grep -v grep | grep -v python | grep -v sh | awk '{print $2}'| xargs kill -9
|
||||
}
|
||||
|
||||
|
||||
case "$1" in
|
||||
start)
|
||||
app_start
|
||||
;;
|
||||
stop)
|
||||
app_stop
|
||||
;;
|
||||
restart|reload)
|
||||
app_stop
|
||||
sleep 0.3
|
||||
app_start
|
||||
;;
|
||||
*)
|
||||
echo "Please use start or stop as first argument"
|
||||
;;
|
||||
esac
|
||||
|
||||
93
plugins/alist/install.sh
Executable file
93
plugins/alist/install.sh
Executable file
@@ -0,0 +1,93 @@
|
||||
#!/bin/bash
|
||||
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin:/opt/homebrew/bin
|
||||
export PATH
|
||||
|
||||
curPath=`pwd`
|
||||
rootPath=$(dirname "$curPath")
|
||||
rootPath=$(dirname "$rootPath")
|
||||
serverPath=$(dirname "$rootPath")
|
||||
|
||||
# cd /Users/midoks/Desktop/mwdev/server/mdserver-web/plugins/alist && bash install.sh install 3.37.4
|
||||
# cd /www/server/mdserver-web/plugins/alist && bash install.sh install 3.45.0
|
||||
|
||||
VERSION=$2
|
||||
|
||||
sysArch=`arch`
|
||||
sysName=`uname`
|
||||
|
||||
ALIST_ARCH_NAME=amd64
|
||||
if [ "$sysArch" = "arm64" ];then
|
||||
ALIST_ARCH_NAME=arm64
|
||||
elif [ "$sysArch" = "x86_64" ]; then
|
||||
ALIST_ARCH_NAME=amd64
|
||||
elif [ "$sysArch" = "aarch64" ]; then
|
||||
ALIST_ARCH_NAME=arm64
|
||||
fi
|
||||
|
||||
ALIST_NAME=linux
|
||||
if [ "$sysName" = "Darwin" ];then
|
||||
ALIST_NAME=darwin
|
||||
fi
|
||||
|
||||
Install_App()
|
||||
{
|
||||
echo '正在安装脚本文件...'
|
||||
|
||||
mkdir -p $serverPath/source
|
||||
mkdir -p $serverPath/source/alist
|
||||
|
||||
FILE_TGZ=alist-${ALIST_NAME}-${ALIST_ARCH_NAME}.tar.gz
|
||||
|
||||
ALIST_DIR=$serverPath/source/alist
|
||||
|
||||
if [ ! -f $ALIST_DIR/${FILE_TGZ} ];then
|
||||
wget --no-check-certificate -O $ALIST_DIR/${FILE_TGZ} https://github.com/alist-org/alist/releases/download/v${VERSION}/${FILE_TGZ}
|
||||
fi
|
||||
|
||||
mkdir -p $serverPath/alist
|
||||
|
||||
cd $ALIST_DIR && tar -zxvf ${FILE_TGZ} -C $serverPath/alist
|
||||
echo "${VERSION}" > $serverPath/alist/version.pl
|
||||
|
||||
cd ${rootPath} && python3 ${rootPath}/plugins/alist/index.py start
|
||||
cd ${rootPath} && python3 ${rootPath}/plugins/alist/index.py initd_install
|
||||
|
||||
cd $serverPath/alist && ./alist admin set admin
|
||||
# cd /www/server/alist && ./alist admin set admin
|
||||
|
||||
echo '安装完成'
|
||||
}
|
||||
|
||||
Uninstall_App()
|
||||
{
|
||||
if [ -f /usr/lib/systemd/system/alist.service ];then
|
||||
systemctl stop alist
|
||||
systemctl disable alist
|
||||
rm -rf /usr/lib/systemd/system/alist.service
|
||||
systemctl daemon-reload
|
||||
fi
|
||||
|
||||
if [ -f /lib/systemd/system/alist.service ];then
|
||||
systemctl stop alist
|
||||
systemctl disable alist
|
||||
rm -rf /lib/systemd/system/alist.service
|
||||
systemctl daemon-reload
|
||||
fi
|
||||
|
||||
if [ -f $serverPath/alist/initd/alist ];then
|
||||
$serverPath/alist/initd/alist stop
|
||||
fi
|
||||
|
||||
if [ -d $serverPath/alist ];then
|
||||
rm -rf $serverPath/alist
|
||||
fi
|
||||
|
||||
echo "卸载alist成功"
|
||||
}
|
||||
|
||||
action=$1
|
||||
if [ "${1}" = 'install' ];then
|
||||
Install_App
|
||||
else
|
||||
Uninstall_App
|
||||
fi
|
||||
93
plugins/alist/js/alist.js
Executable file
93
plugins/alist/js/alist.js
Executable file
@@ -0,0 +1,93 @@
|
||||
function alistPost(method, version, args,callback){
|
||||
var loadT = layer.msg('正在获取...', { icon: 16, time: 0, shade: 0.3 });
|
||||
|
||||
var req_data = {};
|
||||
req_data['name'] = 'alist';
|
||||
req_data['func'] = method;
|
||||
req_data['version'] = version;
|
||||
|
||||
if (typeof(args) == 'string'){
|
||||
req_data['args'] = JSON.stringify(toArrayObject(args));
|
||||
} else {
|
||||
req_data['args'] = JSON.stringify(args);
|
||||
}
|
||||
|
||||
$.post('/plugins/run', req_data, function(data) {
|
||||
layer.close(loadT);
|
||||
if (!data.status){
|
||||
//错误展示10S
|
||||
layer.msg(data.msg,{icon:0,time:2000,shade: [10, '#000']});
|
||||
return;
|
||||
}
|
||||
|
||||
if(typeof(callback) == 'function'){
|
||||
callback(data);
|
||||
}
|
||||
},'json');
|
||||
}
|
||||
|
||||
function alistPostCallbak(method, version, args,callback){
|
||||
var loadT = layer.msg('正在获取...', { icon: 16, time: 0, shade: 0.3 });
|
||||
|
||||
var req_data = {};
|
||||
req_data['name'] = 'alist';
|
||||
req_data['func'] = method;
|
||||
args['version'] = version;
|
||||
|
||||
if (typeof(args) == 'string'){
|
||||
req_data['args'] = JSON.stringify(toArrayObject(args));
|
||||
} else {
|
||||
req_data['args'] = JSON.stringify(args);
|
||||
}
|
||||
|
||||
$.post('/plugins/callback', req_data, function(data) {
|
||||
layer.close(loadT);
|
||||
if (!data.status){
|
||||
layer.msg(data.msg,{icon:0,time:2000,shade: [0.3, '#000']});
|
||||
return;
|
||||
}
|
||||
|
||||
if(typeof(callback) == 'function'){
|
||||
callback(data);
|
||||
}
|
||||
},'json');
|
||||
}
|
||||
|
||||
function clearTaskCopy(){
|
||||
layer.confirm('您真的要清空复制任务吗?', { icon: 3, closeBtn: 2 }, function() {
|
||||
alistPost('clear_copy_task', '', {}, function(data){
|
||||
var rdata = $.parseJSON(data.data);
|
||||
showMsg(rdata.msg, function(){},{ icon: rdata.status ? 1 : 2 });
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function commonHomePage(){
|
||||
alistPost('home_page', '', {}, function(data){
|
||||
var rdata = $.parseJSON(data.data);
|
||||
window.open(rdata.data);
|
||||
});
|
||||
}
|
||||
|
||||
function alistCommonFunc(){
|
||||
var con = '';
|
||||
con += '<hr/><p class="conf_p" style="text-align:center;">\
|
||||
<button class="btn btn-default btn-sm" onclick="commonHomePage()">主页</button>\
|
||||
<button class="btn btn-default btn-sm" onclick="clearTaskCopy()">清空复制任务</button>\
|
||||
</p>';
|
||||
|
||||
$(".soft-man-con").html(con);
|
||||
}
|
||||
|
||||
function alistReadme(){
|
||||
|
||||
|
||||
var readme = '<ul class="help-info-text c7">';
|
||||
readme += '<li>手动开启默认端口:5244</li>';
|
||||
readme += '<li>默认admin:admin</li>';
|
||||
readme += '<li>手动改密码: cd /www/server/alist && ./alist admin set NEW_PASSWORD</li>';
|
||||
readme += '</ul>';
|
||||
|
||||
$('.soft-man-con').html(readme);
|
||||
}
|
||||
|
||||
35
plugins/apache/check.sh
Executable file
35
plugins/apache/check.sh
Executable file
@@ -0,0 +1,35 @@
|
||||
#!/bin/bash
|
||||
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin:/opt/homebrew/bin
|
||||
export PATH
|
||||
|
||||
# OpenResty服务名称
|
||||
service_name="openresty"
|
||||
|
||||
# 检查OpenResty是否正在运行
|
||||
if systemctl is-active --quiet "$service_name"; then
|
||||
# 检查是否存在僵尸进程
|
||||
zombie_processes=$(ps -ef | grep -i openresty | grep -v grep | awk '{print $2}' | xargs ps -o state= -p 2>/dev/null | grep -c Z)
|
||||
if [ "$zombie_processes" -gt 0 ]; then
|
||||
echo "kill nginx 僵尸进程"
|
||||
ps -ef|grep nginx| grep -v grep| awk '{print $2}' | xargs kill -9
|
||||
echo "检测到OpenResty僵尸进程,正在重启服务..."
|
||||
systemctl restart "$service_name"
|
||||
echo "服务已重启"
|
||||
else
|
||||
echo "OpenResty运行正常"
|
||||
fi
|
||||
else
|
||||
echo "kill nginx"
|
||||
ps -ef|grep nginx| grep -v grep| awk '{print $2}' | xargs kill -9
|
||||
echo "OpenResty未运行,正在启动服务..."
|
||||
systemctl start "$service_name"
|
||||
echo "服务已启动"
|
||||
fi
|
||||
|
||||
NGINX_IDS=`ps -ef|grep nginx | grep -v grep| awk '{print $2}'`
|
||||
if [ "$NGINX_IDS" = "" ];then
|
||||
ps -ef|grep nginx| grep -v grep| awk '{print $2}' | xargs kill -9
|
||||
systemctl start "$service_name"
|
||||
echo "OpenResty未运行,正在启动服务..."
|
||||
fi
|
||||
|
||||
57
plugins/apache/conf/httpd-mpm.conf
Normal file
57
plugins/apache/conf/httpd-mpm.conf
Normal file
@@ -0,0 +1,57 @@
|
||||
<IfModule !mpm_netware_module>
|
||||
PidFile "logs/httpd.pid"
|
||||
</IfModule>
|
||||
|
||||
<IfModule mpm_prefork_module>
|
||||
StartServers 5
|
||||
MinSpareServers 5
|
||||
MaxSpareServers 10
|
||||
MaxRequestWorkers 250
|
||||
MaxConnectionsPerChild 0
|
||||
</IfModule>
|
||||
|
||||
<IfModule mpm_worker_module>
|
||||
StartServers 3
|
||||
MinSpareThreads 75
|
||||
MaxSpareThreads 250
|
||||
ThreadsPerChild 25
|
||||
MaxRequestWorkers 400
|
||||
MaxConnectionsPerChild 0
|
||||
</IfModule>
|
||||
|
||||
<IfModule mpm_event_module>
|
||||
StartServers 3
|
||||
MinSpareThreads 75
|
||||
MaxSpareThreads 250
|
||||
ThreadsPerChild 25
|
||||
MaxRequestWorkers 400
|
||||
MaxConnectionsPerChild 0
|
||||
</IfModule>
|
||||
|
||||
<IfModule mpm_netware_module>
|
||||
ThreadStackSize 65536
|
||||
StartThreads 250
|
||||
MinSpareThreads 25
|
||||
MaxSpareThreads 250
|
||||
MaxThreads 1000
|
||||
MaxConnectionsPerChild 0
|
||||
</IfModule>
|
||||
|
||||
<IfModule mpm_mpmt_os2_module>
|
||||
StartServers 2
|
||||
MinSpareThreads 5
|
||||
MaxSpareThreads 10
|
||||
MaxConnectionsPerChild 0
|
||||
</IfModule>
|
||||
|
||||
<IfModule mpm_winnt_module>
|
||||
ThreadsPerChild 150
|
||||
MaxConnectionsPerChild 0
|
||||
</IfModule>
|
||||
|
||||
<IfModule !mpm_netware_module>
|
||||
MaxMemFree 2048
|
||||
</IfModule>
|
||||
<IfModule mpm_netware_module>
|
||||
MaxMemFree 100
|
||||
</IfModule>
|
||||
105
plugins/apache/conf/httpd.conf
Normal file
105
plugins/apache/conf/httpd.conf
Normal file
@@ -0,0 +1,105 @@
|
||||
ServerRoot "{$SERVER_PATH}/apache/httpd"
|
||||
Listen 8280
|
||||
|
||||
LoadModule authn_file_module modules/mod_authn_file.so
|
||||
LoadModule authn_core_module modules/mod_authn_core.so
|
||||
LoadModule authz_host_module modules/mod_authz_host.so
|
||||
LoadModule authz_groupfile_module modules/mod_authz_groupfile.so
|
||||
LoadModule authz_user_module modules/mod_authz_user.so
|
||||
LoadModule authz_core_module modules/mod_authz_core.so
|
||||
LoadModule access_compat_module modules/mod_access_compat.so
|
||||
LoadModule auth_basic_module modules/mod_auth_basic.so
|
||||
LoadModule reqtimeout_module modules/mod_reqtimeout.so
|
||||
LoadModule filter_module modules/mod_filter.so
|
||||
LoadModule brotli_module modules/mod_brotli.so
|
||||
LoadModule mime_module modules/mod_mime.so
|
||||
LoadModule log_config_module modules/mod_log_config.so
|
||||
LoadModule env_module modules/mod_env.so
|
||||
LoadModule expires_module modules/mod_expires.so
|
||||
LoadModule headers_module modules/mod_headers.so
|
||||
LoadModule unique_id_module modules/mod_unique_id.so
|
||||
LoadModule setenvif_module modules/mod_setenvif.so
|
||||
LoadModule version_module modules/mod_version.so
|
||||
LoadModule unixd_module modules/mod_unixd.so
|
||||
LoadModule status_module modules/mod_status.so
|
||||
LoadModule dir_module modules/mod_dir.so
|
||||
LoadModule alias_module modules/mod_alias.so
|
||||
LoadModule rewrite_module modules/mod_rewrite.so
|
||||
|
||||
<IfModule unixd_module>
|
||||
User daemon
|
||||
Group daemon
|
||||
</IfModule>
|
||||
|
||||
ServerAdmin you@example.com
|
||||
ServerName localhost:8280
|
||||
|
||||
<Directory />
|
||||
AllowOverride none
|
||||
Require all denied
|
||||
</Directory>
|
||||
|
||||
DocumentRoot "{$SERVER_PATH}/apache/httpd/htdocs"
|
||||
<Directory "{$SERVER_PATH}/apache/httpd/htdocs">
|
||||
Options Indexes FollowSymLinks
|
||||
AllowOverride None
|
||||
Require all granted
|
||||
</Directory>
|
||||
|
||||
<IfModule dir_module>
|
||||
DirectoryIndex index.html
|
||||
</IfModule>
|
||||
|
||||
<Files ".ht*">
|
||||
Require all denied
|
||||
</Files>
|
||||
|
||||
ErrorLog "logs/error.log"
|
||||
LogLevel warn
|
||||
|
||||
<IfModule log_config_module>
|
||||
LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" combined
|
||||
LogFormat "%h %l %u %t \"%r\" %>s %b" common
|
||||
|
||||
<IfModule logio_module>
|
||||
# You need to enable mod_logio.c to use %I and %O
|
||||
LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\" %I %O" combinedio
|
||||
</IfModule>
|
||||
|
||||
CustomLog "logs/access.log" common
|
||||
</IfModule>
|
||||
|
||||
<IfModule alias_module>
|
||||
ScriptAlias /cgi-bin/ "{$SERVER_PATH}/apache/httpd/cgi-bin/"
|
||||
</IfModule>
|
||||
|
||||
<IfModule cgid_module>
|
||||
</IfModule>
|
||||
|
||||
<Directory "{$SERVER_PATH}/apache/httpd/cgi-bin">
|
||||
AllowOverride None
|
||||
Options None
|
||||
Require all granted
|
||||
</Directory>
|
||||
|
||||
<IfModule headers_module>
|
||||
RequestHeader unset Proxy early
|
||||
</IfModule>
|
||||
|
||||
<IfModule mime_module>
|
||||
AddType application/x-compress .Z
|
||||
AddType application/x-gzip .gz .tgz
|
||||
</IfModule>
|
||||
|
||||
EnableSendfile on
|
||||
|
||||
Include conf/extra/httpd-info.conf
|
||||
Include conf/extra/httpd-mpm.conf
|
||||
|
||||
<IfModule ssl_module>
|
||||
SSLRandomSeed startup builtin
|
||||
SSLRandomSeed connect builtin
|
||||
</IfModule>
|
||||
|
||||
|
||||
|
||||
506
plugins/apache/conf/httpd.conf.tpl
Normal file
506
plugins/apache/conf/httpd.conf.tpl
Normal file
@@ -0,0 +1,506 @@
|
||||
#
|
||||
# This is the main Apache HTTP server configuration file. It contains the
|
||||
# configuration directives that give the server its instructions.
|
||||
# See <URL:http://httpd.apache.org/docs/2.4/> for detailed information.
|
||||
# In particular, see
|
||||
# <URL:http://httpd.apache.org/docs/2.4/mod/directives.html>
|
||||
# for a discussion of each configuration directive.
|
||||
#
|
||||
# Do NOT simply read the instructions in here without understanding
|
||||
# what they do. They're here only as hints or reminders. If you are unsure
|
||||
# consult the online docs. You have been warned.
|
||||
#
|
||||
# Configuration and logfile names: If the filenames you specify for many
|
||||
# of the server's control files begin with "/" (or "drive:/" for Win32), the
|
||||
# server will use that explicit path. If the filenames do *not* begin
|
||||
# with "/", the value of ServerRoot is prepended -- so "logs/access_log"
|
||||
# with ServerRoot set to "/usr/local/apache2" will be interpreted by the
|
||||
# server as "/usr/local/apache2/logs/access_log", whereas "/logs/access_log"
|
||||
# will be interpreted as '/logs/access_log'.
|
||||
|
||||
#
|
||||
# ServerRoot: The top of the directory tree under which the server's
|
||||
# configuration, error, and log files are kept.
|
||||
#
|
||||
# Do not add a slash at the end of the directory path. If you point
|
||||
# ServerRoot at a non-local disk, be sure to specify a local disk on the
|
||||
# Mutex directive, if file-based mutexes are used. If you wish to share the
|
||||
# same ServerRoot for multiple httpd daemons, you will need to change at
|
||||
# least PidFile.
|
||||
#
|
||||
ServerRoot "/Users/midoks/Desktop/mwdev/server/apache/httpd"
|
||||
|
||||
#
|
||||
# Mutex: Allows you to set the mutex mechanism and mutex file directory
|
||||
# for individual mutexes, or change the global defaults
|
||||
#
|
||||
# Uncomment and change the directory if mutexes are file-based and the default
|
||||
# mutex file directory is not on a local disk or is not appropriate for some
|
||||
# other reason.
|
||||
#
|
||||
# Mutex default:logs
|
||||
|
||||
#
|
||||
# Listen: Allows you to bind Apache to specific IP addresses and/or
|
||||
# ports, instead of the default. See also the <VirtualHost>
|
||||
# directive.
|
||||
#
|
||||
# Change this to Listen on specific IP addresses as shown below to
|
||||
# prevent Apache from glomming onto all bound IP addresses.
|
||||
#
|
||||
#Listen 12.34.56.78:80
|
||||
Listen 80
|
||||
|
||||
#
|
||||
# Dynamic Shared Object (DSO) Support
|
||||
#
|
||||
# To be able to use the functionality of a module which was built as a DSO you
|
||||
# have to place corresponding `LoadModule' lines at this location so the
|
||||
# directives contained in it are actually available _before_ they are used.
|
||||
# Statically compiled modules (those listed by `httpd -l') do not need
|
||||
# to be loaded here.
|
||||
#
|
||||
# Example:
|
||||
# LoadModule foo_module modules/mod_foo.so
|
||||
#
|
||||
LoadModule authn_file_module modules/mod_authn_file.so
|
||||
#LoadModule authn_dbm_module modules/mod_authn_dbm.so
|
||||
#LoadModule authn_anon_module modules/mod_authn_anon.so
|
||||
#LoadModule authn_dbd_module modules/mod_authn_dbd.so
|
||||
#LoadModule authn_socache_module modules/mod_authn_socache.so
|
||||
LoadModule authn_core_module modules/mod_authn_core.so
|
||||
LoadModule authz_host_module modules/mod_authz_host.so
|
||||
LoadModule authz_groupfile_module modules/mod_authz_groupfile.so
|
||||
LoadModule authz_user_module modules/mod_authz_user.so
|
||||
#LoadModule authz_dbm_module modules/mod_authz_dbm.so
|
||||
#LoadModule authz_owner_module modules/mod_authz_owner.so
|
||||
#LoadModule authz_dbd_module modules/mod_authz_dbd.so
|
||||
LoadModule authz_core_module modules/mod_authz_core.so
|
||||
LoadModule access_compat_module modules/mod_access_compat.so
|
||||
LoadModule auth_basic_module modules/mod_auth_basic.so
|
||||
#LoadModule auth_form_module modules/mod_auth_form.so
|
||||
#LoadModule auth_digest_module modules/mod_auth_digest.so
|
||||
#LoadModule allowmethods_module modules/mod_allowmethods.so
|
||||
#LoadModule file_cache_module modules/mod_file_cache.so
|
||||
#LoadModule cache_module modules/mod_cache.so
|
||||
#LoadModule cache_disk_module modules/mod_cache_disk.so
|
||||
#LoadModule cache_socache_module modules/mod_cache_socache.so
|
||||
#LoadModule socache_shmcb_module modules/mod_socache_shmcb.so
|
||||
#LoadModule socache_dbm_module modules/mod_socache_dbm.so
|
||||
#LoadModule socache_memcache_module modules/mod_socache_memcache.so
|
||||
#LoadModule socache_redis_module modules/mod_socache_redis.so
|
||||
#LoadModule watchdog_module modules/mod_watchdog.so
|
||||
#LoadModule macro_module modules/mod_macro.so
|
||||
#LoadModule dbd_module modules/mod_dbd.so
|
||||
#LoadModule dumpio_module modules/mod_dumpio.so
|
||||
#LoadModule buffer_module modules/mod_buffer.so
|
||||
#LoadModule ratelimit_module modules/mod_ratelimit.so
|
||||
LoadModule reqtimeout_module modules/mod_reqtimeout.so
|
||||
#LoadModule ext_filter_module modules/mod_ext_filter.so
|
||||
#LoadModule request_module modules/mod_request.so
|
||||
#LoadModule include_module modules/mod_include.so
|
||||
LoadModule filter_module modules/mod_filter.so
|
||||
#LoadModule substitute_module modules/mod_substitute.so
|
||||
#LoadModule sed_module modules/mod_sed.so
|
||||
#LoadModule brotli_module modules/mod_brotli.so
|
||||
LoadModule mime_module modules/mod_mime.so
|
||||
LoadModule log_config_module modules/mod_log_config.so
|
||||
#LoadModule log_debug_module modules/mod_log_debug.so
|
||||
#LoadModule logio_module modules/mod_logio.so
|
||||
LoadModule env_module modules/mod_env.so
|
||||
#LoadModule expires_module modules/mod_expires.so
|
||||
LoadModule headers_module modules/mod_headers.so
|
||||
#LoadModule unique_id_module modules/mod_unique_id.so
|
||||
LoadModule setenvif_module modules/mod_setenvif.so
|
||||
LoadModule version_module modules/mod_version.so
|
||||
#LoadModule remoteip_module modules/mod_remoteip.so
|
||||
#LoadModule proxy_module modules/mod_proxy.so
|
||||
#LoadModule proxy_connect_module modules/mod_proxy_connect.so
|
||||
#LoadModule proxy_ftp_module modules/mod_proxy_ftp.so
|
||||
#LoadModule proxy_http_module modules/mod_proxy_http.so
|
||||
#LoadModule proxy_fcgi_module modules/mod_proxy_fcgi.so
|
||||
#LoadModule proxy_scgi_module modules/mod_proxy_scgi.so
|
||||
#LoadModule proxy_uwsgi_module modules/mod_proxy_uwsgi.so
|
||||
#LoadModule proxy_fdpass_module modules/mod_proxy_fdpass.so
|
||||
#LoadModule proxy_wstunnel_module modules/mod_proxy_wstunnel.so
|
||||
#LoadModule proxy_ajp_module modules/mod_proxy_ajp.so
|
||||
#LoadModule proxy_balancer_module modules/mod_proxy_balancer.so
|
||||
#LoadModule proxy_express_module modules/mod_proxy_express.so
|
||||
#LoadModule proxy_hcheck_module modules/mod_proxy_hcheck.so
|
||||
#LoadModule session_module modules/mod_session.so
|
||||
#LoadModule session_cookie_module modules/mod_session_cookie.so
|
||||
#LoadModule session_dbd_module modules/mod_session_dbd.so
|
||||
#LoadModule slotmem_shm_module modules/mod_slotmem_shm.so
|
||||
#LoadModule ssl_module modules/mod_ssl.so
|
||||
#LoadModule http2_module modules/mod_http2.so
|
||||
#LoadModule lbmethod_byrequests_module modules/mod_lbmethod_byrequests.so
|
||||
#LoadModule lbmethod_bytraffic_module modules/mod_lbmethod_bytraffic.so
|
||||
#LoadModule lbmethod_bybusyness_module modules/mod_lbmethod_bybusyness.so
|
||||
#LoadModule lbmethod_heartbeat_module modules/mod_lbmethod_heartbeat.so
|
||||
LoadModule unixd_module modules/mod_unixd.so
|
||||
#LoadModule dav_module modules/mod_dav.so
|
||||
LoadModule status_module modules/mod_status.so
|
||||
LoadModule autoindex_module modules/mod_autoindex.so
|
||||
#LoadModule info_module modules/mod_info.so
|
||||
#LoadModule cgid_module modules/mod_cgid.so
|
||||
#LoadModule dav_fs_module modules/mod_dav_fs.so
|
||||
#LoadModule vhost_alias_module modules/mod_vhost_alias.so
|
||||
#LoadModule negotiation_module modules/mod_negotiation.so
|
||||
LoadModule dir_module modules/mod_dir.so
|
||||
#LoadModule actions_module modules/mod_actions.so
|
||||
#LoadModule speling_module modules/mod_speling.so
|
||||
#LoadModule userdir_module modules/mod_userdir.so
|
||||
LoadModule alias_module modules/mod_alias.so
|
||||
#LoadModule rewrite_module modules/mod_rewrite.so
|
||||
|
||||
<IfModule unixd_module>
|
||||
#
|
||||
# If you wish httpd to run as a different user or group, you must run
|
||||
# httpd as root initially and it will switch.
|
||||
#
|
||||
# User/Group: The name (or #number) of the user/group to run httpd as.
|
||||
# It is usually good practice to create a dedicated user and group for
|
||||
# running httpd, as with most system services.
|
||||
#
|
||||
User daemon
|
||||
Group daemon
|
||||
|
||||
</IfModule>
|
||||
|
||||
# 'Main' server configuration
|
||||
#
|
||||
# The directives in this section set up the values used by the 'main'
|
||||
# server, which responds to any requests that aren't handled by a
|
||||
# <VirtualHost> definition. These values also provide defaults for
|
||||
# any <VirtualHost> containers you may define later in the file.
|
||||
#
|
||||
# All of these directives may appear inside <VirtualHost> containers,
|
||||
# in which case these default settings will be overridden for the
|
||||
# virtual host being defined.
|
||||
#
|
||||
|
||||
#
|
||||
# ServerAdmin: Your address, where problems with the server should be
|
||||
# e-mailed. This address appears on some server-generated pages, such
|
||||
# as error documents. e.g. admin@your-domain.com
|
||||
#
|
||||
ServerAdmin you@example.com
|
||||
|
||||
#
|
||||
# ServerName gives the name and port that the server uses to identify itself.
|
||||
# This can often be determined automatically, but we recommend you specify
|
||||
# it explicitly to prevent problems during startup.
|
||||
#
|
||||
# If your host doesn't have a registered DNS name, enter its IP address here.
|
||||
#
|
||||
#ServerName www.example.com:80
|
||||
|
||||
#
|
||||
# Deny access to the entirety of your server's filesystem. You must
|
||||
# explicitly permit access to web content directories in other
|
||||
# <Directory> blocks below.
|
||||
#
|
||||
<Directory />
|
||||
AllowOverride none
|
||||
Require all denied
|
||||
</Directory>
|
||||
|
||||
#
|
||||
# Note that from this point forward you must specifically allow
|
||||
# particular features to be enabled - so if something's not working as
|
||||
# you might expect, make sure that you have specifically enabled it
|
||||
# below.
|
||||
#
|
||||
|
||||
#
|
||||
# DocumentRoot: The directory out of which you will serve your
|
||||
# documents. By default, all requests are taken from this directory, but
|
||||
# symbolic links and aliases may be used to point to other locations.
|
||||
#
|
||||
DocumentRoot "/Users/midoks/Desktop/mwdev/server/apache/httpd/htdocs"
|
||||
<Directory "/Users/midoks/Desktop/mwdev/server/apache/httpd/htdocs">
|
||||
#
|
||||
# Possible values for the Options directive are "None", "All",
|
||||
# or any combination of:
|
||||
# Indexes Includes FollowSymLinks SymLinksifOwnerMatch ExecCGI MultiViews
|
||||
#
|
||||
# Note that "MultiViews" must be named *explicitly* --- "Options All"
|
||||
# doesn't give it to you.
|
||||
#
|
||||
# The Options directive is both complicated and important. Please see
|
||||
# http://httpd.apache.org/docs/2.4/mod/core.html#options
|
||||
# for more information.
|
||||
#
|
||||
Options Indexes FollowSymLinks
|
||||
|
||||
#
|
||||
# AllowOverride controls what directives may be placed in .htaccess files.
|
||||
# It can be "All", "None", or any combination of the keywords:
|
||||
# AllowOverride FileInfo AuthConfig Limit
|
||||
#
|
||||
AllowOverride None
|
||||
|
||||
#
|
||||
# Controls who can get stuff from this server.
|
||||
#
|
||||
Require all granted
|
||||
</Directory>
|
||||
|
||||
#
|
||||
# DirectoryIndex: sets the file that Apache will serve if a directory
|
||||
# is requested.
|
||||
#
|
||||
<IfModule dir_module>
|
||||
DirectoryIndex index.html
|
||||
</IfModule>
|
||||
|
||||
#
|
||||
# The following lines prevent .htaccess and .htpasswd files from being
|
||||
# viewed by Web clients.
|
||||
#
|
||||
<Files ".ht*">
|
||||
Require all denied
|
||||
</Files>
|
||||
|
||||
#
|
||||
# ErrorLog: The location of the error log file.
|
||||
# If you do not specify an ErrorLog directive within a <VirtualHost>
|
||||
# container, error messages relating to that virtual host will be
|
||||
# logged here. If you *do* define an error logfile for a <VirtualHost>
|
||||
# container, that host's errors will be logged there and not here.
|
||||
#
|
||||
ErrorLog "logs/error_log"
|
||||
|
||||
#
|
||||
# LogLevel: Control the number of messages logged to the error_log.
|
||||
# Possible values include: debug, info, notice, warn, error, crit,
|
||||
# alert, emerg.
|
||||
#
|
||||
LogLevel warn
|
||||
|
||||
<IfModule log_config_module>
|
||||
#
|
||||
# The following directives define some format nicknames for use with
|
||||
# a CustomLog directive (see below).
|
||||
#
|
||||
LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" combined
|
||||
LogFormat "%h %l %u %t \"%r\" %>s %b" common
|
||||
|
||||
<IfModule logio_module>
|
||||
# You need to enable mod_logio.c to use %I and %O
|
||||
LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\" %I %O" combinedio
|
||||
</IfModule>
|
||||
|
||||
#
|
||||
# The location and format of the access logfile (Common Logfile Format).
|
||||
# If you do not define any access logfiles within a <VirtualHost>
|
||||
# container, they will be logged here. Contrariwise, if you *do*
|
||||
# define per-<VirtualHost> access logfiles, transactions will be
|
||||
# logged therein and *not* in this file.
|
||||
#
|
||||
CustomLog "logs/access_log" common
|
||||
|
||||
#
|
||||
# If you prefer a logfile with access, agent, and referer information
|
||||
# (Combined Logfile Format) you can use the following directive.
|
||||
#
|
||||
#CustomLog "logs/access_log" combined
|
||||
</IfModule>
|
||||
|
||||
<IfModule alias_module>
|
||||
#
|
||||
# Redirect: Allows you to tell clients about documents that used to
|
||||
# exist in your server's namespace, but do not anymore. The client
|
||||
# will make a new request for the document at its new location.
|
||||
# Example:
|
||||
# Redirect permanent /foo http://www.example.com/bar
|
||||
|
||||
#
|
||||
# Alias: Maps web paths into filesystem paths and is used to
|
||||
# access content that does not live under the DocumentRoot.
|
||||
# Example:
|
||||
# Alias /webpath /full/filesystem/path
|
||||
#
|
||||
# If you include a trailing / on /webpath then the server will
|
||||
# require it to be present in the URL. You will also likely
|
||||
# need to provide a <Directory> section to allow access to
|
||||
# the filesystem path.
|
||||
|
||||
#
|
||||
# ScriptAlias: This controls which directories contain server scripts.
|
||||
# ScriptAliases are essentially the same as Aliases, except that
|
||||
# documents in the target directory are treated as applications and
|
||||
# run by the server when requested rather than as documents sent to the
|
||||
# client. The same rules about trailing "/" apply to ScriptAlias
|
||||
# directives as to Alias.
|
||||
#
|
||||
ScriptAlias /cgi-bin/ "/Users/midoks/Desktop/mwdev/server/apache/httpd/cgi-bin/"
|
||||
|
||||
</IfModule>
|
||||
|
||||
<IfModule cgid_module>
|
||||
#
|
||||
# ScriptSock: On threaded servers, designate the path to the UNIX
|
||||
# socket used to communicate with the CGI daemon of mod_cgid.
|
||||
#
|
||||
#Scriptsock cgisock
|
||||
</IfModule>
|
||||
|
||||
#
|
||||
# "/Users/midoks/Desktop/mwdev/server/apache/httpd/cgi-bin" should be changed to whatever your ScriptAliased
|
||||
# CGI directory exists, if you have that configured.
|
||||
#
|
||||
<Directory "/Users/midoks/Desktop/mwdev/server/apache/httpd/cgi-bin">
|
||||
AllowOverride None
|
||||
Options None
|
||||
Require all granted
|
||||
</Directory>
|
||||
|
||||
<IfModule headers_module>
|
||||
#
|
||||
# Avoid passing HTTP_PROXY environment to CGI's on this or any proxied
|
||||
# backend servers which have lingering "httpoxy" defects.
|
||||
# 'Proxy' request header is undefined by the IETF, not listed by IANA
|
||||
#
|
||||
RequestHeader unset Proxy early
|
||||
</IfModule>
|
||||
|
||||
<IfModule mime_module>
|
||||
#
|
||||
# TypesConfig points to the file containing the list of mappings from
|
||||
# filename extension to MIME-type.
|
||||
#
|
||||
TypesConfig conf/mime.types
|
||||
|
||||
#
|
||||
# AddType allows you to add to or override the MIME configuration
|
||||
# file specified in TypesConfig for specific file types.
|
||||
#
|
||||
#AddType application/x-gzip .tgz
|
||||
#
|
||||
# AddEncoding allows you to have certain browsers uncompress
|
||||
# information on the fly. Note: Not all browsers support this.
|
||||
#
|
||||
#AddEncoding x-compress .Z
|
||||
#AddEncoding x-gzip .gz .tgz
|
||||
#
|
||||
# If the AddEncoding directives above are commented-out, then you
|
||||
# probably should define those extensions to indicate media types:
|
||||
#
|
||||
AddType application/x-compress .Z
|
||||
AddType application/x-gzip .gz .tgz
|
||||
|
||||
#
|
||||
# AddHandler allows you to map certain file extensions to "handlers":
|
||||
# actions unrelated to filetype. These can be either built into the server
|
||||
# or added with the Action directive (see below)
|
||||
#
|
||||
# To use CGI scripts outside of ScriptAliased directories:
|
||||
# (You will also need to add "ExecCGI" to the "Options" directive.)
|
||||
#
|
||||
#AddHandler cgi-script .cgi
|
||||
|
||||
# For type maps (negotiated resources):
|
||||
#AddHandler type-map var
|
||||
|
||||
#
|
||||
# Filters allow you to process content before it is sent to the client.
|
||||
#
|
||||
# To parse .shtml files for server-side includes (SSI):
|
||||
# (You will also need to add "Includes" to the "Options" directive.)
|
||||
#
|
||||
#AddType text/html .shtml
|
||||
#AddOutputFilter INCLUDES .shtml
|
||||
</IfModule>
|
||||
|
||||
#
|
||||
# The mod_mime_magic module allows the server to use various hints from the
|
||||
# contents of the file itself to determine its type. The MIMEMagicFile
|
||||
# directive tells the module where the hint definitions are located.
|
||||
#
|
||||
#MIMEMagicFile conf/magic
|
||||
|
||||
#
|
||||
# Customizable error responses come in three flavors:
|
||||
# 1) plain text 2) local redirects 3) external redirects
|
||||
#
|
||||
# Some examples:
|
||||
#ErrorDocument 500 "The server made a boo boo."
|
||||
#ErrorDocument 404 /missing.html
|
||||
#ErrorDocument 404 "/cgi-bin/missing_handler.pl"
|
||||
#ErrorDocument 402 http://www.example.com/subscription_info.html
|
||||
#
|
||||
|
||||
#
|
||||
# MaxRanges: Maximum number of Ranges in a request before
|
||||
# returning the entire resource, or one of the special
|
||||
# values 'default', 'none' or 'unlimited'.
|
||||
# Default setting is to accept 200 Ranges.
|
||||
#MaxRanges unlimited
|
||||
|
||||
#
|
||||
# EnableMMAP and EnableSendfile: On systems that support it,
|
||||
# memory-mapping or the sendfile syscall may be used to deliver
|
||||
# files. This usually improves server performance, but must
|
||||
# be turned off when serving from networked-mounted
|
||||
# filesystems or if support for these functions is otherwise
|
||||
# broken on your system.
|
||||
# Defaults: EnableMMAP On, EnableSendfile Off
|
||||
#
|
||||
#EnableMMAP off
|
||||
#EnableSendfile on
|
||||
|
||||
# Supplemental configuration
|
||||
#
|
||||
# The configuration files in the conf/extra/ directory can be
|
||||
# included to add extra features or to modify the default configuration of
|
||||
# the server, or you may simply copy their contents here and change as
|
||||
# necessary.
|
||||
|
||||
# Server-pool management (MPM specific)
|
||||
#Include conf/extra/httpd-mpm.conf
|
||||
|
||||
# Multi-language error messages
|
||||
#Include conf/extra/httpd-multilang-errordoc.conf
|
||||
|
||||
# Fancy directory listings
|
||||
#Include conf/extra/httpd-autoindex.conf
|
||||
|
||||
# Language settings
|
||||
#Include conf/extra/httpd-languages.conf
|
||||
|
||||
# User home directories
|
||||
#Include conf/extra/httpd-userdir.conf
|
||||
|
||||
# Real-time info on requests and configuration
|
||||
#Include conf/extra/httpd-info.conf
|
||||
|
||||
# Virtual hosts
|
||||
#Include conf/extra/httpd-vhosts.conf
|
||||
|
||||
# Local access to the Apache HTTP Server Manual
|
||||
#Include conf/extra/httpd-manual.conf
|
||||
|
||||
# Distributed authoring and versioning (WebDAV)
|
||||
#Include conf/extra/httpd-dav.conf
|
||||
|
||||
# Various default settings
|
||||
#Include conf/extra/httpd-default.conf
|
||||
|
||||
# Configure mod_proxy_html to understand HTML4/XHTML1
|
||||
<IfModule proxy_html_module>
|
||||
Include conf/extra/proxy-html.conf
|
||||
</IfModule>
|
||||
|
||||
# Secure (SSL/TLS) connections
|
||||
#Include conf/extra/httpd-ssl.conf
|
||||
#
|
||||
# Note: The following must must be present to support
|
||||
# starting without SSL on platforms with no /dev/random equivalent
|
||||
# but a statically compiled-in mod_ssl.
|
||||
#
|
||||
<IfModule ssl_module>
|
||||
SSLRandomSeed startup builtin
|
||||
SSLRandomSeed connect builtin
|
||||
</IfModule>
|
||||
|
||||
BIN
plugins/apache/ico.png
Normal file
BIN
plugins/apache/ico.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.4 KiB |
25
plugins/apache/index.html
Executable file
25
plugins/apache/index.html
Executable file
@@ -0,0 +1,25 @@
|
||||
<div class="bt-form">
|
||||
<!-- <div class='plugin_version'></div> -->
|
||||
<div class="bt-w-main">
|
||||
<div class="bt-w-menu">
|
||||
<p class="bgw" onclick="httpPluginService('apache',$('.plugin_version').attr('version'));">服务</p>
|
||||
<p onclick="pluginInitD('apache', $('.plugin_version').attr('version'));">自启动</p>
|
||||
<p onclick="pluginConfig('apache');">配置修改</p>
|
||||
<p onclick="getHttpdStatus();">负载状态</p>
|
||||
<p onclick="setOpCfg();">MPM性能调整</p>
|
||||
<p onclick="pluginLogs('apache');">错误日志</p>
|
||||
<p onclick="otherFunc();">维护功能</p>
|
||||
</div>
|
||||
<div class="bt-w-con pd15">
|
||||
<div class="soft-man-con" style="height: 500px; overflow-y: auto;"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<script type="text/javascript">
|
||||
// resetPluginWinHeight(600);
|
||||
$.getScript( "/plugins/file?name=apache&f=js/httpd.js", function(){
|
||||
httpPluginService('apache');
|
||||
});
|
||||
</script>
|
||||
593
plugins/apache/index.py
Executable file
593
plugins/apache/index.py
Executable file
@@ -0,0 +1,593 @@
|
||||
# coding:utf-8
|
||||
|
||||
import sys
|
||||
import io
|
||||
import os
|
||||
import time
|
||||
import threading
|
||||
import subprocess
|
||||
import re
|
||||
|
||||
|
||||
web_dir = os.getcwd() + "/web"
|
||||
if os.path.exists(web_dir):
|
||||
sys.path.append(web_dir)
|
||||
os.chdir(web_dir)
|
||||
|
||||
import core.mw as mw
|
||||
|
||||
app_debug = False
|
||||
if mw.isAppleSystem():
|
||||
app_debug = True
|
||||
|
||||
|
||||
def getPluginName():
|
||||
return 'apache'
|
||||
|
||||
|
||||
def getPluginDir():
|
||||
return mw.getPluginDir() + '/' + getPluginName()
|
||||
|
||||
|
||||
def getServerDir():
|
||||
return mw.getServerDir() + '/' + getPluginName()
|
||||
|
||||
|
||||
def getInitDFile():
|
||||
current_os = mw.getOs()
|
||||
if current_os == 'darwin':
|
||||
return '/tmp/' + getPluginName()
|
||||
|
||||
if current_os.startswith('freebsd'):
|
||||
return '/etc/rc.d/' + getPluginName()
|
||||
|
||||
return '/etc/init.d/' + getPluginName()
|
||||
|
||||
|
||||
def getArgs():
|
||||
args = sys.argv[2:]
|
||||
# print(args)
|
||||
tmp = {}
|
||||
args_len = len(args)
|
||||
|
||||
if args_len == 1:
|
||||
t = args[0].strip('{').strip('}')
|
||||
t = t.split(':',2)
|
||||
tmp[t[0]] = t[1]
|
||||
elif args_len > 1:
|
||||
for i in range(len(args)):
|
||||
t = args[i].split(':',2)
|
||||
tmp[t[0]] = t[1]
|
||||
# print(tmp)
|
||||
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 clearTemp():
|
||||
path_bin = getServerDir() + "/httpd"
|
||||
|
||||
|
||||
def getConf():
|
||||
path = getServerDir() + "/httpd/conf/httpd.conf"
|
||||
return path
|
||||
|
||||
|
||||
def getConfMpm():
|
||||
path = getServerDir() + "/httpd/conf/extra/httpd-mpm.conf"
|
||||
return path
|
||||
|
||||
|
||||
def getConfTpl():
|
||||
path = getPluginDir() + '/conf/httpd.conf'
|
||||
return path
|
||||
|
||||
|
||||
def getOs():
|
||||
data = {}
|
||||
data['os'] = mw.getOs()
|
||||
data['auth'] = True
|
||||
return mw.getJson(data)
|
||||
|
||||
|
||||
def getInitDTpl():
|
||||
path = getPluginDir() + "/init.d/httpd.tpl"
|
||||
return path
|
||||
|
||||
|
||||
def getPidFile():
|
||||
file = getConf()
|
||||
content = mw.readFile(file)
|
||||
rep = r'pid\s*(.*);'
|
||||
tmp = re.search(rep, content)
|
||||
return tmp.groups()[0].strip()
|
||||
|
||||
|
||||
def getFileOwner(filename):
|
||||
import pwd
|
||||
stat = os.lstat(filename)
|
||||
uid = stat.st_uid
|
||||
pw = pwd.getpwuid(uid)
|
||||
return pw.pw_name
|
||||
|
||||
|
||||
def checkAuthEq(file, owner='root'):
|
||||
fowner = getFileOwner(file)
|
||||
if (fowner == owner):
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def confReplace():
|
||||
service_path = mw.getServerDir()
|
||||
content = mw.readFile(getConfTpl())
|
||||
content = content.replace('{$SERVER_PATH}', service_path)
|
||||
|
||||
# 主配置文件
|
||||
nconf = getServerDir() + '/httpd/conf/httpd.conf'
|
||||
mw.writeFile(nconf, content)
|
||||
|
||||
|
||||
def initDreplace():
|
||||
|
||||
file_tpl = getInitDTpl()
|
||||
service_path = mw.getServerDir()
|
||||
|
||||
initD_path = getServerDir() + '/init.d'
|
||||
|
||||
# OpenResty is not installed
|
||||
if not os.path.exists(getServerDir()):
|
||||
print("ok")
|
||||
exit(0)
|
||||
|
||||
# init.d
|
||||
file_bin = initD_path + '/' + getPluginName()
|
||||
if not os.path.exists(initD_path):
|
||||
os.mkdir(initD_path)
|
||||
|
||||
# initd replace
|
||||
content = mw.readFile(file_tpl)
|
||||
content = content.replace('{$SERVER_PATH}', service_path)
|
||||
mw.writeFile(file_bin, content)
|
||||
mw.execShell('chmod +x ' + file_bin)
|
||||
|
||||
# config replace
|
||||
confReplace()
|
||||
|
||||
# systemd
|
||||
# /usr/lib/systemd/system
|
||||
systemDir = mw.systemdCfgDir()
|
||||
systemService = systemDir + '/httpd.service'
|
||||
if os.path.exists(systemDir) and not os.path.exists(systemService):
|
||||
systemServiceTpl = getPluginDir() + '/init.d/httpd.service.tpl'
|
||||
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 status():
|
||||
cmd = "ps -ef|grep 'httpd' |grep -v grep | grep -v python | awk '{print $2}'"
|
||||
data = mw.execShell(cmd)
|
||||
if data[0] == '':
|
||||
return 'stop'
|
||||
return 'start'
|
||||
|
||||
|
||||
def restyOp(method):
|
||||
file = initDreplace()
|
||||
|
||||
# 启动时,先检查一下配置文件
|
||||
check = getServerDir() + "/httpd/bin/httpd -t"
|
||||
check_data = mw.execShell(check)
|
||||
if not check_data[1].find('Syntax OK') > -1:
|
||||
return check_data[1]
|
||||
|
||||
current_os = mw.getOs()
|
||||
if current_os == "darwin":
|
||||
data = mw.execShell(file + ' ' + method)
|
||||
if data[1] == '':
|
||||
return 'ok'
|
||||
return data[1]
|
||||
|
||||
if current_os.startswith("freebsd"):
|
||||
mw.execShell('service httpd '+method)
|
||||
if data[1] == '':
|
||||
return 'ok'
|
||||
return data[1]
|
||||
|
||||
data = mw.execShell('systemctl ' + method + ' httpd')
|
||||
if data[1] == '':
|
||||
return 'ok'
|
||||
return data[1]
|
||||
|
||||
|
||||
def op_submit_systemctl_restart():
|
||||
current_os = mw.getOs()
|
||||
if current_os.startswith("freebsd"):
|
||||
mw.execShell('service httpd restart')
|
||||
return True
|
||||
|
||||
mw.execShell('systemctl restart httpd')
|
||||
return True
|
||||
|
||||
|
||||
def op_submit_init_restart(file):
|
||||
mw.execShell(file + ' restart')
|
||||
|
||||
|
||||
def restyOp_restart():
|
||||
file = initDreplace()
|
||||
|
||||
# 启动时,先检查一下配置文件
|
||||
check = getServerDir() + "/httpd/bin/httpd -t"
|
||||
check_data = mw.execShell(check)
|
||||
if not check_data[1].find('Syntax OK') > -1:
|
||||
return 'ERROR: 配置出错<br><a style="color:red;">' + check_data[1].replace("\n", '<br>') + '</a>'
|
||||
|
||||
if not mw.isAppleSystem():
|
||||
threading.Timer(2, op_submit_systemctl_restart).start()
|
||||
return 'ok'
|
||||
|
||||
threading.Timer(2, op_submit_init_restart, args=(file,)).start()
|
||||
return 'ok'
|
||||
|
||||
|
||||
def start():
|
||||
return restyOp('start')
|
||||
|
||||
|
||||
def stop():
|
||||
r = restyOp('stop')
|
||||
|
||||
mw.execShell("ps -ef|grep httpd | grep -v grep | awk '{print $2}'|xargs -r kill")
|
||||
return r
|
||||
|
||||
|
||||
def restart():
|
||||
return restyOp_restart()
|
||||
|
||||
|
||||
def reload():
|
||||
confReplace()
|
||||
return restyOp('reload')
|
||||
|
||||
|
||||
def initdStatus():
|
||||
current_os = mw.getOs()
|
||||
if current_os == 'darwin':
|
||||
return "Apple Computer does not support"
|
||||
|
||||
if current_os.startswith('freebsd'):
|
||||
initd_bin = getInitDFile()
|
||||
if os.path.exists(initd_bin):
|
||||
return 'ok'
|
||||
|
||||
shell_cmd = 'systemctl status httpd | grep loaded | grep "enabled;"'
|
||||
data = mw.execShell(shell_cmd)
|
||||
if data[0] == '':
|
||||
return 'fail'
|
||||
return 'ok'
|
||||
|
||||
|
||||
def initdInstall():
|
||||
current_os = mw.getOs()
|
||||
if current_os == 'darwin':
|
||||
return "Apple Computer does not support"
|
||||
|
||||
# freebsd initd install
|
||||
if current_os.startswith('freebsd'):
|
||||
import shutil
|
||||
source_bin = initDreplace()
|
||||
initd_bin = getInitDFile()
|
||||
shutil.copyfile(source_bin, initd_bin)
|
||||
mw.execShell('chmod +x ' + initd_bin)
|
||||
mw.execShell('sysrc httpd_enable="YES"')
|
||||
return 'ok'
|
||||
|
||||
mw.execShell('systemctl enable httpd')
|
||||
return 'ok'
|
||||
|
||||
|
||||
def initdUinstall():
|
||||
current_os = mw.getOs()
|
||||
if current_os == 'darwin':
|
||||
return "Apple Computer does not support"
|
||||
|
||||
if current_os.startswith('freebsd'):
|
||||
initd_bin = getInitDFile()
|
||||
os.remove(initd_bin)
|
||||
mw.execShell('sysrc httpd_enable="NO"')
|
||||
return 'ok'
|
||||
|
||||
mw.execShell('systemctl disable httpd')
|
||||
return 'ok'
|
||||
|
||||
def getHttpdStatusPort():
|
||||
conf = mw.getServerDir() + '/apache/httpd/conf/httpd.conf'
|
||||
content = mw.readFile(conf)
|
||||
if not content:
|
||||
return None
|
||||
rep = r'^\s*Listen\s*(?:\d+\.\d+\.\d+\.\d+:)?(\d+)' # 匹配非注释行的 Listen 指令,忽略大小写
|
||||
tmp = re.search(rep, content, re.IGNORECASE | re.MULTILINE)
|
||||
if tmp:
|
||||
port = tmp.groups()[0].strip()
|
||||
return port
|
||||
return None
|
||||
|
||||
|
||||
def runInfoDone(data):
|
||||
result = {}
|
||||
if not data:
|
||||
return result
|
||||
|
||||
# 解析服务器状态数据
|
||||
lines = data.strip().split('\n')
|
||||
for line in lines:
|
||||
if ':' in line:
|
||||
key, value = line.split(':', 1)
|
||||
key = key.strip()
|
||||
value = value.strip()
|
||||
result[key] = value
|
||||
|
||||
return result
|
||||
|
||||
|
||||
def runInfo():
|
||||
op_status = status()
|
||||
if op_status == 'stop':
|
||||
return mw.returnJson(False, "未启动!")
|
||||
|
||||
port = getHttpdStatusPort()
|
||||
if not port:
|
||||
return mw.returnJson(False, "无法获取端口信息!")
|
||||
|
||||
# 取Openresty负载状态
|
||||
try:
|
||||
url = 'http://127.0.0.1:%s/server-status?auto' % port
|
||||
result = mw.httpGet(url, timeout=3)
|
||||
data = runInfoDone(result)
|
||||
return mw.getJson(data)
|
||||
except Exception as e:
|
||||
try:
|
||||
url = 'http://' + mw.getHostAddr() + ':%s/server-status?auto' % port
|
||||
result = mw.httpGet(url)
|
||||
data = runInfoDone(result)
|
||||
return mw.getJson(data)
|
||||
except Exception as e:
|
||||
return mw.returnJson(False, "apache异常!")
|
||||
|
||||
except Exception as e:
|
||||
return mw.returnJson(False, "apache not started!")
|
||||
|
||||
|
||||
def errorLogPath():
|
||||
return getServerDir() + '/httpd/logs/error.log'
|
||||
|
||||
|
||||
def getCfg():
|
||||
cfg = getConfMpm()
|
||||
content = mw.readFile(cfg)
|
||||
|
||||
unitrep = "[kmgKMG]"
|
||||
|
||||
# 获取当前 MPM 模块
|
||||
mpm_module = ""
|
||||
mpm_match = re.search(r"mpm_(\w+)_module", content)
|
||||
if mpm_match:
|
||||
mpm_module = mpm_match.group(1)
|
||||
|
||||
# MPM 配置参数
|
||||
mpm_cfg_args = {
|
||||
"prefork": [
|
||||
{"name": "StartServers", "ps": "服务器进程启动数量", 'type': 2},
|
||||
{"name": "MinSpareServers", "ps": "保持空闲的最小服务器进程数", 'type': 2},
|
||||
{"name": "MaxSpareServers", "ps": "保持空闲的最大服务器进程数", 'type': 2},
|
||||
{"name": "MaxRequestWorkers", "ps": "允许启动的最大服务器进程数", 'type': 2},
|
||||
{"name": "MaxConnectionsPerChild", "ps": "服务器进程服务的最大连接数", 'type': 2},
|
||||
],
|
||||
"worker": [
|
||||
{"name": "StartServers", "ps": "初始服务器进程数", 'type': 2},
|
||||
{"name": "MinSpareThreads", "ps": "保持空闲的最小工作线程数", 'type': 2},
|
||||
{"name": "MaxSpareThreads", "ps": "保持空闲的最大工作线程数", 'type': 2},
|
||||
{"name": "ThreadsPerChild", "ps": "每个服务器进程的工作线程数", 'type': 2},
|
||||
{"name": "MaxRequestWorkers", "ps": "最大工作线程数", 'type': 2},
|
||||
{"name": "MaxConnectionsPerChild", "ps": "服务器进程服务的最大连接数", 'type': 2},
|
||||
],
|
||||
"event": [
|
||||
{"name": "StartServers", "ps": "初始服务器进程数", 'type': 2},
|
||||
{"name": "MinSpareThreads", "ps": "保持空闲的最小工作线程数", 'type': 2},
|
||||
{"name": "MaxSpareThreads", "ps": "保持空闲的最大工作线程数", 'type': 2},
|
||||
{"name": "ThreadsPerChild", "ps": "每个服务器进程的工作线程数", 'type': 2},
|
||||
{"name": "MaxRequestWorkers", "ps": "最大工作线程数", 'type': 2},
|
||||
{"name": "MaxConnectionsPerChild", "ps": "服务器进程服务的最大连接数", 'type': 2},
|
||||
],
|
||||
"netware": [
|
||||
{"name": "ThreadStackSize", "ps": "每个工作线程分配的堆栈大小", 'type': 2},
|
||||
{"name": "StartThreads", "ps": "服务器启动时启动的工作线程数", 'type': 2},
|
||||
{"name": "MinSpareThreads", "ps": "保持空闲的最小线程数", 'type': 2},
|
||||
{"name": "MaxSpareThreads", "ps": "保持空闲的最大线程数", 'type': 2},
|
||||
{"name": "MaxThreads", "ps": "同时活跃的最大工作线程数", 'type': 2},
|
||||
{"name": "MaxConnectionsPerChild", "ps": "线程服务的最大连接数", 'type': 2},
|
||||
],
|
||||
"mpmt_os2": [
|
||||
{"name": "StartServers", "ps": "维护的服务器进程数", 'type': 2},
|
||||
{"name": "MinSpareThreads", "ps": "每个进程的最小空闲线程数", 'type': 2},
|
||||
{"name": "MaxSpareThreads", "ps": "每个进程的最大空闲线程数", 'type': 2},
|
||||
{"name": "MaxConnectionsPerChild", "ps": "每个服务器进程的最大连接数", 'type': 2},
|
||||
],
|
||||
"winnt": [
|
||||
{"name": "ThreadsPerChild", "ps": "服务器进程中的工作线程数", 'type': 2},
|
||||
{"name": "MaxConnectionsPerChild", "ps": "服务器进程服务的最大连接数", 'type': 2},
|
||||
]
|
||||
}
|
||||
|
||||
# 通用配置参数
|
||||
common_cfg_args = [
|
||||
{"name": "MaxMemFree", "ps": "每个分配器允许持有的最大空闲KB数", 'type': 2},
|
||||
]
|
||||
|
||||
# 合并配置参数
|
||||
cfg_args = []
|
||||
if mpm_module in mpm_cfg_args:
|
||||
cfg_args.extend(mpm_cfg_args[mpm_module])
|
||||
cfg_args.extend(common_cfg_args)
|
||||
|
||||
rdata = []
|
||||
for i in cfg_args:
|
||||
# 匹配 MPM 特定配置
|
||||
rep = r"<IfModule mpm_%s_module>.*?(%s)\s+(\w+).*?</IfModule>" % (mpm_module, i["name"])
|
||||
k = re.search(rep, content, re.DOTALL)
|
||||
|
||||
# 如果没有找到 MPM 特定配置,尝试匹配通用配置
|
||||
if not k:
|
||||
rep = r"(%s)\s+(\w+)" % i["name"]
|
||||
k = re.search(rep, content)
|
||||
|
||||
if not k:
|
||||
continue
|
||||
|
||||
key = k.group(1)
|
||||
v = k.group(2) if len(k.groups()) > 1 else ""
|
||||
|
||||
if re.search(unitrep, v):
|
||||
u = str.upper(v[-1])
|
||||
v = v[:-1]
|
||||
if len(u) == 1:
|
||||
psstr = u + "B," + i["ps"]
|
||||
else:
|
||||
psstr = u + "," + i["ps"]
|
||||
else:
|
||||
u = ""
|
||||
|
||||
kv = {"name": key, "value": v, "unit": u,
|
||||
"ps": i["ps"], "type": i["type"]}
|
||||
rdata.append(kv)
|
||||
return mw.returnJson(True, "ok", rdata)
|
||||
|
||||
def replaceChar(value, index, new_char):
|
||||
return value[:index] + new_char + value[index+1:]
|
||||
|
||||
def setCfg():
|
||||
args = getArgs()
|
||||
|
||||
# 检查参数,允许动态参数
|
||||
cfg = getConfMpm()
|
||||
mw.backFile(cfg)
|
||||
content = mw.readFile(cfg)
|
||||
|
||||
# 获取当前 MPM 模块
|
||||
mpm_module = ""
|
||||
mpm_match = re.search(r"mpm_(\w+)_module", content)
|
||||
if mpm_match:
|
||||
mpm_module = mpm_match.group(1)
|
||||
|
||||
# 验证参数值
|
||||
for k, v in args.items():
|
||||
# 检查是否为数字参数
|
||||
if not re.search(r"\d+", v):
|
||||
return mw.returnJson(False, '参数值错误,请输入数字整数')
|
||||
|
||||
# 替换 MPM 特定配置
|
||||
if mpm_module:
|
||||
def replace_mpm_config(match):
|
||||
return match.group(1) + k + match.group(2) + v + match.group(3)
|
||||
rep = r"(<IfModule mpm_%s_module>.*?)%s(\s+)\d+(.*?</IfModule>)" % (mpm_module, k)
|
||||
if re.search(rep, content, re.DOTALL):
|
||||
content = re.sub(rep, replace_mpm_config, content, flags=re.DOTALL)
|
||||
|
||||
# 替换通用配置
|
||||
def replace_common_config(match):
|
||||
return k + match.group(1) + v
|
||||
rep = r"%s(\s+)\d+" % k
|
||||
if re.search(rep, content):
|
||||
content = re.sub(rep, replace_common_config, content)
|
||||
|
||||
mw.writeFile(cfg, content)
|
||||
isError = mw.checkHttpdConfig()
|
||||
if (isError != True):
|
||||
mw.restoreFile(cfg)
|
||||
return mw.returnJson(False, 'ERROR: 配置出错<br><a style="color:red;">' + isError.replace("\n", '<br>') + '</a>')
|
||||
|
||||
mw.restartWeb()
|
||||
return mw.returnJson(True, '设置成功')
|
||||
|
||||
|
||||
def cronAddCheck():
|
||||
try:
|
||||
import tool_task
|
||||
tool_task.createBgTask()
|
||||
return mw.returnJson(True, '添加检查任务成功')
|
||||
except Exception as e:
|
||||
return mw.returnJson(False, '添加检查任务失败:'+str(e))
|
||||
|
||||
def cronDelCheck():
|
||||
try:
|
||||
import tool_task
|
||||
tool_task.removeBgTask()
|
||||
return mw.returnJson(True, '删除检查任务成功')
|
||||
except Exception as e:
|
||||
return mw.returnJson(False, '删除检查任务失败:'+str(e))
|
||||
|
||||
def cronCheck():
|
||||
return 'ok'
|
||||
|
||||
|
||||
def installPreInspection():
|
||||
return 'ok'
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
version = '2.4'
|
||||
version_pl = getServerDir() + "/version.pl"
|
||||
if os.path.exists(version_pl):
|
||||
version = mw.readFile(version_pl)
|
||||
|
||||
|
||||
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 == 'install_pre_inspection':
|
||||
print(installPreInspection())
|
||||
elif func == 'conf':
|
||||
print(getConf())
|
||||
elif func == 'get_os':
|
||||
print(getOs())
|
||||
elif func == 'run_info':
|
||||
print(runInfo())
|
||||
elif func == 'error_log':
|
||||
print(errorLogPath())
|
||||
elif func == 'get_cfg':
|
||||
print(getCfg())
|
||||
elif func == 'set_cfg':
|
||||
print(setCfg())
|
||||
elif func == 'check':
|
||||
print(cronCheck())
|
||||
elif func == 'cron_add_check':
|
||||
print(cronAddCheck())
|
||||
elif func == 'cron_del_check':
|
||||
print(cronDelCheck())
|
||||
else:
|
||||
print('error')
|
||||
18
plugins/apache/info.json
Executable file
18
plugins/apache/info.json
Executable file
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"sort": 0,
|
||||
"title":"apache",
|
||||
"tip":"soft",
|
||||
"name":"apache",
|
||||
"type":"其他插件",
|
||||
"ps":"世界排名第一,快速、可靠并且可通过简单的API扩充",
|
||||
"shell":"install.sh",
|
||||
"install_pre_inspection":true,
|
||||
"checks":"server/apache",
|
||||
"path":"server/apache",
|
||||
"author":"apache",
|
||||
"home":"https://downloads.apache.org/httpd/",
|
||||
"date":"2026-04-11",
|
||||
"pid": "1",
|
||||
"versions": ["2.4"],
|
||||
"updates": ["2.4:66"]
|
||||
}
|
||||
16
plugins/apache/init.d/httpd.service.tpl
Normal file
16
plugins/apache/init.d/httpd.service.tpl
Normal file
@@ -0,0 +1,16 @@
|
||||
[Unit]
|
||||
Description=Apache Web Server
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
Environment=LANG=C
|
||||
ExecStart={$SERVER_PATH}/apache/httpd/bin/httpd $OPTIONS -DFOREGROUND
|
||||
ExecReload={$SERVER_PATH}/apache/httpd/bin/httpd $OPTIONS -k graceful
|
||||
KillSignal=SIGWINCH
|
||||
|
||||
KillMode=mixed
|
||||
PrivateTmp=true
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
17
plugins/apache/init.d/httpd.service2.tpl
Normal file
17
plugins/apache/init.d/httpd.service2.tpl
Normal file
@@ -0,0 +1,17 @@
|
||||
[Unit]
|
||||
Description=Apache Web Server
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
Type=forking
|
||||
PIDFile={$SERVER_PATH}/apache/httpd/logs/httpd.pid
|
||||
ExecStart={$SERVER_PATH}/apache/httpd/bin/apachectl start
|
||||
ExecReload={$SERVER_PATH}/apache/httpd/bin/apachectl graceful
|
||||
ExecStop={$SERVER_PATH}/apache/httpd/bin/apachectl stop
|
||||
PrivateTmp=true
|
||||
User=www
|
||||
Group=www
|
||||
Restart=on-failure
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
47
plugins/apache/init.d/httpd.tpl
Normal file
47
plugins/apache/init.d/httpd.tpl
Normal file
@@ -0,0 +1,47 @@
|
||||
#!/bin/bash
|
||||
# chkconfig: 345 85 15
|
||||
# description: Apache Server
|
||||
|
||||
### BEGIN INIT INFO
|
||||
# Provides: apache
|
||||
# Required-Start: $network
|
||||
# Required-Stop: $network
|
||||
# Default-Start: 2 3 4 5
|
||||
# Default-Stop: 0 1 6
|
||||
# Description: Apache Server
|
||||
### END INIT INFO
|
||||
|
||||
|
||||
PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin:/opt/homebrew/bin
|
||||
# Apache 可执行文件的路径
|
||||
DAEMON={$SERVER_PATH}/apache/httpd/bin/httpd
|
||||
# 进程的 PID 文件路径
|
||||
PIDFILE={$SERVER_PATH}/apache/httpd/logs/httpd.pid
|
||||
|
||||
# 根据传入的参数执行不同操作
|
||||
case "$1" in
|
||||
start)
|
||||
echo "Starting Apache..."
|
||||
$DAEMON -k start
|
||||
;;
|
||||
stop)
|
||||
echo "Stopping Apache..."
|
||||
$DAEMON -k stop
|
||||
;;
|
||||
restart)
|
||||
echo "Restarting Apache..."
|
||||
$DAEMON -k restart
|
||||
;;
|
||||
status)
|
||||
if [ -f $PIDFILE ]; then
|
||||
echo "Apache is running."
|
||||
else
|
||||
echo "Apache is not running."
|
||||
fi
|
||||
;;
|
||||
*)
|
||||
echo "Usage: $0 {start|stop|restart|status}"
|
||||
exit 1
|
||||
esac
|
||||
|
||||
exit 0
|
||||
87
plugins/apache/install.sh
Executable file
87
plugins/apache/install.sh
Executable file
@@ -0,0 +1,87 @@
|
||||
#!/bin/bash
|
||||
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin:/opt/homebrew/bin
|
||||
export PATH
|
||||
|
||||
# cd /Users/midoks/Desktop/mwdev/server/mdserver-web/plugins/apache && bash install.sh install 2.4
|
||||
# cd /www/server/mdserver-web/plugins/apache && bash install.sh install 2.4
|
||||
# cd /www/server/mdserver-web/plugins/apache && bash install.sh upgrade 2.4
|
||||
|
||||
# curl -I -H "Accept-Encoding: br" http://localhost
|
||||
# curl -I -H "Accept-Encoding: zstd" http://localhost
|
||||
# curl --http3 -v https://www.xxx.com
|
||||
|
||||
# apt install ncat -y
|
||||
# nc -u -v www.xx.com 443
|
||||
|
||||
# cd /Users/midoks/Desktop/mwdev/server/mdserver-web/plugins/apache && python3 plugins/apache/index.py start
|
||||
# cd /www/server/mdserver-web && python3 plugins/apache/index.py start
|
||||
# cd /www/server/mdserver-web && python3 plugins/apache/index.py run_info
|
||||
# ps -ef|grep httpd | grep -v grep | awk '{print $2}'|xargs kill
|
||||
|
||||
curPath=`pwd`
|
||||
rootPath=$(dirname "$curPath")
|
||||
rootPath=$(dirname "$rootPath")
|
||||
serverPath=$(dirname "$rootPath")
|
||||
|
||||
sysName=`uname`
|
||||
action=$1
|
||||
type=$2
|
||||
|
||||
VERSION=$2
|
||||
openrestyDir=${serverPath}/source/apache
|
||||
|
||||
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 -s /bin/bash www
|
||||
fi
|
||||
|
||||
if [ "${action}" = "upgrade" ];then
|
||||
sh -x $curPath/versions/$2/install.sh $1
|
||||
|
||||
echo "${VERSION}" > $serverPath/apache/version.pl
|
||||
|
||||
mkdir -p $serverPath/web_conf/php/conf
|
||||
echo 'set $PHP_ENV 0;' > $serverPath/web_conf/php/conf/enable-php-00.conf
|
||||
|
||||
#初始化
|
||||
cd ${rootPath} && python3 ${rootPath}/plugins/apache/index.py start
|
||||
cd ${rootPath} && python3 ${rootPath}/plugins/apache/index.py initd_install
|
||||
exit 0
|
||||
fi
|
||||
|
||||
|
||||
if [ "${2}" = "" ];then
|
||||
echo '缺少安装脚本版本...'
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [ "${action}" = "uninstall" ];then
|
||||
if [ -f /usr/lib/systemd/system/httpd.service ] || [ -f /lib/systemd/system/httpd.service ];then
|
||||
systemctl stop httpd
|
||||
rm -rf /usr/systemd/system/httpd.service
|
||||
rm -rf /lib/systemd/system/httpd.service
|
||||
systemctl daemon-reload
|
||||
fi
|
||||
|
||||
if [ -f $serverPath/apache/init.d/httpd ];then
|
||||
$serverPath/apache/init.d/httpd stop
|
||||
fi
|
||||
|
||||
rm -rf $serverPath/apache
|
||||
fi
|
||||
|
||||
sh -x $curPath/versions/$2/install.sh $1
|
||||
|
||||
if [ "${action}" = "install" ] && [ -d $serverPath/apache ];then
|
||||
echo "${VERSION}" > $serverPath/apache/version.pl
|
||||
|
||||
mkdir -p $serverPath/web_conf/php/conf
|
||||
echo 'set $PHP_ENV 0;' > $serverPath/web_conf/php/conf/enable-php-00.conf
|
||||
|
||||
#初始化
|
||||
cd ${rootPath} && python3 ${rootPath}/plugins/apache/index.py start
|
||||
cd ${rootPath} && python3 ${rootPath}/plugins/apache/index.py initd_install
|
||||
fi
|
||||
278
plugins/apache/js/httpd.js
Executable file
278
plugins/apache/js/httpd.js
Executable file
@@ -0,0 +1,278 @@
|
||||
function httpPost(method, args, callback){
|
||||
var loadT = layer.msg('正在获取...', { icon: 16, time: 0, shade: 0.3 });
|
||||
$.post('/plugins/run', {name:'apache', func:method, args:JSON.stringify(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 httpPluginService(_name, version){
|
||||
var data = {name:_name, func:'status'}
|
||||
if ( typeof(version) != 'undefined' ){
|
||||
data['version'] = version;
|
||||
} else {
|
||||
version = '';
|
||||
}
|
||||
|
||||
httpPost('status', data, function(data){
|
||||
if (data.data == 'start'){
|
||||
orPluginSetService(_name, true, version);
|
||||
} else {
|
||||
orPluginSetService(_name, false, version);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function orPluginSetService(_name ,status, version){
|
||||
var serviceCon ='<p class="status">当前状态:<span>'+(status ? '开启' : '关闭' )+
|
||||
'</span><span style="color: '+
|
||||
(status?'#20a53a;':'red;')+
|
||||
' margin-left: 3px;" class="glyphicon ' + (status?'glyphicon glyphicon-play':'glyphicon-pause')+'"></span></p><div class="sfm-opt">\
|
||||
<button class="btn btn-default btn-sm" onclick="orPluginOpService(\''+_name+'\',\''+(status?'stop':'start')+'\',\''+version+'\')">'+(status?'停止':'启动')+'</button>\
|
||||
<button class="btn btn-default btn-sm" onclick="orPluginOpService(\''+_name+'\',\'restart\',\''+version+'\',\'yes\')">重启</button>\
|
||||
<button class="btn btn-default btn-sm" onclick="orPluginOpService(\''+_name+'\',\'reload\',\''+version+'\')">重载配置</button>\
|
||||
</div>';
|
||||
$(".soft-man-con").html(serviceCon);
|
||||
}
|
||||
|
||||
|
||||
function orPluginOpService(a, b, v,request_callback) {
|
||||
|
||||
var c = "name=" + a + "&func=" + b;
|
||||
if(v != ''){
|
||||
c = c + '&version='+v;
|
||||
}
|
||||
|
||||
var d = "";
|
||||
|
||||
switch(b) {
|
||||
case "stop":d = '停止';break;
|
||||
case "start":d = '启动';break;
|
||||
case "restart":d = '重启';break;
|
||||
case "reload":d = '重载';break;
|
||||
}
|
||||
layer.confirm( msgTpl('您真的要{1}{2}{3}服务吗?', [d,a,v]), {icon:3,closeBtn: 2}, function() {
|
||||
httpPost('get_os',{},function(data){
|
||||
var rdata = $.parseJSON(data.data);
|
||||
if (!rdata['auth']){
|
||||
layer.prompt({title: '检查到权限不足,需要输入密码!', formType: 1},function(pwd, index){
|
||||
|
||||
layer.close(index);
|
||||
var data = {'pwd':pwd};
|
||||
c += '&args='+JSON.stringify(data);
|
||||
orPluginOpServiceOp(a,b,c,d,a,v,request_callback);
|
||||
});
|
||||
} else {
|
||||
orPluginOpServiceOp(a,b,c,d,a,v,request_callback);
|
||||
|
||||
}
|
||||
});
|
||||
})
|
||||
}
|
||||
|
||||
function orPluginOpServiceOp(a,b,c,d,a,v,request_callback){
|
||||
|
||||
var request_path = "/plugins/run";
|
||||
if (request_callback == 'yes'){
|
||||
request_path = "/plugins/callback";
|
||||
}
|
||||
|
||||
var e = layer.msg(msgTpl('正在{1}{2}{3}服务,请稍候...',[d,a,v]), {icon: 16,time: 0});
|
||||
$.post(request_path, c, function(g) {
|
||||
layer.close(e);
|
||||
|
||||
var f = g.data == 'ok' ? msgTpl('{1}{2}服务已{3}',[a,v,d]) : msgTpl('{1}{2}服务{3}失败!',[a,v,d]);
|
||||
layer.msg(f, {icon: g.data == 'ok' ? 1 : 2});
|
||||
|
||||
if( b != "reload" && g.data == 'ok' ) {
|
||||
if ( b == 'start' ) {
|
||||
orPluginSetService(a, true, v);
|
||||
} else if ( b == 'stop' ){
|
||||
orPluginSetService(a, false, v);
|
||||
}
|
||||
}
|
||||
|
||||
if( g.status && g.data != 'ok' ) {
|
||||
layer.msg(g.data, {icon: 2,time: 10000,shade: 0.3});
|
||||
}
|
||||
|
||||
},'json').error(function() {
|
||||
layer.close(e);
|
||||
layer.msg('操作异常!', {icon: 2});
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
// 定时器变量
|
||||
var httpdStatusTimer = null;
|
||||
|
||||
//查看Apache负载状态
|
||||
function getHttpdStatus() {
|
||||
$.post('/plugins/run', {name:'apache', func:'run_info'}, function(data) {
|
||||
try {
|
||||
var rdata = $.parseJSON(data.data);
|
||||
|
||||
console.log(rdata);
|
||||
if ('status' in rdata && !rdata.status){
|
||||
showMsg(rdata.msg, function(){}, null,3000);
|
||||
return;
|
||||
}
|
||||
|
||||
var con = "<div><table class='table table-hover table-bordered'>\
|
||||
<tr><th>服务器版本(ServerVersion)</th><td>" + (rdata.ServerVersion || '-') + "</td></tr>\
|
||||
<tr><th>服务器MPM(ServerMPM)</th><td>" + (rdata.ServerMPM || '-') + "</td></tr>\
|
||||
<tr><th>服务器构建时间(Server Built)</th><td>" + (rdata['Server Built'] || '-') + "</td></tr>\
|
||||
<tr><th>当前时间(CurrentTime)</th><td>" + (rdata.CurrentTime || '-') + "</td></tr>\
|
||||
<tr><th>重启时间(RestartTime)</th><td>" + (rdata.RestartTime || '-') + "</td></tr>\
|
||||
<tr><th>服务器运行时间(ServerUptime)</th><td>" + (rdata.ServerUptime || '-') + "</td></tr>\
|
||||
<tr><th>服务器运行秒数(ServerUptimeSeconds)</th><td>" + (rdata.ServerUptimeSeconds || '-') + "</td></tr>\
|
||||
<tr><th>1分钟负载(Load1)</th><td>" + (rdata.Load1 || '-') + "</td></tr>\
|
||||
<tr><th>5分钟负载(Load5)</th><td>" + (rdata.Load5 || '-') + "</td></tr>\
|
||||
<tr><th>15分钟负载(Load15)</th><td>" + (rdata.Load15 || '-') + "</td></tr>\
|
||||
<tr><th>总访问次数(Total Accesses)</th><td>" + (rdata['Total Accesses'] || '-') + "</td></tr>\
|
||||
<tr><th>总流量(Total kBytes)</th><td>" + (rdata['Total kBytes'] || '-') + " KB</td></tr>\
|
||||
<tr><th>总请求时间(Total Duration)</th><td>" + (rdata['Total Duration'] || '-') + "</td></tr>\
|
||||
<tr><th>CPU用户时间(CPUUser)</th><td>" + (rdata.CPUUser || '-') + "</td></tr>\
|
||||
<tr><th>CPU系统时间(CPUSystem)</th><td>" + (rdata.CPUSystem || '-') + "</td></tr>\
|
||||
<tr><th>CPU负载(CPULoad)</th><td>" + (rdata.CPULoad || '-') + "</td></tr>\
|
||||
<tr><th>每秒请求数(ReqPerSec)</th><td>" + (rdata.ReqPerSec || '-') + "</td></tr>\
|
||||
<tr><th>每秒流量(BytesPerSec)</th><td>" + (rdata.BytesPerSec || '-') + "</td></tr>\
|
||||
<tr><th>每请求流量(BytesPerReq)</th><td>" + (rdata.BytesPerReq || '-') + "</td></tr>\
|
||||
<tr><th>每请求时间(DurationPerReq)</th><td>" + (rdata.DurationPerReq || '-') + "</td></tr>\
|
||||
<tr><th>活动工作进程(BusyWorkers)</th><td>" + (rdata.BusyWorkers || '-') + "</td></tr>\
|
||||
<tr><th>优雅关闭进程(GracefulWorkers)</th><td>" + (rdata.GracefulWorkers || '-') + "</td></tr>\
|
||||
<tr><th>空闲工作进程(IdleWorkers)</th><td>" + (rdata.IdleWorkers || '-') + "</td></tr>\
|
||||
<tr><th>进程数(Processes)</th><td>" + (rdata.Processes || '-') + "</td></tr>\
|
||||
<tr><th>总连接数(ConnsTotal)</th><td>" + (rdata.ConnsTotal || '-') + "</td></tr>\
|
||||
</table></div>";
|
||||
$(".soft-man-con").html(con);
|
||||
}catch(err){
|
||||
showMsg(data.data, function(){}, null,3000);
|
||||
}
|
||||
},'json');
|
||||
}
|
||||
|
||||
// 启动自动刷新
|
||||
function startHttpdStatusAutoRefresh() {
|
||||
// 先清除现有的定时器
|
||||
if (httpdStatusTimer) {
|
||||
clearInterval(httpdStatusTimer);
|
||||
}
|
||||
|
||||
// 立即执行一次
|
||||
getHttpdStatus();
|
||||
|
||||
// 设置定时器,每5秒刷新一次
|
||||
httpdStatusTimer = setInterval(getHttpdStatus, 5000);
|
||||
}
|
||||
|
||||
// 停止自动刷新
|
||||
function stopHttpdStatusAutoRefresh() {
|
||||
if (httpdStatusTimer) {
|
||||
clearInterval(httpdStatusTimer);
|
||||
httpdStatusTimer = null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function setOpCfg(){
|
||||
httpPost('get_cfg', {}, function(data){
|
||||
var rdata = $.parseJSON(data.data);
|
||||
var rdata = rdata.data;
|
||||
// console.log(rdata);
|
||||
|
||||
var mlist = '';
|
||||
for (var i = 0; i < rdata.length; i++) {
|
||||
var w = '70'
|
||||
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;
|
||||
}
|
||||
mlist += '<p style="margin-top:15px;"><span>' + rdata[i].name + '</span>' + ibody + "<b class='unit c9'>"+rdata[i].unit+"</b>" +', <font class="c9">' + rdata[i].ps + '</font></p>';
|
||||
}
|
||||
var con = '<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="setOpCfg()">刷新</button>\
|
||||
<button class="btn btn-success btn-sm" onclick="submitConf()">保存</button>\
|
||||
</div>\
|
||||
</div>'
|
||||
$(".soft-man-con").html(con);
|
||||
});
|
||||
}
|
||||
|
||||
function submitConf() {
|
||||
var data = {};
|
||||
|
||||
// 收集所有配置参数
|
||||
$("input[name]").each(function() {
|
||||
data[$(this).attr('name')] = $(this).val();
|
||||
});
|
||||
|
||||
$("select[name]").each(function() {
|
||||
data[$(this).attr('name')] = $(this).val() || 'on';
|
||||
});
|
||||
|
||||
// console.log(data);
|
||||
httpPost('set_cfg', data, function(rdata){
|
||||
var rdata = $.parseJSON(rdata.data);
|
||||
// console.log(rdata);
|
||||
layer.msg(rdata.msg, { icon: rdata.status ? 1 : 2 });
|
||||
});
|
||||
}
|
||||
|
||||
function otherFunc(){
|
||||
var con = '<p class="conf_p" style="text-align:center;">\
|
||||
<button class="btn btn-default btn-sm" onclick="cronAddCheck()">添加检查任务</button> \
|
||||
<button class="btn btn-default btn-sm" onclick="cronDelCheck()">删除检查任务</button>\
|
||||
</p>';
|
||||
$(".soft-man-con").html(con);
|
||||
}
|
||||
|
||||
function cronAddCheck(){
|
||||
httpPost('cron_add_check', {}, function(data){
|
||||
var rdata = $.parseJSON(data.data);
|
||||
layer.msg(rdata.msg, { icon: rdata.status ? 1 : 2 });
|
||||
});
|
||||
}
|
||||
|
||||
function cronDelCheck(){
|
||||
httpPost('cron_del_check', {}, function(data){
|
||||
var rdata = $.parseJSON(data.data);
|
||||
layer.msg(rdata.msg, { icon: rdata.status ? 1 : 2 });
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
124
plugins/apache/tool_task.py
Normal file
124
plugins/apache/tool_task.py
Normal file
@@ -0,0 +1,124 @@
|
||||
# coding:utf-8
|
||||
|
||||
import sys
|
||||
import io
|
||||
import os
|
||||
import time
|
||||
import json
|
||||
|
||||
web_dir = os.getcwd() + "/web"
|
||||
if os.path.exists(web_dir):
|
||||
sys.path.append(web_dir)
|
||||
os.chdir(web_dir)
|
||||
|
||||
import core.mw as mw
|
||||
from utils.crontab import crontab as MwCrontab
|
||||
|
||||
|
||||
app_debug = False
|
||||
if mw.isAppleSystem():
|
||||
app_debug = True
|
||||
|
||||
|
||||
def getPluginName():
|
||||
return 'apache'
|
||||
|
||||
|
||||
def getPluginDir():
|
||||
return mw.getPluginDir() + '/' + getPluginName()
|
||||
|
||||
|
||||
def getServerDir():
|
||||
return mw.getServerDir() + '/' + getPluginName()
|
||||
|
||||
|
||||
def getTaskConf():
|
||||
conf = getServerDir() + "/task_config.json"
|
||||
return conf
|
||||
|
||||
|
||||
def getConfigData():
|
||||
conf = getTaskConf()
|
||||
if os.path.exists(conf):
|
||||
return json.loads(mw.readFile(getTaskConf()))
|
||||
return {
|
||||
"task_id": -1,
|
||||
"period": "minute-n",
|
||||
"where1": "3",
|
||||
"hour": "0",
|
||||
"minute": "0",
|
||||
}
|
||||
|
||||
|
||||
def createBgTask():
|
||||
removeBgTask()
|
||||
createBgTaskByName(getPluginName())
|
||||
|
||||
|
||||
def createBgTaskByName(name):
|
||||
args = getConfigData()
|
||||
_name = "[apache]检查任务"
|
||||
res = mw.M("crontab").field("id, name").where("name=?", (_name,)).find()
|
||||
if res:
|
||||
return True
|
||||
|
||||
if "task_id" in args and args["task_id"] > 0:
|
||||
res = mw.M("crontab").field("id, name").where(
|
||||
"id=?", (args["task_id"],)).find()
|
||||
if res and res["id"] == args["task_id"]:
|
||||
print("计划任务已经存在!")
|
||||
return True
|
||||
|
||||
mw_dir = mw.getPanelDir()
|
||||
cmd = '''
|
||||
mw_dir=%s
|
||||
rname=%s
|
||||
plugin_path=%s
|
||||
script_path=%s
|
||||
''' % (mw_dir, name, getServerDir(), getPluginDir())
|
||||
cmd += 'echo "bash $script_path/check.sh"' + "\n"
|
||||
cmd += 'cd $mw_dir && bash $script_path/check.sh' + "\n"
|
||||
|
||||
params = {
|
||||
'name': _name,
|
||||
'type': args['period'],
|
||||
'week': "",
|
||||
'where1': args['where1'],
|
||||
'hour': args['hour'],
|
||||
'minute': args['minute'],
|
||||
'save': "",
|
||||
'backup_to': "",
|
||||
'stype': "toShell",
|
||||
'sname': '',
|
||||
'sbody': cmd,
|
||||
'url_address': '',
|
||||
}
|
||||
|
||||
task_id = MwCrontab.instance().add(params)
|
||||
if task_id > 0:
|
||||
args["task_id"] = task_id
|
||||
args["name"] = name
|
||||
mw.writeFile(getTaskConf(), json.dumps(args))
|
||||
|
||||
|
||||
def removeBgTask():
|
||||
cfg = getConfigData()
|
||||
if "task_id" in cfg 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"]:
|
||||
data = MwCrontab.instance().delete(cfg["task_id"])
|
||||
if data["status"]:
|
||||
cfg["task_id"] = -1
|
||||
mw.writeFile(getTaskConf(), json.dumps(cfg))
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
if len(sys.argv) > 1:
|
||||
action = sys.argv[1]
|
||||
if action == "remove":
|
||||
removeBgTask()
|
||||
elif action == "add":
|
||||
createBgTask()
|
||||
172
plugins/apache/versions/2.4/install.sh
Normal file
172
plugins/apache/versions/2.4/install.sh
Normal file
@@ -0,0 +1,172 @@
|
||||
#!/bin/bash
|
||||
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin:/opt/homebrew/bin
|
||||
export PATH
|
||||
|
||||
# cd /Users/midoks/Desktop/mwdev/server/mdserver-web/plugins/apache && bash install.sh install 2.4
|
||||
# cd /www/server/mdserver-web/plugins/apache && bash install.sh install 2.4
|
||||
|
||||
curPath=`pwd`
|
||||
rootPath=$(dirname "$curPath")
|
||||
rootPath=$(dirname "$rootPath")
|
||||
serverPath=$(dirname "$rootPath")
|
||||
|
||||
sysName=`uname`
|
||||
action=$1
|
||||
type=$2
|
||||
|
||||
VERSION=2.4.68
|
||||
apacheDir=${serverPath}/source/apache
|
||||
|
||||
Install_App()
|
||||
{
|
||||
if [ "${action}" = "install" ];then
|
||||
if [ -d $serverPath/apache ];then
|
||||
exit 0
|
||||
fi
|
||||
fi
|
||||
|
||||
# ----- cpu start ------
|
||||
if [ -z "${cpuCore}" ]; then
|
||||
cpuCore="1"
|
||||
fi
|
||||
|
||||
if [ -f /proc/cpuinfo ];then
|
||||
cpuCore=`cat /proc/cpuinfo | grep "processor" | wc -l`
|
||||
fi
|
||||
|
||||
MEM_INFO=$(free -m|grep Mem|awk '{printf("%.f",($2)/1024)}')
|
||||
if [ "${cpuCore}" != "1" ] && [ "${MEM_INFO}" != "0" ];then
|
||||
if [ "${cpuCore}" -gt "${MEM_INFO}" ];then
|
||||
cpuCore="${MEM_INFO}"
|
||||
fi
|
||||
else
|
||||
cpuCore="1"
|
||||
fi
|
||||
|
||||
if [ "$cpuCore" -gt "2" ];then
|
||||
cpuCore=`echo "$cpuCore" | awk '{printf("%.f",($1)*0.8)}'`
|
||||
else
|
||||
cpuCore="1"
|
||||
fi
|
||||
# ----- cpu end ------
|
||||
|
||||
mkdir -p ${apacheDir}
|
||||
echo 'install script ...'
|
||||
OPTIONS=''
|
||||
|
||||
# 安装 APR 和 APR-util
|
||||
APR_VERSION=1.7.6
|
||||
APR_UTIL_VERSION=1.6.3
|
||||
|
||||
if [ ! -f ${apacheDir}/apr-${APR_VERSION}.tar.bz2 ];then
|
||||
wget --no-check-certificate -O ${apacheDir}/apr-${APR_VERSION}.tar.bz2 https://downloads.apache.org/apr/apr-${APR_VERSION}.tar.bz2 -T 3
|
||||
fi
|
||||
|
||||
if [ ! -f ${apacheDir}/apr-util-${APR_UTIL_VERSION}.tar.bz2 ];then
|
||||
wget --no-check-certificate -O ${apacheDir}/apr-util-${APR_UTIL_VERSION}.tar.bz2 https://downloads.apache.org/apr/apr-util-${APR_UTIL_VERSION}.tar.bz2 -T 3
|
||||
fi
|
||||
|
||||
# 安装 APR
|
||||
if [ ! -d ${serverPath}/apache/apr ];then
|
||||
cd ${apacheDir} && tar -jxf apr-${APR_VERSION}.tar.bz2
|
||||
cd ${apacheDir}/apr-${APR_VERSION} && ./configure --prefix=${serverPath}/apache/apr
|
||||
make -j${cpuCore} && make install
|
||||
if [ "$?" = "0" ];then
|
||||
# 检查 APR 配置文件
|
||||
APR_CONFIG=$(find ${serverPath}/apache/apr -name "apr-*config" | head -1)
|
||||
if [ -z "$APR_CONFIG" ];then
|
||||
echo "APR installation failed: apr-config not found"
|
||||
exit 1
|
||||
fi
|
||||
echo "APR installed successfully: $APR_CONFIG"
|
||||
else
|
||||
echo "APR configure failed"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
# 安装 APR-util
|
||||
if [ ! -d ${serverPath}/apache/apr-util ];then
|
||||
cd ${apacheDir} && tar -jxf apr-util-${APR_UTIL_VERSION}.tar.bz2
|
||||
cd ${apacheDir}/apr-util-${APR_UTIL_VERSION} && ./configure --prefix=${serverPath}/apache/apr-util --with-apr=${serverPath}/apache/apr
|
||||
make -j${cpuCore} && make install
|
||||
if [ "$?" = "0" ];then
|
||||
# 检查 APR-util 配置文件
|
||||
APU_CONFIG=$(find ${serverPath}/apache/apr-util -name "apu-*config" | head -1)
|
||||
if [ -z "$APU_CONFIG" ];then
|
||||
echo "APR-util installation failed: apu-config not found"
|
||||
find ${serverPath}/apache/apr-util -name "apu-*config"
|
||||
exit 1
|
||||
fi
|
||||
echo "APR-util installed successfully: $APU_CONFIG"
|
||||
else
|
||||
echo "APR-util configure failed"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ ! -f ${apacheDir}/httpd-${VERSION}.tar.bz2 ];then
|
||||
wget --no-check-certificate -O ${apacheDir}/httpd-${VERSION}.tar.bz2 https://downloads.apache.org/httpd/httpd-${VERSION}.tar.bz2 -T 3
|
||||
fi
|
||||
|
||||
if [ ! -d ${apacheDir}/httpd-${VERSION} ];then
|
||||
cd ${apacheDir} && tar -jxf ${apacheDir}/httpd-${VERSION}.tar.bz2
|
||||
fi
|
||||
|
||||
|
||||
# 检查 APR 和 APR-util 可执行文件
|
||||
APR_CONFIG=$(find ${serverPath}/apache/apr -name "apr-*config" | head -1)
|
||||
APU_CONFIG=$(find ${serverPath}/apache/apr-util -name "apu-*config" | head -1)
|
||||
|
||||
if [ -z "$APR_CONFIG" ];then
|
||||
echo "APR config not found"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ -z "$APU_CONFIG" ];then
|
||||
echo "APR-util config not found"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
OPTIONS="${OPTIONS} --with-apr=$APR_CONFIG"
|
||||
OPTIONS="${OPTIONS} --with-apr-util=$APU_CONFIG"
|
||||
|
||||
# if [ "$sysName" != "Darwin" ];then
|
||||
# OPTIONS="${OPTIONS} --enable-systemd"
|
||||
# fi
|
||||
# echo "cd ${apacheDir}/httpd-${VERSION} && ./configure --prefix=$serverPath/apache $OPTIONS"
|
||||
cd ${apacheDir}/httpd-${VERSION} && ./configure \
|
||||
--prefix=$serverPath/apache/httpd \
|
||||
$OPTIONS
|
||||
|
||||
make -j${cpuCore} && make install && make clean
|
||||
|
||||
|
||||
if [ -d ${apacheDir}/apr-${APR_VERSION} ];then
|
||||
rm -rf ${apacheDir}/apr-${APR_VERSION}
|
||||
fi
|
||||
|
||||
if [ -d ${apacheDir}/apr-util-${APR_UTIL_VERSION} ];then
|
||||
rm -rf ${apacheDir}/apr-util-${APR_UTIL_VERSION}
|
||||
fi
|
||||
|
||||
if [ -d ${apacheDir}/httpd-${APR_UTIL_VERSION} ];then
|
||||
rm -rf ${apacheDir}/httpd-${APR_UTIL_VERSION}
|
||||
fi
|
||||
|
||||
echo 'installation of apache completed'
|
||||
}
|
||||
|
||||
Uninstall_App()
|
||||
{
|
||||
echo 'uninstalling apache completed'
|
||||
}
|
||||
|
||||
action=$1
|
||||
if [ "${1}" = "install" ];then
|
||||
Install_App
|
||||
elif [ "${1}" = "upgrade" ];then
|
||||
Install_App
|
||||
else
|
||||
Uninstall_App
|
||||
fi
|
||||
@@ -14,8 +14,12 @@ import json
|
||||
import paramiko
|
||||
import ftplib
|
||||
|
||||
sys.path.append(os.getcwd() + "/class/core")
|
||||
import mw
|
||||
web_dir = os.getcwd() + "/web"
|
||||
if os.path.exists(web_dir):
|
||||
sys.path.append(web_dir)
|
||||
os.chdir(web_dir)
|
||||
|
||||
import core.mw as mw
|
||||
|
||||
DEBUG = True
|
||||
BLOCK_SIZE = 1024 * 1024 * 2
|
||||
@@ -58,7 +62,7 @@ class FtpPSClient:
|
||||
default_backup_path = "/backup/"
|
||||
config_file = "cfg.json"
|
||||
|
||||
def __init__(self, load_config=True, timeout=10):
|
||||
def __init__(self, load_config=True, timeout=600):
|
||||
self.timeout = timeout
|
||||
if load_config:
|
||||
data = self.get_config()
|
||||
@@ -136,7 +140,7 @@ class FtpPSClient:
|
||||
"database": "db",
|
||||
"path": "path",
|
||||
}
|
||||
file_regx = prefix_dict.get(data_type) + "_(.+)_20\d+_\d+\."
|
||||
file_regx = prefix_dict.get(data_type) + r"_(.+)_20\d+_\d+\."
|
||||
sub_search = re.search(file_regx, file_name)
|
||||
sub_path_name = ""
|
||||
if sub_search:
|
||||
|
||||
@@ -12,19 +12,13 @@ if sys.platform != "darwin":
|
||||
os.chdir("/www/server/mdserver-web")
|
||||
|
||||
|
||||
sys.path.append(os.getcwd() + "/class/core")
|
||||
import mw
|
||||
import db
|
||||
web_dir = os.getcwd() + "/web"
|
||||
if os.path.exists(web_dir):
|
||||
sys.path.append(web_dir)
|
||||
os.chdir(web_dir)
|
||||
|
||||
_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')
|
||||
import core.mw as mw
|
||||
import core.db as db
|
||||
|
||||
app_debug = False
|
||||
if mw.isAppleSystem():
|
||||
@@ -77,7 +71,10 @@ def status():
|
||||
def getConf():
|
||||
cfg = getServerDir() + "/cfg.json"
|
||||
if not os.path.exists(cfg):
|
||||
return mw.returnJson(False, "未配置", [])
|
||||
rdata = {
|
||||
'use_sftp':False,
|
||||
}
|
||||
return mw.returnJson(False, "未配置", rdata)
|
||||
data = mw.readFile(cfg)
|
||||
data = json.loads(data)
|
||||
return mw.returnJson(True, "OK", data)
|
||||
@@ -173,9 +170,17 @@ def deleteFile():
|
||||
return mw.returnJson(True, "删除成功")
|
||||
return mw.returnJson(False, "删除失败")
|
||||
|
||||
def findPathName(path, filename):
|
||||
f = os.scandir(path)
|
||||
l = []
|
||||
for ff in f:
|
||||
t = {}
|
||||
if ff.name.find(filename) > -1:
|
||||
t['filename'] = path + '/' + ff.name
|
||||
l.append(t)
|
||||
return l
|
||||
|
||||
def backupAllFunc(stype):
|
||||
os.chdir(mw.getRunDir())
|
||||
|
||||
name = sys.argv[2]
|
||||
num = sys.argv[3]
|
||||
@@ -187,21 +192,17 @@ def backupAllFunc(stype):
|
||||
}
|
||||
|
||||
backups = []
|
||||
sql = db.Sql()
|
||||
|
||||
# print("stype:", stype)
|
||||
# 提前获取-清理多余备份
|
||||
if stype == 'site':
|
||||
pid = sql.table('sites').where('name=?', (name,)).getField('id')
|
||||
backups = sql.table('backup').where(
|
||||
'type=? and pid=?', ('0', pid)).field('id,filename').select()
|
||||
pid = mw.M('sites').where('name=?', (name,)).getField('id')
|
||||
backups = mw.M('backup').where('type=? and pid=?', ('0', pid)).field('id,filename').select()
|
||||
if stype == 'database':
|
||||
db_path = mw.getServerDir() + '/mysql'
|
||||
pid = mw.M('databases').dbPos(db_path, 'mysql').where(
|
||||
'name=?', (name,)).getField('id')
|
||||
backups = sql.table('backup').where(
|
||||
'type=? and pid=?', ('1', pid)).field('id,filename').select()
|
||||
pid = mw.M('databases').dbPos(db_path, 'mysql').where('name=?', (name,)).getField('id')
|
||||
backups = mw.M('backup').where('type=? and pid=?', ('1', pid)).field('id,filename').select()
|
||||
if stype == 'path':
|
||||
backup_dir = mw.getBackupDir()
|
||||
backup_path = backup_dir + '/path'
|
||||
_name = 'path_{}'.format(os.path.basename(name))
|
||||
backups = findPathName(backup_path, _name)
|
||||
@@ -210,18 +211,15 @@ def backupAllFunc(stype):
|
||||
if stype.find('database_') > -1:
|
||||
plugin_name = stype.replace('database_', '')
|
||||
db_path = mw.getServerDir() + '/' + plugin_name
|
||||
pid = mw.M('databases').dbPos(db_path, 'mysql').where(
|
||||
'name=?', (name,)).getField('id')
|
||||
backups = sql.table('backup').where(
|
||||
'type=? and pid=?', ('1', pid)).field('id,filename').select()
|
||||
pid = mw.M('databases').dbPos(db_path, 'mysql').where('name=?', (name,)).getField('id')
|
||||
backups = mw.M('backup').where('type=? and pid=?', ('1', pid)).field('id,filename').select()
|
||||
|
||||
args = stype + " " + name + " " + num
|
||||
cmd = 'python3 ' + mw.getRunDir() + '/scripts/backup.py ' + args
|
||||
cmd = 'python3 ' + mw.getPanelDir() + '/scripts/backup.py ' + args
|
||||
if stype.find('database_') > -1:
|
||||
plugin_name = stype.replace('database_', '')
|
||||
args = "database " + name + " " + num
|
||||
cmd = 'python3 ' + mw.getRunDir() + '/plugins/' + plugin_name + \
|
||||
'/scripts/backup.py ' + args
|
||||
cmd = 'python3 ' + mw.getPanelDir() + '/plugins/' + plugin_name + '/scripts/backup.py ' + args
|
||||
|
||||
os.system(cmd)
|
||||
|
||||
@@ -237,15 +235,18 @@ def backupAllFunc(stype):
|
||||
bk_name = stype
|
||||
|
||||
find_path = mw.getBackupDir() + '/' + bk_name + '/' + bk_prefix + '_' + name
|
||||
find_new_file = "ls " + find_path + \
|
||||
"_* | grep '.gz' | cut -d \ -f 1 | awk 'END {print}'"
|
||||
if stype == 'path':
|
||||
_name = 'path_{}'.format(os.path.basename(name))
|
||||
find_path = mw.getBackupDir() + '/path/'+_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 == "":
|
||||
mw.echoInfo("not find upload file!")
|
||||
return ''
|
||||
|
||||
print("|-准备上传文件 {}".format(filename))
|
||||
mw.echoInfo("准备上传文件 {}".format(filename))
|
||||
ftp = FtpPSClient()
|
||||
ftp.uploadFile(filename, stype)
|
||||
|
||||
@@ -259,7 +260,7 @@ def backupAllFunc(stype):
|
||||
fn = os.path.basename(backup['filename'])
|
||||
object_name = ftp.buildDirName(stype, fn)
|
||||
ftp.deleteFile(object_name)
|
||||
mw.echoInfo("---已清理远程过期备份文件:" + object_name)
|
||||
mw.echoInfo("已清理远程过期备份文件:" + object_name)
|
||||
sep -= 1
|
||||
if sep < 0:
|
||||
break
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
{
|
||||
"sort":6,
|
||||
"title":"FTP存储空间",
|
||||
"hook":["backup"],
|
||||
"tip":"soft",
|
||||
@@ -13,5 +14,5 @@
|
||||
"author":"midoks",
|
||||
"home":"https://github.com/midoks/mdserver-web",
|
||||
"date":"2022-10-23",
|
||||
"pid": "4"
|
||||
"pid": "5"
|
||||
}
|
||||
@@ -7,7 +7,6 @@ rootPath=$(dirname "$curPath")
|
||||
rootPath=$(dirname "$rootPath")
|
||||
serverPath=$(dirname "$rootPath")
|
||||
|
||||
install_tmp=${rootPath}/tmp/mw_install.pl
|
||||
sys_os=`uname`
|
||||
|
||||
|
||||
@@ -15,7 +14,7 @@ Install_App()
|
||||
{
|
||||
mkdir -p ${serverPath}/backup_ftp
|
||||
echo "${1}" > ${serverPath}/backup_ftp/version.pl
|
||||
echo '安装完成' > $install_tmp
|
||||
echo '安装完成'
|
||||
|
||||
}
|
||||
|
||||
@@ -25,7 +24,7 @@ Uninstall_App()
|
||||
}
|
||||
|
||||
action=$1
|
||||
if [ "${1}" == 'install' ];then
|
||||
if [ "${1}" = 'install' ];then
|
||||
Install_App $2
|
||||
else
|
||||
Uninstall_App $2
|
||||
|
||||
35
plugins/caddy/check.sh
Executable file
35
plugins/caddy/check.sh
Executable file
@@ -0,0 +1,35 @@
|
||||
#!/bin/bash
|
||||
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin:/opt/homebrew/bin
|
||||
export PATH
|
||||
|
||||
# OpenResty服务名称
|
||||
service_name="caddy"
|
||||
|
||||
# 检查OpenResty是否正在运行
|
||||
if systemctl is-active --quiet "$service_name"; then
|
||||
# 检查是否存在僵尸进程
|
||||
zombie_processes=$(ps -ef | grep -i caddy | grep -v grep | awk '{print $2}' | xargs ps -o state= -p 2>/dev/null | grep -c Z)
|
||||
if [ "$zombie_processes" -gt 0 ]; then
|
||||
echo "kill caddy 僵尸进程"
|
||||
ps -ef|grep caddy| grep -v grep| awk '{print $2}' | xargs kill -9
|
||||
echo "检测到OpenResty僵尸进程,正在重启服务..."
|
||||
systemctl restart "$service_name"
|
||||
echo "服务已重启"
|
||||
else
|
||||
echo "OpenResty运行正常"
|
||||
fi
|
||||
else
|
||||
echo "kill caddy"
|
||||
ps -ef|grep caddy| grep -v grep| awk '{print $2}' | xargs kill -9
|
||||
echo "caddy未运行,正在启动服务..."
|
||||
systemctl start "$service_name"
|
||||
echo "服务已启动"
|
||||
fi
|
||||
|
||||
NGINX_IDS=`ps -ef|grep caddy | grep -v grep| awk '{print $2}'`
|
||||
if [ "$NGINX_IDS" = "" ];then
|
||||
ps -ef|grep caddy| grep -v grep| awk '{print $2}' | xargs kill -9
|
||||
systemctl start "$service_name"
|
||||
echo "caddy未运行,正在启动服务..."
|
||||
fi
|
||||
|
||||
13
plugins/caddy/conf/Caddyfile
Normal file
13
plugins/caddy/conf/Caddyfile
Normal file
@@ -0,0 +1,13 @@
|
||||
:8080 {
|
||||
# Set this path to your site's directory.
|
||||
root * /tmp
|
||||
|
||||
# Enable the static file server.
|
||||
file_server
|
||||
|
||||
# Another common task is to set up a reverse proxy:
|
||||
# reverse_proxy localhost:8080
|
||||
|
||||
# Or serve a PHP site through php-fpm:
|
||||
# php_fastcgi localhost:9000
|
||||
}
|
||||
BIN
plugins/caddy/ico.png
Normal file
BIN
plugins/caddy/ico.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 3.5 KiB |
24
plugins/caddy/index.html
Executable file
24
plugins/caddy/index.html
Executable file
@@ -0,0 +1,24 @@
|
||||
<div class="bt-form">
|
||||
<!-- <div class='plugin_version'></div> -->
|
||||
<div class="bt-w-main">
|
||||
<div class="bt-w-menu">
|
||||
<p class="bgw" onclick="orPluginService('caddy',$('.plugin_version').attr('version'));">服务</p>
|
||||
<p onclick="pluginInitD('caddy', $('.plugin_version').attr('version'));">自启动</p>
|
||||
<p onclick="pluginConfig('caddy');">配置修改</p>
|
||||
<p onclick="getOpStatus();">负载状态</p>
|
||||
<p onclick="setOpCfg();">性能调整</p>
|
||||
<p onclick="pluginLogs('caddy');">错误日志</p>
|
||||
<p onclick="otherFunc();">维护功能</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=caddy&f=js/caddy.js", function(){
|
||||
orPluginService('caddy');
|
||||
});
|
||||
</script>
|
||||
445
plugins/caddy/index.py
Executable file
445
plugins/caddy/index.py
Executable file
@@ -0,0 +1,445 @@
|
||||
# coding:utf-8
|
||||
|
||||
import sys
|
||||
import io
|
||||
import os
|
||||
import time
|
||||
import threading
|
||||
import subprocess
|
||||
import re
|
||||
|
||||
|
||||
web_dir = os.getcwd() + "/web"
|
||||
if os.path.exists(web_dir):
|
||||
sys.path.append(web_dir)
|
||||
os.chdir(web_dir)
|
||||
|
||||
import core.mw as mw
|
||||
|
||||
app_debug = False
|
||||
if mw.isAppleSystem():
|
||||
app_debug = True
|
||||
|
||||
|
||||
def getPluginName():
|
||||
return 'caddy'
|
||||
|
||||
|
||||
def getPluginDir():
|
||||
return mw.getPluginDir() + '/' + getPluginName()
|
||||
|
||||
|
||||
def getServerDir():
|
||||
return mw.getServerDir() + '/' + getPluginName()
|
||||
|
||||
|
||||
def getInitDFile():
|
||||
current_os = mw.getOs()
|
||||
if current_os == 'darwin':
|
||||
return '/tmp/' + getPluginName()
|
||||
|
||||
if current_os.startswith('freebsd'):
|
||||
return '/etc/rc.d/' + getPluginName()
|
||||
|
||||
return '/etc/init.d/' + getPluginName()
|
||||
|
||||
|
||||
def getArgs():
|
||||
args = sys.argv[2:]
|
||||
# print(args)
|
||||
tmp = {}
|
||||
args_len = len(args)
|
||||
|
||||
if args_len == 1:
|
||||
t = args[0].strip('{').strip('}')
|
||||
t = t.split(':',2)
|
||||
tmp[t[0]] = t[1]
|
||||
elif args_len > 1:
|
||||
for i in range(len(args)):
|
||||
t = args[i].split(':',2)
|
||||
tmp[t[0]] = t[1]
|
||||
# print(tmp)
|
||||
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 getOs():
|
||||
data = {}
|
||||
data['os'] = mw.getOs()
|
||||
data['auth'] = True
|
||||
return mw.getJson(data)
|
||||
|
||||
def getConf():
|
||||
path = getServerDir() + "/Caddyfile"
|
||||
return path
|
||||
|
||||
|
||||
def getConfTpl():
|
||||
path = getPluginDir() + '/conf/Caddyfile'
|
||||
return path
|
||||
|
||||
|
||||
def getInitDTpl():
|
||||
path = getPluginDir() + "/init.d/caddy.tpl"
|
||||
return path
|
||||
|
||||
def initDreplace():
|
||||
file_tpl = getInitDTpl()
|
||||
service_path = mw.getServerDir()
|
||||
initD_path = getServerDir() + '/init.d'
|
||||
|
||||
# init.d
|
||||
file_bin = initD_path + '/' + getPluginName()
|
||||
|
||||
if not os.path.exists(initD_path):
|
||||
os.mkdir(initD_path)
|
||||
|
||||
# initd replace
|
||||
content = mw.readFile(file_tpl)
|
||||
content = content.replace('{$SERVER_PATH}', service_path)
|
||||
mw.writeFile(file_bin, content)
|
||||
mw.execShell('chmod +x ' + file_bin)
|
||||
|
||||
caddy_file = getConf()
|
||||
if not os.path.exists(caddy_file):
|
||||
caddy_file_tpl = getConfTpl()
|
||||
content = mw.readFile(caddy_file_tpl)
|
||||
content = content.replace('{$SERVER_PATH}', service_path)
|
||||
mw.writeFile(caddy_file, content)
|
||||
|
||||
# systemd
|
||||
# /usr/lib/systemd/system
|
||||
systemDir = mw.systemdCfgDir()
|
||||
systemService = systemDir + '/caddy.service'
|
||||
if os.path.exists(systemDir) and not os.path.exists(systemService):
|
||||
systemServiceTpl = getPluginDir() + '/init.d/caddy.service.tpl'
|
||||
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 status():
|
||||
cmd = "ps -ef|grep 'caddy run' |grep -v grep | grep -v python | awk '{print $2}'"
|
||||
data = mw.execShell(cmd)
|
||||
if data[0] == '':
|
||||
return 'stop'
|
||||
return 'start'
|
||||
|
||||
|
||||
def caddyOp(method):
|
||||
file = initDreplace()
|
||||
|
||||
current_os = mw.getOs()
|
||||
if current_os == "darwin":
|
||||
data = mw.execShell(file + ' ' + method)
|
||||
if data[1] == '':
|
||||
return 'ok'
|
||||
return data[1]
|
||||
|
||||
if current_os.startswith("freebsd"):
|
||||
data = mw.execShell('service caddy ' + method)
|
||||
if data[1] == '':
|
||||
return 'ok'
|
||||
return data[1]
|
||||
|
||||
data = mw.execShell('systemctl ' + method + ' caddy')
|
||||
if data[1] == '':
|
||||
return 'ok'
|
||||
return data[1]
|
||||
|
||||
def start():
|
||||
return caddyOp('start')
|
||||
|
||||
|
||||
def stop():
|
||||
r = caddyOp('stop')
|
||||
return r
|
||||
|
||||
|
||||
def restart():
|
||||
return restyOp_restart()
|
||||
|
||||
|
||||
def reload():
|
||||
confReplace()
|
||||
return caddyOp('reload')
|
||||
|
||||
|
||||
def initdStatus():
|
||||
current_os = mw.getOs()
|
||||
if current_os == 'darwin':
|
||||
return "Apple Computer does not support"
|
||||
|
||||
if current_os.startswith('freebsd'):
|
||||
initd_bin = getInitDFile()
|
||||
if os.path.exists(initd_bin):
|
||||
return 'ok'
|
||||
|
||||
shell_cmd = 'systemctl status caddy | grep loaded | grep "enabled;"'
|
||||
data = mw.execShell(shell_cmd)
|
||||
if data[0] == '':
|
||||
return 'fail'
|
||||
return 'ok'
|
||||
|
||||
|
||||
def initdInstall():
|
||||
current_os = mw.getOs()
|
||||
if current_os == 'darwin':
|
||||
return "Apple Computer does not support"
|
||||
|
||||
# freebsd initd install
|
||||
if current_os.startswith('freebsd'):
|
||||
import shutil
|
||||
source_bin = initDreplace()
|
||||
initd_bin = getInitDFile()
|
||||
shutil.copyfile(source_bin, initd_bin)
|
||||
mw.execShell('chmod +x ' + initd_bin)
|
||||
mw.execShell('sysrc ' + getPluginName() + '_enable="YES"')
|
||||
return 'ok'
|
||||
|
||||
mw.execShell('systemctl enable caddy')
|
||||
return 'ok'
|
||||
|
||||
|
||||
def initdUinstall():
|
||||
current_os = mw.getOs()
|
||||
if current_os == 'darwin':
|
||||
return "Apple Computer does not support"
|
||||
|
||||
if current_os.startswith('freebsd'):
|
||||
initd_bin = getInitDFile()
|
||||
os.remove(initd_bin)
|
||||
mw.execShell('sysrc ' + getPluginName() + '_enable="NO"')
|
||||
return 'ok'
|
||||
|
||||
mw.execShell('systemctl disable caddy')
|
||||
return 'ok'
|
||||
|
||||
|
||||
def runInfo():
|
||||
op_status = status()
|
||||
if op_status == 'stop':
|
||||
return mw.returnJson(False, "未启动!")
|
||||
|
||||
|
||||
def errorLogPath():
|
||||
return getServerDir() + '/nginx/logs/error.log'
|
||||
|
||||
|
||||
def getCfg():
|
||||
cfg = getConf()
|
||||
content = mw.readFile(cfg)
|
||||
|
||||
unitrep = "[kmgKMG]"
|
||||
cfg_args = [
|
||||
{"name": "worker_processes", "ps": "处理进程,auto表示自动,数字表示进程数", 'type': 2},
|
||||
{"name": "worker_connections", "ps": "最大并发链接数", 'type': 2},
|
||||
{"name": "keepalive_timeout", "ps": "连接超时时间", 'type': 2},
|
||||
{"name": "zstd", "ps": "是否开启zstd压缩传输", 'type': 1},
|
||||
{"name": "brotli", "ps": "是否开启brotli压缩传输", 'type': 1},
|
||||
{"name": "gzip", "ps": "是否开启gzip压缩传输", 'type': 1},
|
||||
{"name": "gzip_min_length", "ps": "最小压缩文件", 'type': 2},
|
||||
{"name": "gzip_comp_level", "ps": "压缩率", 'type': 2},
|
||||
{"name": "client_max_body_size", "ps": "最大上传文件", 'type': 2},
|
||||
{"name": "server_names_hash_bucket_size",
|
||||
"ps": "服务器名字的hash表大小", 'type': 2},
|
||||
{"name": "client_header_buffer_size", "ps": "客户端请求头buffer大小", 'type': 2},
|
||||
]
|
||||
|
||||
# {"name": "client_body_buffer_size", "ps": "请求主体缓冲区"}
|
||||
rdata = []
|
||||
for i in cfg_args:
|
||||
rep = r"(%s)\s+(\w+)" % i["name"]
|
||||
k = re.search(rep, content)
|
||||
if not k:
|
||||
return mw.returnJson(False, "获取 key {} 失败".format(k))
|
||||
k = k.group(1)
|
||||
v = re.search(rep, content)
|
||||
if not v:
|
||||
return mw.returnJson(False, "获取 value {} 失败".format(v))
|
||||
v = v.group(2)
|
||||
|
||||
if re.search(unitrep, v):
|
||||
u = str.upper(v[-1])
|
||||
v = v[:-1]
|
||||
if len(u) == 1:
|
||||
psstr = u + "B," + i["ps"]
|
||||
else:
|
||||
psstr = u + "," + i["ps"]
|
||||
else:
|
||||
u = ""
|
||||
|
||||
kv = {"name": k, "value": v, "unit": u,
|
||||
"ps": i["ps"], "type": i["type"]}
|
||||
rdata.append(kv)
|
||||
|
||||
return mw.returnJson(True, "ok", rdata)
|
||||
|
||||
def replaceChar(value, index, new_char):
|
||||
return value[:index] + new_char + value[index+1:]
|
||||
|
||||
def makeWorkerCpuAffinity(val):
|
||||
if val == "auto":
|
||||
return "auto"
|
||||
|
||||
if mw.isNumber(val):
|
||||
core_num = int(val)
|
||||
default_core_str = "0"*core_num
|
||||
core_num_arr = []
|
||||
for x in range(core_num):
|
||||
t = replaceChar(default_core_str, x , "1")
|
||||
core_num_arr.append(t)
|
||||
return " ".join(core_num_arr)
|
||||
|
||||
return 'auto'
|
||||
|
||||
def setCfg():
|
||||
|
||||
args = getArgs()
|
||||
data = checkArgs(args, [
|
||||
'worker_processes', 'worker_connections', 'keepalive_timeout','zstd','brotli',
|
||||
'gzip', 'gzip_min_length', 'gzip_comp_level', 'client_max_body_size',
|
||||
'server_names_hash_bucket_size', 'client_header_buffer_size'
|
||||
])
|
||||
if not data[0]:
|
||||
return data[1]
|
||||
|
||||
cfg = getConf()
|
||||
mw.backFile(cfg)
|
||||
content = mw.readFile(cfg)
|
||||
|
||||
unitrep = "[kmgKMG]"
|
||||
cfg_args = [
|
||||
{"name": "worker_processes", "ps": "处理进程,auto表示自动,数字表示进程数", 'type': 2},
|
||||
{"name": "worker_connections", "ps": "最大并发链接数", 'type': 2},
|
||||
{"name": "keepalive_timeout", "ps": "连接超时时间", 'type': 2},
|
||||
{"name": "zstd", "ps": "是否开启zstd压缩传输", 'type': 1},
|
||||
{"name": "brotli", "ps": "是否开启brotli压缩传输", 'type': 1},
|
||||
{"name": "gzip", "ps": "是否开启压缩传输", 'type': 1},
|
||||
{"name": "gzip_min_length", "ps": "最小压缩文件", 'type': 2},
|
||||
{"name": "gzip_comp_level", "ps": "压缩率", 'type': 2},
|
||||
{"name": "client_max_body_size", "ps": "最大上传文件", 'type': 2},
|
||||
{"name": "server_names_hash_bucket_size",
|
||||
"ps": "服务器名字的hash表大小", 'type': 2},
|
||||
{"name": "client_header_buffer_size", "ps": "客户端请求头buffer大小", 'type': 2},
|
||||
]
|
||||
|
||||
# print(args)
|
||||
for k, v in args.items():
|
||||
# print(k, v)
|
||||
rep = r"%s\s+[^kKmMgG\;\n]+" % k
|
||||
if k == "worker_processes" or k == "gzip":
|
||||
if not re.search(r"auto|on|off|\d+", v):
|
||||
return mw.returnJson(False, '参数值错误')
|
||||
elif k == "zstd" or k == "brotli":
|
||||
if not re.search(r"auto|on|off|\d+", v):
|
||||
return mw.returnJson(False, '参数值错误')
|
||||
else:
|
||||
if not re.search(r"\d+", v):
|
||||
return mw.returnJson(False, '参数值错误,请输入数字整数')
|
||||
|
||||
if k == "worker_processes" :
|
||||
k_wca = "worker_cpu_affinity"
|
||||
rep_wca = r"%s\s+[^\;\n]+" % k_wca
|
||||
v_wca = makeWorkerCpuAffinity(v)
|
||||
newconf = "%s %s" % (k_wca, v_wca)
|
||||
content = re.sub(rep_wca, newconf, content)
|
||||
|
||||
|
||||
if re.search(rep, content):
|
||||
newconf = "%s %s" % (k, v)
|
||||
content = re.sub(rep, newconf, content)
|
||||
elif re.search(rep, content):
|
||||
newconf = "%s %s" % (k, v)
|
||||
content = re.sub(rep, newconf, content)
|
||||
|
||||
mw.writeFile(cfg, content)
|
||||
isError = mw.checkWebConfig()
|
||||
if (isError != True):
|
||||
mw.restoreFile(cfg)
|
||||
return mw.returnJson(False, 'ERROR: 配置出错<br><a style="color:red;">' + isError.replace("\n", '<br>') + '</a>')
|
||||
|
||||
mw.restartWeb()
|
||||
return mw.returnJson(True, '设置成功')
|
||||
|
||||
|
||||
def cronAddCheck():
|
||||
try:
|
||||
import tool_task
|
||||
tool_task.createBgTask()
|
||||
return mw.returnJson(True, '添加检查任务成功')
|
||||
except Exception as e:
|
||||
return mw.returnJson(False, '添加检查任务失败:'+str(e))
|
||||
|
||||
def cronDelCheck():
|
||||
try:
|
||||
import tool_task
|
||||
tool_task.removeBgTask()
|
||||
return mw.returnJson(True, '删除检查任务成功')
|
||||
except Exception as e:
|
||||
return mw.returnJson(False, '删除检查任务失败:'+str(e))
|
||||
|
||||
def cronCheck():
|
||||
return 'ok'
|
||||
|
||||
|
||||
def installPreInspection():
|
||||
return 'ok'
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
version = '1.27.1'
|
||||
version_pl = getServerDir() + "/version.pl"
|
||||
if os.path.exists(version_pl):
|
||||
version = mw.readFile(version_pl)
|
||||
|
||||
|
||||
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 == 'install_pre_inspection':
|
||||
print(installPreInspection())
|
||||
elif func == 'conf':
|
||||
print(getConf())
|
||||
elif func == 'get_os':
|
||||
print(getOs())
|
||||
elif func == 'run_info':
|
||||
print(runInfo())
|
||||
elif func == 'error_log':
|
||||
print(errorLogPath())
|
||||
elif func == 'get_cfg':
|
||||
print(getCfg())
|
||||
elif func == 'set_cfg':
|
||||
print(setCfg())
|
||||
elif func == 'check':
|
||||
print(cronCheck())
|
||||
elif func == 'cron_add_check':
|
||||
print(cronAddCheck())
|
||||
elif func == 'cron_del_check':
|
||||
print(cronDelCheck())
|
||||
else:
|
||||
print('error')
|
||||
18
plugins/caddy/info.json
Executable file
18
plugins/caddy/info.json
Executable file
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"sort": 0,
|
||||
"title":"caddy",
|
||||
"tip":"soft",
|
||||
"name":"caddy",
|
||||
"type":"其他插件",
|
||||
"ps":"一款功能强大、企业级、开源 Web 服务器,具有用 Go 编写的自动 HTTPS",
|
||||
"shell":"install.sh",
|
||||
"install_pre_inspection":true,
|
||||
"checks":"server/caddy",
|
||||
"path":"server/caddy",
|
||||
"author":"caddyserver",
|
||||
"home":"https://caddyserver.com/",
|
||||
"date":"2026-04-11",
|
||||
"pid": "1",
|
||||
"versions": ["2.11"],
|
||||
"updates": ["2.11.2"]
|
||||
}
|
||||
36
plugins/caddy/init.d/caddy.service.tpl
Normal file
36
plugins/caddy/init.d/caddy.service.tpl
Normal file
@@ -0,0 +1,36 @@
|
||||
# caddy.service
|
||||
#
|
||||
# For using Caddy with a config file.
|
||||
#
|
||||
# Make sure the ExecStart and ExecReload commands are correct
|
||||
# for your installation.
|
||||
#
|
||||
# See https://caddyserver.com/docs/install for instructions.
|
||||
#
|
||||
# WARNING: This service does not use the --resume flag, so if you
|
||||
# use the API to make changes, they will be overwritten by the
|
||||
# Caddyfile next time the service is restarted. If you intend to
|
||||
# use Caddy's API to configure it, add the --resume flag to the
|
||||
# `caddy run` command or use the caddy-api.service file instead.
|
||||
|
||||
[Unit]
|
||||
Description=Caddy
|
||||
Documentation=https://caddyserver.com/docs/
|
||||
After=network.target network-online.target
|
||||
Requires=network-online.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
User=www
|
||||
Group=www
|
||||
ExecStart={$SERVER_PATH}/caddy/bin/caddy run --environ --config {$SERVER_PATH}/caddy/Caddyfile
|
||||
ExecReload={$SERVER_PATH}/caddy/bin/caddy reload --config {$SERVER_PATH}/caddy/Caddyfile --force
|
||||
TimeoutStopSec=5s
|
||||
LimitNOFILE=1048576
|
||||
LimitNPROC=512
|
||||
PrivateTmp=true
|
||||
ProtectSystem=full
|
||||
AmbientCapabilities=CAP_NET_BIND_SERVICE
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
111
plugins/caddy/init.d/caddy.tpl
Normal file
111
plugins/caddy/init.d/caddy.tpl
Normal file
@@ -0,0 +1,111 @@
|
||||
#!/bin/sh
|
||||
#
|
||||
# /etc/rc.d/init.d/caddy
|
||||
#
|
||||
# Runs the caddy
|
||||
#
|
||||
#
|
||||
# chkconfig: - 85 15
|
||||
#
|
||||
|
||||
### BEGIN INIT INFO
|
||||
# Provides: caddy
|
||||
# Required-Start: $remote_fs $syslog
|
||||
# Required-Stop: $remote_fs $syslog
|
||||
# Should-Start: caddy
|
||||
# Should-Stop: caddy
|
||||
# Default-Start: 2 3 4 5
|
||||
# Default-Stop: 0 1 6
|
||||
# Short-Description: Start caddy at boot time.
|
||||
# Description: Control caddy.
|
||||
### 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=caddy
|
||||
CADDY_HOME={$SERVER_PATH}/caddy
|
||||
CADDY_PATH=${CADDY_HOME}/bin/$NAME
|
||||
CADDY_USER={$RUN_USER}
|
||||
SERVICENAME="caddy"
|
||||
LOCKFILE=/tmp/caddy.lock
|
||||
LOGPATH=${CADDY_HOME}/log
|
||||
LOGFILE=${LOGPATH}/caddy.log
|
||||
RETVAL=0
|
||||
|
||||
if [ -d $LOGPATH ];then
|
||||
mkdir -p $LOGPATH
|
||||
fi
|
||||
|
||||
[ -r /etc/sysconfig/$NAME ] && . /etc/sysconfig/$NAME
|
||||
DAEMON_OPTS="--check $NAME"
|
||||
[ ! -z "$CADDY_USER" ] && DAEMON_OPTS="$DAEMON_OPTS --user=${CADDY_USER}"
|
||||
|
||||
status(){
|
||||
isStart=`ps -ef|grep 'caddy run' |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 'caddy run' | grep -v grep | awk '{print $2}'`
|
||||
if [ "$isStart" != '' ];then
|
||||
echo "${SERVICENAME}(pid $(echo $isStart)) already running"
|
||||
return $RETVAL
|
||||
fi
|
||||
|
||||
cd ${CADDY_HOME}
|
||||
echo -e "starting ${SERVICENAME}: \c"
|
||||
${CADDY_PATH} run --environ --config ${CADDY_HOME}/Caddyfile > ${LOGFILE} 2>&1 &
|
||||
RETVAL=$?
|
||||
[ $RETVAL = 0 ] && touch ${LOCKFILE} && echo -e "\033[32mdone\033[0m"
|
||||
return $RETVAL
|
||||
}
|
||||
|
||||
stop() {
|
||||
pids=`ps -ef | grep 'caddy run' | grep -v grep | awk '{print $2}'`
|
||||
arr=($pids)
|
||||
echo -e "stopping caddy... \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
|
||||
76
plugins/caddy/install.sh
Executable file
76
plugins/caddy/install.sh
Executable file
@@ -0,0 +1,76 @@
|
||||
#!/bin/bash
|
||||
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin:/opt/homebrew/bin
|
||||
export PATH
|
||||
|
||||
# cd /Users/midoks/Desktop/mwdev/server/mdserver-web/plugins/caddy && bash install.sh install 1.21.4
|
||||
# cd /www/server/mdserver-web/plugins/caddy && bash install.sh install 2.11
|
||||
# cd /www/server/mdserver-web/plugins/caddy && bash install.sh install 1.29.2
|
||||
# cd /www/server/mdserver-web/plugins/caddy && bash install.sh upgrade 1.29.2
|
||||
|
||||
# curl -I -H "Accept-Encoding: br" http://localhost
|
||||
# curl -I -H "Accept-Encoding: zstd" http://localhost
|
||||
# curl --http3 -v https://www.xxx.com
|
||||
|
||||
# cd /www/server/mdserver-web && python3 plugins/caddy/index.py run_info
|
||||
|
||||
curPath=`pwd`
|
||||
rootPath=$(dirname "$curPath")
|
||||
rootPath=$(dirname "$rootPath")
|
||||
serverPath=$(dirname "$rootPath")
|
||||
|
||||
sysName=`uname`
|
||||
action=$1
|
||||
type=$2
|
||||
|
||||
VERSION=$2
|
||||
openrestyDir=${serverPath}/source/caddy
|
||||
|
||||
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 -s /bin/bash www
|
||||
fi
|
||||
|
||||
if [ "${action}" = "upgrade" ];then
|
||||
sh -x $curPath/versions/$2/install.sh $1
|
||||
|
||||
echo "${VERSION}" > $serverPath/caddy/version.pl
|
||||
|
||||
#初始化
|
||||
cd ${rootPath} && python3 ${rootPath}/plugins/caddy/index.py start
|
||||
cd ${rootPath} && python3 ${rootPath}/plugins/caddy/index.py initd_install
|
||||
exit 0
|
||||
fi
|
||||
|
||||
|
||||
if [ "${2}" = "" ];then
|
||||
echo '缺少安装脚本版本...'
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [ "${action}" = "uninstall" ];then
|
||||
if [ -f /usr/lib/systemd/system/caddy.service ] || [ -f /lib/systemd/system/caddy.service ];then
|
||||
systemctl stop caddy
|
||||
rm -rf /usr/systemd/system/caddy.service
|
||||
rm -rf /lib/systemd/system/caddy.service
|
||||
systemctl daemon-reload
|
||||
fi
|
||||
|
||||
if [ -f $serverPath/caddy/init.d/caddy ];then
|
||||
$serverPath/caddy/init.d/caddy stop
|
||||
fi
|
||||
|
||||
rm -rf $serverPath/caddy
|
||||
fi
|
||||
|
||||
sh -x $curPath/versions/$2/install.sh $1
|
||||
|
||||
if [ "${action}" = "install" ] && [ -d $serverPath/caddy ];then
|
||||
echo "${VERSION}" > $serverPath/caddy/version.pl
|
||||
|
||||
#初始化
|
||||
cd ${rootPath} && python3 ${rootPath}/plugins/caddy/index.py start
|
||||
cd ${rootPath} && python3 ${rootPath}/plugins/caddy/index.py initd_install
|
||||
fi
|
||||
238
plugins/caddy/js/caddy.js
Executable file
238
plugins/caddy/js/caddy.js
Executable file
@@ -0,0 +1,238 @@
|
||||
function caddyPost(method, args, callback){
|
||||
var loadT = layer.msg('正在获取...', { icon: 16, time: 0, shade: 0.3 });
|
||||
$.post('/plugins/run', {name:'caddy', func:method, args:JSON.stringify(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 orPluginService(_name, version){
|
||||
var data = {name:_name, func:'status'}
|
||||
if ( typeof(version) != 'undefined' ){
|
||||
data['version'] = version;
|
||||
} else {
|
||||
version = '';
|
||||
}
|
||||
|
||||
caddyPost('status', data, function(data){
|
||||
if (data.data == 'start'){
|
||||
orPluginSetService(_name, true, version);
|
||||
} else {
|
||||
orPluginSetService(_name, false, version);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function orPluginSetService(_name ,status, version){
|
||||
var serviceCon ='<p class="status">当前状态:<span>'+(status ? '开启' : '关闭' )+
|
||||
'</span><span style="color: '+
|
||||
(status?'#20a53a;':'red;')+
|
||||
' margin-left: 3px;" class="glyphicon ' + (status?'glyphicon glyphicon-play':'glyphicon-pause')+'"></span></p><div class="sfm-opt">\
|
||||
<button class="btn btn-default btn-sm" onclick="orPluginOpService(\''+_name+'\',\''+(status?'stop':'start')+'\',\''+version+'\')">'+(status?'停止':'启动')+'</button>\
|
||||
<button class="btn btn-default btn-sm" onclick="orPluginOpService(\''+_name+'\',\'restart\',\''+version+'\',\'yes\')">重启</button>\
|
||||
<button class="btn btn-default btn-sm" onclick="orPluginOpService(\''+_name+'\',\'reload\',\''+version+'\')">重载配置</button>\
|
||||
</div>';
|
||||
$(".soft-man-con").html(serviceCon);
|
||||
}
|
||||
|
||||
|
||||
function orPluginOpService(a, b, v,request_callback) {
|
||||
|
||||
var c = "name=" + a + "&func=" + b;
|
||||
if(v != ''){
|
||||
c = c + '&version='+v;
|
||||
}
|
||||
|
||||
var d = "";
|
||||
|
||||
switch(b) {
|
||||
case "stop":d = '停止';break;
|
||||
case "start":d = '启动';break;
|
||||
case "restart":d = '重启';break;
|
||||
case "reload":d = '重载';break;
|
||||
}
|
||||
layer.confirm( msgTpl('您真的要{1}{2}{3}服务吗?', [d,a,v]), {icon:3,closeBtn: 2}, function() {
|
||||
caddyPost('get_os',{},function(data){
|
||||
var rdata = $.parseJSON(data.data);
|
||||
if (!rdata['auth']){
|
||||
layer.prompt({title: '检查到权限不足,需要输入密码!', formType: 1},function(pwd, index){
|
||||
|
||||
layer.close(index);
|
||||
var data = {'pwd':pwd};
|
||||
c += '&args='+JSON.stringify(data);
|
||||
orPluginOpServiceOp(a,b,c,d,a,v,request_callback);
|
||||
});
|
||||
} else {
|
||||
orPluginOpServiceOp(a,b,c,d,a,v,request_callback);
|
||||
|
||||
}
|
||||
});
|
||||
})
|
||||
}
|
||||
|
||||
function orPluginOpServiceOp(a,b,c,d,a,v,request_callback){
|
||||
|
||||
var request_path = "/plugins/run";
|
||||
if (request_callback == 'yes'){
|
||||
request_path = "/plugins/callback";
|
||||
}
|
||||
|
||||
var e = layer.msg(msgTpl('正在{1}{2}{3}服务,请稍候...',[d,a,v]), {icon: 16,time: 0});
|
||||
$.post(request_path, c, function(g) {
|
||||
layer.close(e);
|
||||
|
||||
var f = g.data == 'ok' ? msgTpl('{1}{2}服务已{3}',[a,v,d]) : msgTpl('{1}{2}服务{3}失败!',[a,v,d]);
|
||||
layer.msg(f, {icon: g.data == 'ok' ? 1 : 2});
|
||||
|
||||
if( b != "reload" && g.data == 'ok' ) {
|
||||
if ( b == 'start' ) {
|
||||
orPluginSetService(a, true, v);
|
||||
} else if ( b == 'stop' ){
|
||||
orPluginSetService(a, false, v);
|
||||
}
|
||||
}
|
||||
|
||||
if( g.status && g.data != 'ok' ) {
|
||||
layer.msg(g.data, {icon: 2,time: 10000,shade: 0.3});
|
||||
}
|
||||
|
||||
},'json').error(function() {
|
||||
layer.close(e);
|
||||
layer.msg('操作异常!', {icon: 2});
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
//查看Nginx负载状态
|
||||
function getOpStatus() {
|
||||
var loadT = layer.msg('正在处理,请稍后...', { icon: 16, time: 0, shade: 0.3 });
|
||||
$.post('/plugins/run', {name:'openresty', func:'run_info'}, function(data) {
|
||||
layer.close(loadT);
|
||||
try {
|
||||
var rdata = $.parseJSON(data.data);
|
||||
if ('status' in rdata && !rdata.status){
|
||||
showMsg(rdata.msg, function(){}, null,3000);
|
||||
return;
|
||||
}
|
||||
|
||||
var con = "<div><table class='table table-hover table-bordered'>\
|
||||
<tr><th>活动连接(Active connections)</th><td>" + rdata.active + "</td></tr>\
|
||||
<tr><th>总连接次数(accepts)</th><td>" + rdata.accepts + "</td></tr>\
|
||||
<tr><th>总握手次数(handled)</th><td>" + rdata.handled + "</td></tr>\
|
||||
<tr><th>总请求数(requests)</th><td>" + rdata.requests + "</td></tr>\
|
||||
<tr><th>请求数(Reading)</th><td>" + rdata.Reading + "</td></tr>\
|
||||
<tr><th>响应数(Writing)</th><td>" + rdata.Writing + "</td></tr>\
|
||||
<tr><th>驻留进程(Waiting)</th><td>" + rdata.Waiting + "</td></tr>\
|
||||
</table></div>";
|
||||
$(".soft-man-con").html(con);
|
||||
}catch(err){
|
||||
showMsg(data.data, function(){}, null,3000);
|
||||
}
|
||||
},'json');
|
||||
}
|
||||
|
||||
|
||||
function setOpCfg(){
|
||||
caddyPost('get_cfg', {}, function(data){
|
||||
var rdata = $.parseJSON(data.data);
|
||||
var rdata = rdata.data;
|
||||
// console.log(rdata);
|
||||
|
||||
var mlist = '';
|
||||
for (var i = 0; i < rdata.length; i++) {
|
||||
var w = '70'
|
||||
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;
|
||||
}
|
||||
mlist += '<p style="margin-top:15px;"><span>' + rdata[i].name + '</span>' + ibody + "<b class='unit c9'>"+rdata[i].unit+"</b>" +', <font class="c9">' + rdata[i].ps + '</font></p>';
|
||||
}
|
||||
var con = '<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="setOpCfg()">刷新</button>\
|
||||
<button class="btn btn-success btn-sm" onclick="submitConf()">保存</button>\
|
||||
</div>\
|
||||
</div>'
|
||||
$(".soft-man-con").html(con);
|
||||
});
|
||||
}
|
||||
|
||||
function submitConf() {
|
||||
var data = {
|
||||
worker_processes: $("input[name='worker_processes']").val(),
|
||||
worker_connections: $("input[name='worker_connections']").val(),
|
||||
keepalive_timeout: $("input[name='keepalive_timeout']").val(),
|
||||
zstd: $("select[name='zstd']").val() || 'on',
|
||||
brotli: $("select[name='brotli']").val() || 'on',
|
||||
gzip: $("select[name='gzip']").val() || 'on',
|
||||
gzip_min_length: $("input[name='gzip_min_length']").val(),
|
||||
gzip_comp_level: $("input[name='gzip_comp_level']").val(),
|
||||
client_max_body_size: $("input[name='client_max_body_size']").val(),
|
||||
server_names_hash_bucket_size: $("input[name='server_names_hash_bucket_size']").val(),
|
||||
client_header_buffer_size: $("input[name='client_header_buffer_size']").val(),
|
||||
};
|
||||
|
||||
// console.log(data);
|
||||
caddyPost('set_cfg', data, function(rdata){
|
||||
var rdata = $.parseJSON(rdata.data);
|
||||
// console.log(rdata);
|
||||
layer.msg(rdata.msg, { icon: rdata.status ? 1 : 2 });
|
||||
});
|
||||
}
|
||||
|
||||
function otherFunc(){
|
||||
var con = '<p class="conf_p" style="text-align:center;">\
|
||||
<button class="btn btn-default btn-sm" onclick="cronAddCheck()">添加检查任务</button> \
|
||||
<button class="btn btn-default btn-sm" onclick="cronDelCheck()">删除检查任务</button>\
|
||||
</p>';
|
||||
$(".soft-man-con").html(con);
|
||||
}
|
||||
|
||||
function cronAddCheck(){
|
||||
caddyPost('cron_add_check', {}, function(data){
|
||||
var rdata = $.parseJSON(data.data);
|
||||
layer.msg(rdata.msg, { icon: rdata.status ? 1 : 2 });
|
||||
});
|
||||
}
|
||||
|
||||
function cronDelCheck(){
|
||||
caddyPost('cron_del_check', {}, function(data){
|
||||
var rdata = $.parseJSON(data.data);
|
||||
layer.msg(rdata.msg, { icon: rdata.status ? 1 : 2 });
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
124
plugins/caddy/tool_task.py
Normal file
124
plugins/caddy/tool_task.py
Normal file
@@ -0,0 +1,124 @@
|
||||
# coding:utf-8
|
||||
|
||||
import sys
|
||||
import io
|
||||
import os
|
||||
import time
|
||||
import json
|
||||
|
||||
web_dir = os.getcwd() + "/web"
|
||||
if os.path.exists(web_dir):
|
||||
sys.path.append(web_dir)
|
||||
os.chdir(web_dir)
|
||||
|
||||
import core.mw as mw
|
||||
from utils.crontab import crontab as MwCrontab
|
||||
|
||||
|
||||
app_debug = False
|
||||
if mw.isAppleSystem():
|
||||
app_debug = True
|
||||
|
||||
|
||||
def getPluginName():
|
||||
return 'caddy'
|
||||
|
||||
|
||||
def getPluginDir():
|
||||
return mw.getPluginDir() + '/' + getPluginName()
|
||||
|
||||
|
||||
def getServerDir():
|
||||
return mw.getServerDir() + '/' + getPluginName()
|
||||
|
||||
|
||||
def getTaskConf():
|
||||
conf = getServerDir() + "/task_config.json"
|
||||
return conf
|
||||
|
||||
|
||||
def getConfigData():
|
||||
conf = getTaskConf()
|
||||
if os.path.exists(conf):
|
||||
return json.loads(mw.readFile(getTaskConf()))
|
||||
return {
|
||||
"task_id": -1,
|
||||
"period": "minute-n",
|
||||
"where1": "3",
|
||||
"hour": "0",
|
||||
"minute": "0",
|
||||
}
|
||||
|
||||
|
||||
def createBgTask():
|
||||
removeBgTask()
|
||||
createBgTaskByName(getPluginName())
|
||||
|
||||
|
||||
def createBgTaskByName(name):
|
||||
args = getConfigData()
|
||||
_name = "[caddy]检查任务"
|
||||
res = mw.M("crontab").field("id, name").where("name=?", (_name,)).find()
|
||||
if res:
|
||||
return True
|
||||
|
||||
if "task_id" in args and args["task_id"] > 0:
|
||||
res = mw.M("crontab").field("id, name").where(
|
||||
"id=?", (args["task_id"],)).find()
|
||||
if res and res["id"] == args["task_id"]:
|
||||
print("计划任务已经存在!")
|
||||
return True
|
||||
|
||||
mw_dir = mw.getPanelDir()
|
||||
cmd = '''
|
||||
mw_dir=%s
|
||||
rname=%s
|
||||
plugin_path=%s
|
||||
script_path=%s
|
||||
''' % (mw_dir, name, getServerDir(), getPluginDir())
|
||||
cmd += 'echo "bash $script_path/check.sh"' + "\n"
|
||||
cmd += 'cd $mw_dir && bash $script_path/check.sh' + "\n"
|
||||
|
||||
params = {
|
||||
'name': _name,
|
||||
'type': args['period'],
|
||||
'week': "",
|
||||
'where1': args['where1'],
|
||||
'hour': args['hour'],
|
||||
'minute': args['minute'],
|
||||
'save': "",
|
||||
'backup_to': "",
|
||||
'stype': "toShell",
|
||||
'sname': '',
|
||||
'sbody': cmd,
|
||||
'url_address': '',
|
||||
}
|
||||
|
||||
task_id = MwCrontab.instance().add(params)
|
||||
if task_id > 0:
|
||||
args["task_id"] = task_id
|
||||
args["name"] = name
|
||||
mw.writeFile(getTaskConf(), json.dumps(args))
|
||||
|
||||
|
||||
def removeBgTask():
|
||||
cfg = getConfigData()
|
||||
if "task_id" in cfg 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"]:
|
||||
data = MwCrontab.instance().delete(cfg["task_id"])
|
||||
if data["status"]:
|
||||
cfg["task_id"] = -1
|
||||
mw.writeFile(getTaskConf(), json.dumps(cfg))
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
if len(sys.argv) > 1:
|
||||
action = sys.argv[1]
|
||||
if action == "remove":
|
||||
removeBgTask()
|
||||
elif action == "add":
|
||||
createBgTask()
|
||||
88
plugins/caddy/versions/2.11/install.sh
Normal file
88
plugins/caddy/versions/2.11/install.sh
Normal file
@@ -0,0 +1,88 @@
|
||||
#!/bin/bash
|
||||
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin:/opt/homebrew/bin
|
||||
export PATH
|
||||
|
||||
# cd /Users/midoks/Desktop/mwdev/server/mdserver-web/plugins/caddy && bash install.sh install 2.11
|
||||
# cd /Users/midoks/Desktop/mwdev/server/mdserver-web/plugins/caddy && bash install.sh upgrade 2.11
|
||||
# cd /www/server/mdserver-web/plugins/caddy && bash install.sh install 2.11
|
||||
|
||||
curPath=`pwd`
|
||||
rootPath=$(dirname "$curPath")
|
||||
rootPath=$(dirname "$rootPath")
|
||||
serverPath=$(dirname "$rootPath")
|
||||
|
||||
sysName=`uname`
|
||||
action=$1
|
||||
type=$2
|
||||
|
||||
OS=`uname`
|
||||
OSNAME=''
|
||||
case $(uname) in
|
||||
Darwin) OSNAME="mac" ;;
|
||||
Linux) OSNAME="linux" ;;
|
||||
esac
|
||||
|
||||
ARCH=`uname -m`
|
||||
ARCH_NAME=''
|
||||
case $(uname -m) in
|
||||
i386) ARCH_NAME="386" ;;
|
||||
i686) ARCH_NAME="386" ;;
|
||||
x86_64) ARCH_NAME="amd64" ;;
|
||||
arm) ARCH_NAME="arm64" ;;
|
||||
arm64) ARCH_NAME="arm64" ;;
|
||||
aarch64) ARCH_NAME="arm64" ;;
|
||||
esac
|
||||
|
||||
VERSION=2.11.2
|
||||
|
||||
caddyDir=${serverPath}/source/caddy
|
||||
|
||||
Install_App()
|
||||
{
|
||||
if [ "${action}" = "install" ];then
|
||||
if [ -d $serverPath/caddy ];then
|
||||
exit 0
|
||||
fi
|
||||
fi
|
||||
|
||||
mkdir -p ${caddyDir}
|
||||
# mkdir -p ${serverPath}/caddy
|
||||
echo 'install scripts ...'
|
||||
|
||||
FILE_NAME="caddy_${VERSION}_${OSNAME}_${ARCH_NAME}.tar.gz"
|
||||
|
||||
if [ ! -f ${caddyDir}/${FILE_NAME} ];then
|
||||
wget --no-check-certificate -O ${caddyDir}/${FILE_NAME} "https://github.com/caddyserver/caddy/releases/download/v${VERSION}/${FILE_NAME}"
|
||||
fi
|
||||
|
||||
|
||||
echo "cd $serverPath/source/caddy/caddy && tar -zxvf ${caddyDir}/$FILE_NAME"
|
||||
|
||||
mkdir -p cd $serverPath/source/caddy/caddy
|
||||
cd $serverPath/source/caddy/caddy && tar -zxvf ${caddyDir}/$FILE_NAME
|
||||
if [ "$OSNAME" = "mac" ];then
|
||||
xattr -cr caddy
|
||||
fi
|
||||
|
||||
if [ ! -f $serverPath/caddy/bin ];then
|
||||
mkdir -p $serverPath/caddy/bin
|
||||
mv $serverPath/source/caddy/caddy/* $serverPath/caddy/bin
|
||||
chmod +x $serverPath/caddy/bin/caddy
|
||||
fi
|
||||
|
||||
echo 'Installation of caddy completed'
|
||||
}
|
||||
|
||||
Uninstall_App()
|
||||
{
|
||||
echo 'Uninstalling caddy completed'
|
||||
}
|
||||
|
||||
action=$1
|
||||
if [ "${1}" = "install" ];then
|
||||
Install_App
|
||||
elif [ "${1}" = "upgrade" ];then
|
||||
Install_App
|
||||
else
|
||||
Uninstall_App
|
||||
fi
|
||||
@@ -3,6 +3,7 @@
|
||||
<div class="bt-w-menu">
|
||||
<p class="bgw" onclick="pluginService('clean');">服务</p>
|
||||
<p onclick="pluginConfig('clean');">配置修改</p>
|
||||
<p onclick="commonFunc();">常用功能</p>
|
||||
<p onclick="pluginLogs('clean',null,'run_log');">运行日志</p>
|
||||
<p onclick="pRead()">说明</p>
|
||||
</div>
|
||||
@@ -12,8 +13,53 @@
|
||||
</div>
|
||||
</div>
|
||||
<script type="text/javascript">
|
||||
|
||||
pluginService('clean');
|
||||
|
||||
function cleanPost(method, version, args, callback){
|
||||
var loadT = layer.msg('正在获取...', { icon: 16, time: 0, shade: 0.3 });
|
||||
|
||||
var req_data = {};
|
||||
req_data['name'] = 'clean';
|
||||
req_data['func'] = method;
|
||||
req_data['version'] = version;
|
||||
|
||||
if (typeof(args) == 'string'){
|
||||
req_data['args'] = JSON.stringify(toArrayObject(args));
|
||||
} else {
|
||||
req_data['args'] = JSON.stringify(args);
|
||||
}
|
||||
|
||||
$.post('/plugins/run', req_data, function(data) {
|
||||
layer.close(loadT);
|
||||
if (!data.status){
|
||||
layer.msg(data.msg,{icon:0,time:2000,shade: [10, '#000']});
|
||||
return;
|
||||
}
|
||||
|
||||
if(typeof(callback) == 'function'){
|
||||
callback(data);
|
||||
}
|
||||
},'json');
|
||||
}
|
||||
|
||||
function cleanRun(){
|
||||
cleanPost('clean_run', '', {}, function(rdata){
|
||||
// var rdata = $.parseJSON(rdata.data);
|
||||
// layer.msg(rdata.msg, { icon: rdata.status ? 1 : 2 });
|
||||
});
|
||||
|
||||
layer.msg("执行成功!", { icon: 1});
|
||||
}
|
||||
|
||||
function commonFunc(){
|
||||
|
||||
con = '<hr/><p class="conf_p" style="text-align:center;">\
|
||||
<button class="btn btn-default btn-sm" onclick="cleanRun()">手动执行</button> \
|
||||
</p>';
|
||||
|
||||
$(".soft-man-con").html(con);
|
||||
}
|
||||
|
||||
function pRead(){
|
||||
var readme = '<ul class="help-info-text c7">';
|
||||
|
||||
@@ -5,8 +5,12 @@ import io
|
||||
import os
|
||||
import time
|
||||
|
||||
sys.path.append(os.getcwd() + "/class/core")
|
||||
import mw
|
||||
web_dir = os.getcwd() + "/web"
|
||||
if os.path.exists(web_dir):
|
||||
sys.path.append(web_dir)
|
||||
os.chdir(web_dir)
|
||||
|
||||
import core.mw as mw
|
||||
|
||||
app_debug = False
|
||||
if mw.isAppleSystem():
|
||||
@@ -74,7 +78,6 @@ def initConf():
|
||||
content = ""
|
||||
|
||||
clog = [
|
||||
"/var/spool/clientmqueue/*",
|
||||
"/var/log/cron-*",
|
||||
"/var/log/maillog-*",
|
||||
"/var/log/secure-*",
|
||||
@@ -82,11 +85,46 @@ def initConf():
|
||||
"/var/log/yum.log-*",
|
||||
"/var/log/messages-*",
|
||||
"/var/log/btmp-*",
|
||||
"/var/log/auth.*",
|
||||
"/var/log/messages.*",
|
||||
"/var/log/debug.*",
|
||||
"/var/log/syslog.*",
|
||||
"/var/log/syslog",
|
||||
"/var/log/mail.*",
|
||||
"/var/log/mail.err.*",
|
||||
"/var/log/mail.info.*",
|
||||
"/var/log/mail.warn.*",
|
||||
"/var/log/btmp.*",
|
||||
"/var/log/sa/sa*",
|
||||
"/var/log/sysstat/sa*",
|
||||
"/var/log/atop/atop*",
|
||||
"/var/log/anaconda/*.log",
|
||||
|
||||
"/var/log/dpkg.log.*",
|
||||
"/var/log/alternatives.log.*",
|
||||
"/var/log/user.log.*",
|
||||
"/var/log/kern.log.*",
|
||||
"/var/log/daemon.log.*",
|
||||
|
||||
"/var/log/*.gz",
|
||||
"/var/log/*.xz",
|
||||
"/var/log/*.log.*",
|
||||
|
||||
"/var/log/audit/audit.log.*",
|
||||
"/var/log/hawkey.log-*",
|
||||
"/var/log/apt/*.gz",
|
||||
"/var/log/apt/*.xz",
|
||||
"/var/log/rhsm/rhsm.log-*",
|
||||
"/var/log/rhsm/rhsmcertd.log-*",
|
||||
"/var/log/exim4/*.gz",
|
||||
"/var/log/journal/*",
|
||||
"/var/spool/clientmqueue/*",
|
||||
|
||||
"/tmp/yum_save_*",
|
||||
"/tmp/tmp.*",
|
||||
|
||||
"/www/server/dztasks/logs/dztasks.*.log",
|
||||
"/www/server/dztasks/logs/dztasks_*",
|
||||
]
|
||||
for i in clog:
|
||||
content += i + "\n"
|
||||
@@ -98,9 +136,13 @@ def initConf():
|
||||
"/var/log/wtmp",
|
||||
"/var/log/secure",
|
||||
"/var/log/lastlog",
|
||||
"/var/log/syslog",
|
||||
"/var/log/cron",
|
||||
"/www/wwwlogs",
|
||||
"/www/server/rsyncd",
|
||||
"/www/server/acme_pandominassl_apply",
|
||||
"/www/server/sphinx/index",
|
||||
"/www/server/manticoresearch",
|
||||
"/www/server/mongodb/logs",
|
||||
"/www/server/php/53/var/log",
|
||||
"/www/server/php/54/var/log",
|
||||
@@ -116,15 +158,21 @@ def initConf():
|
||||
"/www/server/php/82/var/log",
|
||||
"/www/server/php/83/var/log",
|
||||
"/www/server/php/84/var/log",
|
||||
"/www/server/php/85/var/log",
|
||||
"/www/server/openresty/nginx/logs",
|
||||
"/www/server/phpmyadmin",
|
||||
"/www/server/apache/httpd/logs",
|
||||
"/www/server/redis/data",
|
||||
"/www/server/cron",
|
||||
"/www/server/alist/data/log",
|
||||
"/www/server/dztasks/logs",
|
||||
"/www/server/rsyncd/lsyncd.log",
|
||||
"/www/server/mdserver-web/logs",
|
||||
]
|
||||
for i in clogcom:
|
||||
if os.path.exists(i):
|
||||
content += i + "\n"
|
||||
|
||||
content += "/www/server/cron\n"
|
||||
# 清理日志
|
||||
rootDir = "/var/log"
|
||||
|
||||
@@ -201,6 +249,13 @@ def cleanDirLog(path):
|
||||
cleanDirLog(abspath)
|
||||
|
||||
|
||||
def cleanRun():
|
||||
plugin_dir = getPluginDir()
|
||||
log_file = getServerDir()+'/clean.log'
|
||||
cmd = 'cd '+mw.getPanelDir()+' && python3 '+plugin_dir+'/index.py clean > '+log_file
|
||||
os.system(cmd)
|
||||
return mw.returnJson(True, '执行成功!')
|
||||
|
||||
def cleanLog():
|
||||
conf = getConf()
|
||||
clist = mw.readFile(conf).strip()
|
||||
@@ -225,6 +280,7 @@ def cleanLog():
|
||||
cleanDirLog(abspath)
|
||||
continue
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
func = sys.argv[1]
|
||||
if func == 'status':
|
||||
@@ -243,5 +299,7 @@ if __name__ == "__main__":
|
||||
print(runLog())
|
||||
elif func == 'clean':
|
||||
cleanLog()
|
||||
elif func == 'clean_run':
|
||||
print(cleanRun())
|
||||
else:
|
||||
print('error')
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
{
|
||||
"sort":2,
|
||||
"title":"日志清理",
|
||||
"tip":"soft",
|
||||
"name":"clean",
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user