Compare commits

...
456 Commits
Author SHA1 Message Date
Mr Chen f6d4609947 Merge pull request #583 from midoks/dev
0.17.0
2024-05-27 17:47:09 +08:00
Mr Chen d2ddcebe3e 0.17.0 2024-05-27 17:46:06 +08:00
Mr Chen fd2f2c0aa9 Update config.js 2024-05-27 17:42:48 +08:00
Mr Chen 0aae3b0654 Update config_api.py 2024-05-27 17:39:03 +08:00
Mr Chen e56606ac61 Update config_api.py 2024-05-27 17:34:56 +08:00
Mr Chen 54e11700c2 Update config_api.py 2024-05-27 17:27:45 +08:00
Mr Chen fe3b8de490 update 2024-05-27 17:26:42 +08:00
Mr Chen 9874069100 Update config_api.py 2024-05-27 15:13:08 +08:00
Mr Chen 9419d6d55b Update config_api.py 2024-05-27 15:06:14 +08:00
Mr Chen 2f61facbda Update mw.py 2024-05-27 15:01:01 +08:00
Mr Chen 5cd8d7bf18 Update setting.py 2024-05-27 14:57:55 +08:00
Mr Chen 41f016050f Update mw.py 2024-05-27 14:51:39 +08:00
Mr Chen d931790dd3 Update mw.py 2024-05-27 14:50:56 +08:00
Mr Chen a81e63e131 Update mw.tpl 2024-05-27 14:47:38 +08:00
Mr Chen 30717079d4 Update mw.tpl 2024-05-27 14:46:56 +08:00
Mr Chen 3eb2fbb654 Update mw.py 2024-05-27 14:41:06 +08:00
Mr Chen cf54a430eb Update mw.py 2024-05-27 14:40:43 +08:00
Mr Chen 04925c0534 Update mw.py 2024-05-27 14:40:27 +08:00
Mr Chen 6473755127 Update mw.py 2024-05-27 14:39:13 +08:00
Mr Chen 35f40fc8fc Update mw.py 2024-05-27 14:37:09 +08:00
Mr Chen 088a4ab969 Update mw.py 2024-05-27 14:35:44 +08:00
Mr Chen 3c00ddad75 Update mw.py 2024-05-27 14:31:04 +08:00
Mr Chen 5edd9baeb6 Update config_api.py 2024-05-27 14:22:48 +08:00
Mr Chen 734548d764 update 2024-05-27 14:18:54 +08:00
Mr Chen e47d3a235d Update task.py 2024-05-27 13:59:19 +08:00
Mr Chen 0a93015720 Update tools.py 2024-05-27 12:24:23 +08:00
Mr Chen 1078d71370 Update tools.py 2024-05-27 12:23:53 +08:00
Mr Chen b482127044 update 2024-05-27 12:12:41 +08:00
Mr Chen 27b6ee135c Update config.js 2024-05-27 10:37:08 +08:00
Mr Chen cc2000267d Update config_api.py 2024-05-27 10:35:22 +08:00
Mr Chen c1e3655923 Update config_api.py 2024-05-27 09:34:21 +08:00
Mr Chen b0a6181dca up 2024-05-27 00:16:04 +08:00
Mr Chen 33e7efa440 Update config_api.py 2024-05-26 23:01:16 +08:00
Mr Chen 6d7a38dc08 Update config.js 2024-05-26 22:55:28 +08:00
Mr Chen 8a60965069 update 2024-05-26 22:50:29 +08:00
Mr Chen ef9d627095 Update config_api.py 2024-05-26 22:37:12 +08:00
Mr Chen b66acc8c2a Update config.js 2024-05-26 22:28:19 +08:00
Mr Chen fb56f6c190 Update config_api.py 2024-05-26 22:09:29 +08:00
Mr Chen af0f55e6ef Update config_api.py 2024-05-26 21:23:05 +08:00
Mr Chen fb95e2ad8d Update config_api.py 2024-05-26 21:22:41 +08:00
Mr Chen 138a6d3e66 Update config_api.py 2024-05-26 21:21:57 +08:00
Mr Chen 98b9065585 Update config_api.py 2024-05-26 21:19:49 +08:00
Mr Chen 36c3fae920 Update config.js 2024-05-26 21:17:58 +08:00
Mr Chen 70ee376958 Update config.js 2024-05-26 21:14:04 +08:00
Mr Chen 47b2f1b009 Update config_api.py 2024-05-26 17:01:31 +08:00
Mr Chen f2a35b97b0 Update config_api.py 2024-05-26 16:58:38 +08:00
Mr Chen c26e5dc40d update 2024-05-26 16:39:28 +08:00
Mr Chen 893b2d601c Update config.js 2024-05-26 15:49:56 +08:00
Mr Chen 28964721ec Update config_api.py 2024-05-26 15:47:48 +08:00
Mr Chen 5a2028d797 update 2024-05-26 15:46:12 +08:00
Mr Chen fcbe6a87df Update mw.py 2024-05-26 15:32:05 +08:00
Mr Chen 7f5cf75d68 update 2024-05-26 15:29:21 +08:00
Mr Chen c368638181 Update config_api.py 2024-05-26 15:13:53 +08:00
Mr Chen decc9d2b29 Update config_api.py 2024-05-26 14:08:37 +08:00
Mr Chen 7da2a90712 Update config_api.py 2024-05-26 14:01:53 +08:00
Mr Chen 7dd377460c Update config.js 2024-05-26 14:01:22 +08:00
Mr Chen 6b6ae63aea Update config.js 2024-05-26 14:00:22 +08:00
Mr Chen cc9a4b5536 Update config.js 2024-05-26 13:58:52 +08:00
Mr Chen fd44a312f6 Update config.js 2024-05-26 13:58:26 +08:00
Mr Chen a5802b4280 Update config_api.py 2024-05-26 13:56:40 +08:00
Mr Chen d8c928dc02 Update config.js 2024-05-26 13:56:29 +08:00
Mr Chen 339cdf662b Update cli.sh 2024-05-26 13:55:51 +08:00
Mr Chen 1e2a174e30 Update config_api.py 2024-05-26 13:55:47 +08:00
Mr Chen 2714538ad7 Update config_api.py 2024-05-26 13:54:50 +08:00
Mr Chen 8a43f67801 Update mw.py 2024-05-26 13:52:19 +08:00
Mr Chen f3f7ef4a3d Update config.js 2024-05-26 13:52:12 +08:00
Mr Chen 3523f8d6c8 update 2024-05-26 13:52:07 +08:00
Mr Chen eb8c2b6ad5 update 2024-05-26 00:48:03 +08:00
Mr Chen 438322361d Update files.js 2024-05-25 23:19:57 +08:00
Mr Chen c902f48931 Update config_api.py 2024-05-25 13:24:11 +08:00
Mr Chen e67a258e7a Update sphinx.js 2024-05-25 13:06:28 +08:00
Mr Chen 007993ed9e Update sphinx.js 2024-05-25 13:03:50 +08:00
Mr Chen 81171c0c7c Update webstats_common.lua 2024-05-25 11:44:00 +08:00
Mr Chen 15d07e4234 Merge pull request #582 from midoks/dev
网站统计-PV,UV优化
2024-05-25 11:36:02 +08:00
Mr Chen 786fac9142 Update webstats_common.lua 2024-05-25 11:31:56 +08:00
Mr Chen 5499c8795f update 2024-05-25 11:08:46 +08:00
Mr Chen 322698f345 Update init.sql 2024-05-24 23:31:30 +08:00
Mr Chen f229e03f10 Update stats.js 2024-05-23 15:52:32 +08:00
Mr Chen 6024dbcdb6 Merge pull request #581 from midoks/dev
网站统计 - 特殊处理
2024-05-23 15:51:59 +08:00
Mr Chen c874ea44eb 网站统计 - 特殊处理 2024-05-23 15:50:43 +08:00
Mr Chen c70e4d1412 Merge pull request #580 from midoks/dev
Update index.py
2024-05-23 14:03:08 +08:00
Mr Chen 235c5e329e Update index.py 2024-05-23 14:01:02 +08:00
Mr Chen b77bfab5a8 Merge pull request #579 from midoks/dev
新功能同步到mariadb
2024-05-23 13:11:41 +08:00
Mr Chen 890bbc4214 Update index.py 2024-05-23 13:05:44 +08:00
Mr Chen 2f86f4d6ff Update index.py 2024-05-23 13:04:34 +08:00
Mr Chen c9ced293e8 Update mariadb.js 2024-05-23 12:57:33 +08:00
Mr Chen 43f081aac1 Update mariadb.js 2024-05-23 12:56:09 +08:00
Mr Chen 49aa411248 Update index.py 2024-05-23 12:53:00 +08:00
Mr Chen d597e15402 Update index.py 2024-05-23 12:52:49 +08:00
Mr Chen a74ee3faf4 Update index.py 2024-05-23 12:52:39 +08:00
Mr Chen cfe55ca196 Update index.py 2024-05-23 12:51:24 +08:00
Mr Chen 2edffd36d0 Update requirements.txt 2024-05-23 12:07:31 +08:00
Mr Chen c7077ec283 Update index.py 2024-05-23 11:57:12 +08:00
Mr Chen cfcd4c2050 Update index.py 2024-05-23 11:55:28 +08:00
Mr Chen 2387af2880 Update index.py 2024-05-23 11:25:33 +08:00
Mr Chen 72f825c0c4 Update index.py 2024-05-23 11:23:55 +08:00
Mr Chen fbe4857944 Update index.py 2024-05-23 11:23:05 +08:00
Mr Chen 01fe75e553 Update index.py 2024-05-23 11:21:37 +08:00
Mr Chen 2bd607edad Update index.py 2024-05-23 11:20:35 +08:00
Mr Chen 9b9a806dee Update install.sh 2024-05-23 11:17:20 +08:00
Mr Chen 9bc6d392a0 Update index.py 2024-05-23 11:15:15 +08:00
Mr Chen 26b5dc7cd4 update 2024-05-23 11:04:08 +08:00
Mr Chen 80a690c431 Update mariadb.js 2024-05-23 10:48:15 +08:00
Mr Chen 71840ee1e2 update 2024-05-23 10:45:03 +08:00
Mr Chen fbdbb25a78 Update index.py 2024-05-23 10:36:10 +08:00
Mr Chen e546428596 Update public.js 2024-05-23 00:43:10 +08:00
Mr Chen a16259f718 Update public.js 2024-05-23 00:35:54 +08:00
Mr Chen df0b708462 Update backup.py 2024-05-23 00:16:38 +08:00
Mr Chen b3c383327d Update index.py 2024-05-23 00:15:35 +08:00
Mr Chen 8a9c99fb0d Merge pull request #578 from midoks/dev
Update requirements.txt
2024-05-22 22:22:33 +08:00
Mr Chen a22d90198c Update requirements.txt 2024-05-22 22:21:18 +08:00
Mr Chen dcae829a1c Merge pull request #577 from midoks/dev
0.16.9
2024-05-22 22:12:39 +08:00
Mr Chen de4e134be8 0.16.9
- mysql同步优化,享受丝滑般感觉。
- 网站统计 - 实时-可调节1-10s。
- 网站统计 - 加入大小条件。
- Sphinx优化。
2024-05-22 22:12:03 +08:00
Mr Chen ba96e62126 Update cmd.md 2024-05-22 22:11:07 +08:00
Mr Chen 990fb06567 Update .gitignore 2024-05-22 22:02:17 +08:00
Mr Chen 71c1034d12 Update .gitignore 2024-05-22 22:01:37 +08:00
Mr Chen 6582cbc09d update 2024-05-22 22:01:09 +08:00
Mr Chen 52bad278cc Update README.md 2024-05-22 19:48:12 +08:00
Mr Chen e023490002 网站统计加入大小条件 2024-05-22 19:44:25 +08:00
Mr Chen c1a7443597 Update r3.6.txt 2024-05-22 11:46:40 +08:00
Mr Chen 3f57e7da07 update 2024-05-22 11:45:51 +08:00
Mr Chen 0461bf3225 Update sphinx_make.py 2024-05-22 11:37:47 +08:00
Mr Chen ac0a503aa7 Update push_notice_msg.py 2024-05-22 10:57:45 +08:00
Mr Chen 706e9e98e7 Update redis.tpl 2024-05-22 10:50:08 +08:00
Mr Chen d9e878d607 Update redis_cluster.conf 2024-05-22 10:45:57 +08:00
Mr Chen ceb5f21814 Update redis_slave.conf 2024-05-22 10:44:45 +08:00
Mr Chen c8e689433b mw update_dev 2024-05-22 10:44:21 +08:00
Mr Chen f2171aa159 Update mw.tpl 2024-05-22 03:29:46 +08:00
Mr Chen 6eccdd510e update 2024-05-22 03:29:01 +08:00
Mr Chen c9a00809a4 Update redis_slave.conf 2024-05-22 02:57:42 +08:00
Mr Chen 9f1d8e43e6 Update redis_slave.conf 2024-05-22 02:54:26 +08:00
Mr Chen e8eda79a1a Update redis.conf 2024-05-22 02:54:06 +08:00
Mr Chen f25847d31d Update tidb.yaml 2024-05-22 02:53:27 +08:00
Mr Chen bda7dfa518 Update redis_slave.conf 2024-05-22 02:53:06 +08:00
Mr Chen 4115c29fe9 Update redis_slave.conf 2024-05-22 02:46:09 +08:00
Mr Chen f73e378fd6 Update index.py 2024-05-21 23:41:56 +08:00
Mr Chen aa9ab55fec Update sphinx_make.py 2024-05-21 23:39:19 +08:00
Mr Chen da5c44a747 Update sphinx_make.py 2024-05-21 23:39:04 +08:00
Mr Chen ee55c998ba Update sphinx_make.py 2024-05-21 23:28:55 +08:00
Mr Chen 2e75a56502 Update sphinx_make.py 2024-05-21 23:26:15 +08:00
Mr Chen a66279ba37 Update sphinx_make.py 2024-05-21 23:23:33 +08:00
Mr Chen 78bbae1f53 Update sphinx_make.py 2024-05-21 23:21:20 +08:00
Mr Chen a87def4325 Update sphinx_make.py 2024-05-21 22:29:32 +08:00
Mr Chen b84ac2daf1 Update sphinx_make.py 2024-05-21 20:58:22 +08:00
Mr Chen 7c9bf99669 Update sphinx_make.py 2024-05-21 20:56:38 +08:00
Mr Chen 599f6ecc51 Update sphinx_make.py 2024-05-21 20:42:38 +08:00
Mr Chen 47a17fbc46 Update sphinx_make.py 2024-05-21 20:40:31 +08:00
Mr Chen 80fa0ea62d Update sphinx_make.py 2024-05-21 20:40:15 +08:00
Mr Chen 83c78609a4 Update sphinx_make.py 2024-05-21 20:35:50 +08:00
Mr Chen 298d4b7981 Update sphinx_make.py 2024-05-21 20:33:22 +08:00
Mr Chen dc6f4e03e7 Update sphinx_make.py 2024-05-21 20:27:55 +08:00
Mr Chen 8b0b2b2d72 Update sphinx.js 2024-05-21 20:00:29 +08:00
Mr Chen d4087ce7f0 Update sphinx_make.py 2024-05-21 19:59:23 +08:00
Mr Chen f92df461ba Update sphinx_make.py 2024-05-21 19:58:43 +08:00
Mr Chen 9e015bc08c Update sphinx_make.py 2024-05-21 19:55:36 +08:00
Mr Chen 4bffd9a22d update 2024-05-21 19:43:25 +08:00
Mr Chen e2ef42fbbc Update sphinx.js 2024-05-21 19:23:30 +08:00
Mr Chen f10e05b7ee update 2024-05-21 19:20:07 +08:00
Mr Chen c15288cf09 update 2024-05-21 17:12:39 +08:00
Mr Chen 2461307d64 update 2024-05-21 16:57:05 +08:00
Mr Chen 95ec87346d update 2024-05-21 16:07:59 +08:00
Mr Chen f99c5b5fc4 update 2024-05-21 16:04:02 +08:00
Mr Chen adfd3bef2d Update app.js 2024-05-21 15:58:32 +08:00
Mr Chen c11631c0c3 Update index.html 2024-05-21 14:42:11 +08:00
Mr Chen 4f87725101 Update redis_slave.conf 2024-05-21 14:22:40 +08:00
Mr Chen 3a6695bb0d Update redis.conf 2024-05-21 14:22:02 +08:00
Mr Chen dd3bcced66 Update redis_slave.conf 2024-05-21 14:21:57 +08:00
Mr Chen 81a163f744 Update site.css 2024-05-21 03:08:08 +08:00
Mr Chen 19dd36fa8b Update sphinx_make.py 2024-05-21 00:22:03 +08:00
Mr Chen 9dcb028158 Update sphinx_make.py 2024-05-21 00:15:26 +08:00
Mr Chen 1ce841eac9 Update sphinx_make.py 2024-05-21 00:12:15 +08:00
Mr Chen c99311dad9 update 2024-05-21 00:07:48 +08:00
Mr Chen d7dd126a48 Update sphinx_make.py 2024-05-20 23:50:06 +08:00
Mr Chen ec8ab6bc45 Update sphinx_make.py 2024-05-20 23:48:03 +08:00
Mr Chen d5b87f450d Update sphinx_make.py 2024-05-20 23:46:23 +08:00
Mr Chen 0016f85132 Update sphinx_make.py 2024-05-20 23:18:48 +08:00
Mr Chen 32a6b36297 Update tools.py 2024-05-20 22:31:11 +08:00
Mr Chen 8da60ffc6b Update tools.py 2024-05-20 22:20:32 +08:00
Mr Chen 64d65a5cac Update sphinx_make.py 2024-05-20 20:14:48 +08:00
Mr Chen cf5b986d2e update 2024-05-20 14:11:27 +08:00
Mr Chen e1d0fc49bb Update install.sh 2024-05-20 00:34:08 +08:00
Mr Chen 68187ee7dd update 2024-05-20 00:21:09 +08:00
Mr Chen 9f7a7c5e59 Update rhel.sh 2024-05-20 00:10:44 +08:00
Mr Chen 90b14d59b0 Update install.sh 2024-05-19 23:51:32 +08:00
Mr Chen e8d1ea0a38 Update install.sh 2024-05-19 23:51:11 +08:00
Mr Chen edf8a5534a Update install.sh 2024-05-19 23:48:06 +08:00
Mr Chen 87bceced80 update 2024-05-19 23:18:48 +08:00
Mr Chen db3350e85c Update install.sh 2024-05-19 23:03:59 +08:00
Mr Chen 56b3aae0c7 Update install.sh 2024-05-19 22:22:52 +08:00
Mr Chen 01d5b75d90 Update install.sh 2024-05-19 22:15:43 +08:00
Mr Chen cbf3a15bf0 update 2024-05-19 22:06:47 +08:00
Mr Chen 2f0f6eaed6 Update install.sh 2024-05-19 21:54:20 +08:00
Mr Chen f0fcec44ff Update install.sh 2024-05-19 21:46:52 +08:00
Mr Chen fcfdfe822a up 2024-05-19 21:36:25 +08:00
Mr Chen d8e08812f9 update 2024-05-19 19:49:15 +08:00
Mr Chen d0ba142933 Update sphinx_make.py 2024-05-19 18:42:43 +08:00
Mr Chen b4e130c664 Update sphinx_make.py 2024-05-19 18:25:51 +08:00
Mr Chen 7503907f34 Update sphinx_make.py 2024-05-19 18:22:13 +08:00
Mr Chen 390df93477 Update sphinx_make.py 2024-05-19 18:20:55 +08:00
Mr Chen abcf6250e3 Update sphinx_make.py 2024-05-19 18:20:10 +08:00
Mr Chen 70370fcb0b Update sphinx_make.py 2024-05-19 18:19:57 +08:00
Mr Chen 57e7021be2 Update sphinx_make.py 2024-05-19 18:10:54 +08:00
Mr Chen 45b7abd0fb Update sphinx_make.py 2024-05-19 17:58:50 +08:00
Mr Chen a6d96bb4d8 Update sphinx_make.py 2024-05-19 17:57:27 +08:00
Mr Chen 2742ecfc67 Update sphinx_make.py 2024-05-19 17:49:08 +08:00
Mr Chen 60747c2f04 Update sphinx_make.py 2024-05-19 17:47:51 +08:00
Mr Chen 2b019a704a Update sphinx_make.py 2024-05-19 17:47:27 +08:00
Mr Chen 7e049a381b Update sphinx_make.py 2024-05-19 17:45:39 +08:00
Mr Chen 2f4cf46bcf Update sphinx_make.py 2024-05-19 17:45:27 +08:00
Mr Chen 706ee146b5 Update sphinx_make.py 2024-05-19 17:43:52 +08:00
Mr Chen 79b4d09d50 Update index.py 2024-05-19 17:37:47 +08:00
Mr Chen 2f0797daab Update sphinx_make.py 2024-05-19 17:34:37 +08:00
Mr Chen 8e47c73f5c Update sphinx_make.py 2024-05-19 17:27:22 +08:00
Mr Chen c90d752ae7 update 2024-05-19 17:21:19 +08:00
Mr Chen 7c96bbe72f Update sphinx_make.py 2024-05-19 16:56:27 +08:00
Mr Chen 53134b7ecc Update sphinx_make.py 2024-05-19 16:36:14 +08:00
Mr Chen bf859f7c7c Update sphinx_make.py 2024-05-19 16:29:16 +08:00
Mr Chen 0046d02ddf update 2024-05-19 16:24:33 +08:00
Mr Chen 5a0e7a3b60 update 2024-05-19 15:09:25 +08:00
Mr Chen a74ca20d0e up 2024-05-19 14:58:14 +08:00
Mr Chen 41109af522 Update install.sh 2024-05-19 11:27:13 +08:00
Mr Chen b3b23994eb Update sphinx.js 2024-05-19 02:40:11 +08:00
Mr Chen 02f3a147aa Update index.py 2024-05-19 02:36:34 +08:00
Mr Chen 10edf675f5 Update config_api.py 2024-05-19 02:35:24 +08:00
Mr Chen b07b6f0c94 update 2024-05-19 02:35:03 +08:00
Mr Chen 002639936e update 2024-05-19 01:32:31 +08:00
Mr Chen aac516c8f8 Update stats.js 2024-05-19 00:56:35 +08:00
Mr Chen efd07f6de2 网站统计 - 实时请求 可选择 2024-05-19 00:51:35 +08:00
Mr Chen fa17782573 Update index.py 2024-05-18 14:15:57 +08:00
Mr Chen 9ee3aebc8c Update index.py 2024-05-18 14:02:17 +08:00
Mr Chen b778c8cdeb Update index.py 2024-05-18 13:57:34 +08:00
Mr Chen 1d1e6b1d5b Update index.py 2024-05-18 12:49:46 +08:00
Mr Chen 70012ea7ee Update index.py 2024-05-18 11:56:56 +08:00
Mr Chen 0897fd9370 Update index.py 2024-05-18 11:51:51 +08:00
Mr Chen 477b8b38fd Update index.py 2024-05-18 10:58:10 +08:00
Mr Chen e805910b1b Update index.py 2024-05-18 10:54:06 +08:00
Mr Chen 2f9903753f Update index.py 2024-05-18 02:57:32 +08:00
Mr Chen 53cd6377bb Update stats.js 2024-05-18 00:57:55 +08:00
Mr Chen 56cf0271b8 Update index.py 2024-05-17 18:26:51 +08:00
Mr Chen 4fefb0efb2 Update index.py 2024-05-17 18:26:18 +08:00
Mr Chen 222b8bc4de Update index.py 2024-05-17 17:58:50 +08:00
Mr Chen dc0e6fe78a Update index.py 2024-05-17 17:57:14 +08:00
Mr Chen 22a21025af Update index.py 2024-05-17 17:56:11 +08:00
Mr Chen 7cd102c40f Update index.py 2024-05-17 17:55:47 +08:00
Mr Chen 5cd7a65737 Update index.py 2024-05-17 17:48:40 +08:00
Mr Chen c370486845 Update mysql.js 2024-05-17 16:59:08 +08:00
Mr Chen cb9857edf2 update 2024-05-17 16:51:10 +08:00
Mr Chen 8fb3410366 Update mysql.js 2024-05-17 16:26:55 +08:00
Mr Chen 659ef259b7 Update index.py 2024-05-17 15:44:33 +08:00
Mr Chen 9d89a92b78 Update mysql.js 2024-05-17 15:33:48 +08:00
Mr Chen 139f353034 update 2024-05-17 14:09:57 +08:00
Mr Chen 908b1cf684 Update backup.py 2024-05-17 14:05:25 +08:00
Mr Chen 07dc74c283 update 2024-05-17 13:56:47 +08:00
Mr Chen 61e136fb39 Update backup.py 2024-05-17 13:54:59 +08:00
Mr Chen 030e05b2cf Update index.py 2024-05-17 13:52:19 +08:00
Mr Chen 24750cfdb2 Update config_api.py 2024-05-17 13:35:26 +08:00
Mr Chen 975a69fce8 Update layout.html 2024-05-17 13:35:13 +08:00
Mr Chen 5f42f37ee2 Update mysql.sql 2024-05-17 05:24:48 +08:00
Mr Chen 9925daa363 Update index.py 2024-05-17 05:08:45 +08:00
Mr Chen 2572bde607 Update index.py 2024-05-17 05:05:06 +08:00
Mr Chen 572207105c Update index.py 2024-05-17 05:04:18 +08:00
Mr Chen 2b8804e960 同步问题 2024-05-17 05:01:39 +08:00
Mr Chen 310409c52b Update mw.tpl 2024-05-17 04:06:01 +08:00
Mr Chen 01e95fa6fb Update index.py 2024-05-17 03:36:27 +08:00
Mr Chen 848021a639 Update index.py 2024-05-17 03:27:20 +08:00
Mr Chen a679c0e90c Update index.py 2024-05-17 03:26:37 +08:00
Mr Chen 8d9eb6302c Update index.py 2024-05-17 03:20:29 +08:00
Mr Chen f6663ad370 Update index.py 2024-05-17 03:18:59 +08:00
Mr Chen 9964745e46 Update index.py 2024-05-17 01:49:11 +08:00
Mr Chen b570a4552a Update index.py 2024-05-17 01:48:00 +08:00
Mr Chen cd2655a6f9 Update index.py 2024-05-17 01:47:40 +08:00
Mr Chen 608b40a110 Update index.py 2024-05-17 01:46:17 +08:00
Mr Chen a65d42ed2e Update index.py 2024-05-17 01:43:57 +08:00
Mr Chen b0d8d4bef8 Update index.py 2024-05-17 01:43:19 +08:00
Mr Chen d9f657c236 Update index.py 2024-05-17 01:42:05 +08:00
Mr Chen de02bcd800 Update index.py 2024-05-17 01:39:46 +08:00
Mr Chen 1f8dbe1a5f Update index.py 2024-05-17 01:37:54 +08:00
Mr Chen 8f4949cf8d Update index.py 2024-05-17 01:35:43 +08:00
Mr Chen 6128186a2c Update index.py 2024-05-17 01:26:35 +08:00
Mr Chen 7f707f9aac Update index.py 2024-05-17 01:08:13 +08:00
Mr Chen 56c1cd88ee Update config_api.py 2024-05-17 01:02:36 +08:00
Mr Chen 5d0b8b1877 Update layout.html 2024-05-17 01:01:48 +08:00
Mr Chen e944c9fbc5 Update layout.html 2024-05-17 01:01:16 +08:00
Mr Chen 8cab94e26c Update config_api.py 2024-05-17 00:45:56 +08:00
Mr Chen edb26fd11c update 2024-05-17 00:44:52 +08:00
Mr Chen cfd3a07046 Update mysql.js 2024-05-17 00:32:27 +08:00
Mr Chen 8d4ffc431a Update index.py 2024-05-17 00:21:37 +08:00
Mr Chen 22b97ab255 updatae 2024-05-17 00:21:06 +08:00
Mr Chen 8cfc5c5344 Update stats.js 2024-05-16 21:09:10 +08:00
Mr Chen 01c9633a1b Update index.py 2024-05-16 20:32:22 +08:00
Mr Chen 160634d3d4 Update index.py 2024-05-16 20:09:21 +08:00
Mr Chen 59b4ec32a6 Update index.py 2024-05-16 19:58:22 +08:00
Mr Chen 08a2295cdc Update index.py 2024-05-16 19:57:51 +08:00
Mr Chen fb7dbd69e5 Update index.py 2024-05-16 19:43:55 +08:00
Mr Chen c212a30536 Update index.py 2024-05-16 19:42:04 +08:00
Mr Chen 7a7acd3a4d Update index.py 2024-05-16 19:39:45 +08:00
Mr Chen f5edd83164 Update index.py 2024-05-16 19:04:38 +08:00
Mr Chen 8ea3ac1a3c Update index.py 2024-05-16 19:04:21 +08:00
Mr Chen d241ceea3e Update index.py 2024-05-16 18:55:07 +08:00
Mr Chen 6b3f50374a Update index.py 2024-05-16 18:32:59 +08:00
Mr Chen b3ceb03365 Update index.py 2024-05-16 18:25:20 +08:00
Mr Chen 87b26a14df Update mysql.js 2024-05-16 18:25:02 +08:00
Mr Chen 9046a09c0a Update mysql.js 2024-05-16 18:18:22 +08:00
Mr Chen 8bbfdbbf66 Update index.py 2024-05-16 17:57:46 +08:00
Mr Chen fa2d5c690c Update index.py 2024-05-16 17:46:25 +08:00
Mr Chen 681dfc96cb update 2024-05-16 16:33:19 +08:00
Mr Chen 362f5eb427 Update index.py 2024-05-16 16:28:07 +08:00
Mr Chen b033bd1c73 Update index.py 2024-05-16 16:17:04 +08:00
Mr Chen ea94fa21d5 Update index.py 2024-05-16 16:14:51 +08:00
Mr Chen 36c1541179 Update index.py 2024-05-16 16:13:05 +08:00
Mr Chen d5864db956 update 2024-05-15 23:56:52 +08:00
Mr Chen 00008b657c Update index.py 2024-05-15 21:11:29 +08:00
Mr Chen 3abed25d71 Update index.py 2024-05-15 20:52:58 +08:00
Mr Chen 862ea95a77 Update index.py 2024-05-15 20:22:58 +08:00
Mr Chen 63d624d084 Update index.py 2024-05-15 20:11:59 +08:00
Mr Chen c580076307 Update index.py 2024-05-15 20:01:59 +08:00
Mr Chen 3fada81661 Update index.py 2024-05-15 20:00:36 +08:00
Mr Chen a3bd0c6bda Update index.py 2024-05-15 20:00:19 +08:00
Mr Chen 454b036908 Update index.py 2024-05-15 20:00:01 +08:00
Mr Chen 1af9b1ddaa Update index.py 2024-05-15 19:59:35 +08:00
Mr Chen 5818fb46f1 Update mysql.js 2024-05-15 19:36:15 +08:00
Mr Chen 1069fd5252 Update index.py 2024-05-15 19:33:53 +08:00
Mr Chen 76229b6651 Update index.py 2024-05-15 19:31:30 +08:00
Mr Chen 4c9e717559 Update index.py 2024-05-15 19:30:04 +08:00
Mr Chen 9ae61517a4 Update index.py 2024-05-15 19:20:02 +08:00
Mr Chen 952a173535 Update index.py 2024-05-15 19:11:28 +08:00
Mr Chen 5b9b03a153 Update index.py 2024-05-15 19:09:01 +08:00
Mr Chen 9475dbecfe Update index.py 2024-05-15 18:56:38 +08:00
Mr Chen 5a64835969 Update index.py 2024-05-15 18:56:00 +08:00
Mr Chen 8685f21ad9 Update index.py 2024-05-15 18:50:52 +08:00
Mr Chen 500ccff90b update 2024-05-15 17:48:41 +08:00
Mr Chen 85adb449c2 Update index.py 2024-05-15 17:38:49 +08:00
Mr Chen aa6748c09d Update index.py 2024-05-15 17:32:11 +08:00
Mr Chen 05992b5f82 Update index.py 2024-05-15 17:31:49 +08:00
Mr Chen 1a48e880d0 update 2024-05-15 17:29:02 +08:00
Mr Chen 1da749aea9 Update index.py 2024-05-15 14:45:23 +08:00
Mr Chen bf121ee6f8 Update index.py 2024-05-15 14:41:19 +08:00
Mr Chen 6b6e1f415f Update index.py 2024-05-15 14:40:33 +08:00
Mr Chen 5535d56304 Update index.py 2024-05-15 14:30:56 +08:00
Mr Chen bbc0dee2b6 Update index.py 2024-05-15 14:28:23 +08:00
Mr Chen 1da8f6bbb6 update 2024-05-15 14:21:43 +08:00
Mr Chen de15101a76 Update orm.py 2024-05-15 14:06:17 +08:00
Mr Chen ee99d14df3 Update index.py 2024-05-15 14:03:41 +08:00
Mr Chen 7bff08b3e5 Update index.py 2024-05-15 14:02:10 +08:00
Mr Chen faca0b183e update 2024-05-15 13:59:59 +08:00
Mr Chen 700b9a7c61 Update index.py 2024-05-15 13:53:30 +08:00
Mr Chen 53952d267e Update index.py 2024-05-15 13:42:57 +08:00
Mr Chen ff52646fd2 Update index.py 2024-05-15 13:40:00 +08:00
Mr Chen 1dcd4520f1 Update index.py 2024-05-15 13:37:23 +08:00
Mr Chen 21d86f0583 Update index.py 2024-05-15 13:33:08 +08:00
Mr Chen 3e95e3aaf2 Update index.py 2024-05-15 13:28:46 +08:00
Mr Chen 29c7367906 Update index.py 2024-05-15 13:01:44 +08:00
Mr Chen fc1e23f12d Update index.py 2024-05-15 12:31:52 +08:00
Mr Chen ae4af02909 Update index.py 2024-05-15 12:27:54 +08:00
Mr Chen 6b8716ae59 Update index.py 2024-05-15 12:07:57 +08:00
Mr Chen 661faf8503 Update index.py 2024-05-15 11:57:58 +08:00
Mr Chen d0881ff3eb Update index.py 2024-05-15 06:04:24 +08:00
Mr Chen b708d4821a Update index.py 2024-05-15 05:59:39 +08:00
Mr Chen 2f18bf5fb2 Update index.py 2024-05-15 05:50:25 +08:00
Mr Chen 2079076638 Update index.py 2024-05-15 05:42:44 +08:00
Mr Chen 6ec2e4f29f Update index.py 2024-05-15 05:41:51 +08:00
Mr Chen a07ed61305 Update index.py 2024-05-15 05:38:59 +08:00
Mr Chen 8031ea2c45 Update index.py 2024-05-15 05:35:14 +08:00
Mr Chen 24d3fc18d6 Update index.py 2024-05-15 05:05:29 +08:00
Mr Chen 4b200765aa Update index.py 2024-05-15 05:02:01 +08:00
Mr Chen 994ee32fbf Update index.py 2024-05-15 05:00:03 +08:00
Mr Chen c6a742a932 Update index.py 2024-05-15 04:57:51 +08:00
Mr Chen 478e03bca1 Update index.py 2024-05-15 04:42:42 +08:00
Mr Chen f4cff7bcf7 Update index.py 2024-05-15 04:35:52 +08:00
Mr Chen ff7d2f358e Update index.py 2024-05-15 04:35:12 +08:00
Mr Chen e114b07d7d Update index.py 2024-05-15 04:30:04 +08:00
Mr Chen 272b13b29c Update index.py 2024-05-15 04:24:13 +08:00
Mr Chen 8d875fe589 Update index.py 2024-05-15 04:23:17 +08:00
Mr Chen e85e6a7299 update 2024-05-15 04:22:14 +08:00
Mr Chen e8a3f8b014 Update orm.py 2024-05-15 04:16:49 +08:00
Mr Chen 585cd8b532 update_dev 2024-05-15 04:06:15 +08:00
Mr Chen c89ee89061 Update index.py 2024-05-15 01:20:42 +08:00
Mr Chen ea7310502f Update index.py 2024-05-15 01:19:02 +08:00
Mr Chen 507b34797d Update backup.py 2024-05-15 01:18:56 +08:00
Mr Chen 7baecfc4a0 Update README.md 2024-05-14 23:57:17 +08:00
Mr Chen 8a1b64a524 Update index.py 2024-05-14 23:21:44 +08:00
Mr Chen 89052225f9 Update index.py 2024-05-14 23:20:55 +08:00
Mr Chen b4c0030baa Update index.py 2024-05-14 23:20:26 +08:00
Mr Chen 342dfe407c Create README.md 2024-05-14 22:36:37 +08:00
Mr Chen f9020d3c7f Update index.py 2024-05-14 22:24:08 +08:00
Mr Chen d9eb331f32 Update index.py 2024-05-14 22:23:24 +08:00
Mr Chen bc9b5ab511 Update install.sh 2024-05-14 21:48:55 +08:00
Mr Chen b71e3196b8 Update index.py 2024-05-14 21:38:07 +08:00
Mr Chen 69b8c609aa Update index.py 2024-05-14 21:36:07 +08:00
Mr Chen b1ff41350c Update index.py 2024-05-14 21:02:24 +08:00
Mr Chen ddc9defe51 Update index.py 2024-05-14 20:59:46 +08:00
Mr Chen 2942e47630 Update backup.py 2024-05-14 20:58:49 +08:00
Mr Chen b825ed9c76 Update index.py 2024-05-14 20:56:01 +08:00
Mr Chen 5d2dd11163 Update index.py 2024-05-14 20:54:47 +08:00
Mr Chen 253de5e70f Update index.py 2024-05-14 20:11:59 +08:00
Mr Chen a1e900f5c9 Update index.py 2024-05-14 19:25:18 +08:00
Mr Chen cf44889c74 Update index.py 2024-05-14 19:25:00 +08:00
Mr Chen abd48636ab Update index.py 2024-05-14 19:03:17 +08:00
Mr Chen cc0cdd62b8 Update index.py 2024-05-14 16:24:33 +08:00
Mr Chen 0b95db3bd5 Update index.py 2024-05-14 16:13:06 +08:00
Mr Chen d473f0769f Update mongodb.js 2024-05-14 16:11:23 +08:00
Mr Chen dd498c2cfc update 2024-05-14 14:12:21 +08:00
Mr Chen 77b4d0452a Update mongodb.sh 2024-05-13 23:18:00 +08:00
Mr Chen 924e1ddbec Update layout.html 2024-05-13 22:35:10 +08:00
Mr Chen 3213cee5f8 Update mongodb.sh 2024-05-13 20:50:45 +08:00
Mr Chen 9fbf09f2ef Create tidb_debug.yaml 2024-05-13 10:41:00 +08:00
Mr Chen fd3e074cc3 Update install.sh 2024-05-13 03:18:02 +08:00
Mr Chen 574007da2c Update stats.js 2024-05-13 02:47:13 +08:00
Mr Chen ec6c1b2024 Update nginx.conf 2024-05-12 16:09:36 +08:00
Mr Chen 0dd6f47b2a Update index.py 2024-05-12 15:59:19 +08:00
Mr Chen 968827480e Update install.sh 2024-05-12 14:42:07 +08:00
Mr Chen 4bce8bb56d Update index.py 2024-05-12 14:35:53 +08:00
Mr Chen 00fa001c91 update 2024-05-12 14:27:36 +08:00
Mr Chen 2f65839b9a Update index.py 2024-05-12 14:17:49 +08:00
Mr Chen c0aa4bcc1e Update mongodb.js 2024-05-12 14:10:52 +08:00
Mr Chen 4bb345abc4 update 2024-05-12 14:06:56 +08:00
Mr Chen 3d552379bc update 2024-05-12 12:55:14 +08:00
Mr Chen 2de399b70f Update index.py 2024-05-12 12:46:41 +08:00
Mr Chen 7952570b45 update 2024-05-12 10:58:12 +08:00
Mr Chen 00269662bf Create tidb.yaml 2024-05-12 01:25:10 +08:00
Mr Chen fb6dbde4c9 Update config_api.py 2024-05-12 01:18:49 +08:00
Mr Chen 66a9ca2a5a Update files.js 2024-05-12 01:18:32 +08:00
Mr Chen 2e9561dd86 Update files_api.py 2024-05-12 01:10:04 +08:00
Mr Chen 47ff1e4458 Update r3.7.txt 2024-05-12 00:28:20 +08:00
Mr Chen 9f49595bad Update r3.6.txt 2024-05-12 00:28:08 +08:00
Mr Chen 3e6f4f32d5 Update install.sh 2024-05-11 23:42:26 +08:00
Mr Chen ae69d58ee8 Update install.sh 2024-05-11 23:40:45 +08:00
Mr Chen 1788054cc4 Update install.sh 2024-05-11 22:14:51 +08:00
Mr Chen 20b20d3cce Update index.py 2024-05-11 22:13:53 +08:00
Mr Chen 3fc3d90b6f Update install.sh 2024-05-11 22:09:57 +08:00
Mr Chen cef6db80a2 Update install.sh 2024-05-11 22:08:06 +08:00
Mr Chen 59aab80eab Update install.sh 2024-05-11 22:03:56 +08:00
Mr Chen 53c2c71d3e Update install.sh 2024-05-11 21:56:24 +08:00
Mr Chen d9d814202a Update install.sh 2024-05-11 21:51:40 +08:00
Mr Chen 941dbecfa2 Update index.py 2024-05-11 21:48:05 +08:00
Mr Chen cb7228c681 Update index.py 2024-05-11 21:47:55 +08:00
Mr Chen 7ac1cb2944 Update index.py 2024-05-11 21:47:46 +08:00
Mr Chen 0262d29218 Update install.sh 2024-05-11 21:46:48 +08:00
Mr Chen 5a955c2a33 tidb插件init 2024-05-11 20:21:07 +08:00
Mr Chen dd25ebe1e9 Merge pull request #574 from midoks/dev
修复
2024-05-11 13:54:29 +08:00
Mr Chen e92151c9c5 Update site_api.py 2024-05-11 13:52:52 +08:00
Mr Chen 16cc3eab40 Update site_api.py 2024-05-11 13:52:39 +08:00
Mr Chen 87138fc41f Merge pull request #573 from midoks/dev
迁移加入版本判断
2024-05-11 12:38:52 +08:00
Mr Chen 0787bff959 Update index.py 2024-05-11 12:36:57 +08:00
Mr Chen 8fc6855936 Merge pull request #572 from midoks/dev
mysql 不锁表导出
2024-05-11 12:13:27 +08:00
Mr Chen 43f21b4d58 Update backup.py 2024-05-11 12:06:59 +08:00
Mr Chen 56e75bc4cf Update backup.py 2024-05-11 11:59:38 +08:00
Mr Chen 10190201c7 Update backup.py 2024-05-11 11:46:49 +08:00
Mr Chen 3db2aa0034 Merge pull request #571 from midoks/dev
Update site_api.py
2024-05-11 11:08:09 +08:00
Mr Chen 9fa93b5c84 Update site_api.py 2024-05-11 11:07:23 +08:00
Mr Chen fe3e9eec35 Merge pull request #570 from midoks/dev
调优
2024-05-10 17:40:28 +08:00
Mr Chen 86a3922d4f Update index.html 2024-05-10 17:38:48 +08:00
Mr Chen aa50658245 update 2024-05-10 17:36:06 +08:00
Mr Chen 8cd78d7dea Update index.js 2024-05-10 17:25:19 +08:00
Mr Chen f33bfa214f Merge pull request #569 from midoks/dev
Update README.md
2024-05-10 16:26:26 +08:00
Mr Chen e231c38f1d Update README.md 2024-05-10 16:25:20 +08:00
91 changed files with 3385 additions and 724 deletions
+14 -12
View File
@@ -87,6 +87,7 @@ venv/
ENV/ ENV/
env.bak/ env.bak/
venv.bak/ venv.bak/
lib/python*
# Spyder project settings # Spyder project settings
.spyderproject .spyderproject
@@ -98,6 +99,12 @@ venv.bak/
# mkdocs documentation # mkdocs documentation
/site /site
bin
pyvenv.cfg
include
share
pip-selfcheck.json
# mypy # mypy
.mypy_cache/ .mypy_cache/
@@ -122,27 +129,17 @@ data/default.pl
data/backup.pl data/backup.pl
data/debug.pl data/debug.pl
data/default_site.pl data/default_site.pl
data/ssl.pl
ssl/input.pl
lib/python*
bin
pyvenv.cfg
include
share
pip-selfcheck.json
scripts/init.d/mw scripts/init.d/mw
scripts/mdserver-web scripts/mdserver-web
data/api_login.txt data/api_login.txt
data/sessions data/sessions
data/ssl.pl
data/port.pl data/port.pl
data/ipv6.pl data/ipv6.pl
data/restart.pl data/restart.pl
data/ssl.pl
data/edate.pl data/edate.pl
data/osname.pl data/osname.pl
data/only_netio_counters.pl data/only_netio_counters.pl
@@ -156,6 +153,10 @@ data/bind_domain.pl
data/unauthorized_status.pl data/unauthorized_status.pl
data/auth_secret.pl data/auth_secret.pl
ssl/local
ssl/nginx
ssl/choose.pl
plugins/vip_* plugins/vip_*
plugins/own_* plugins/own_*
plugins/my_* plugins/my_*
@@ -173,6 +174,7 @@ plugins/v2ray
plugins/frp plugins/frp
plugins/file_search plugins/file_search
plugins/proxysql plugins/proxysql
plugins/tidb
debug.out debug.out
+3 -3
View File
@@ -18,7 +18,7 @@
[![Wiki](https://img.shields.io/badge/MW-Wiki-red?style=for-the-badge&logo=wiki)](https://github.com/midoks/mdserver-web/wiki) [![Wiki](https://img.shields.io/badge/MW-Wiki-red?style=for-the-badge&logo=wiki)](https://github.com/midoks/mdserver-web/wiki)
[![](https://data.jsdelivr.com/v1/package/gh/midoks/mdserver-web/badge?style=for-the-badge)](https://www.jsdelivr.com/package/gh/midoks/mdserver-web) [![jsdelivr](https://data.jsdelivr.com/v1/package/gh/midoks/mdserver-web/badge)](https://www.jsdelivr.com/package/gh/midoks/mdserver-web)
* SSH终端工具 * SSH终端工具
* 面板收藏功能 * 面板收藏功能
@@ -110,9 +110,9 @@ docker run -itd --name mw-server --privileged=true -p 7200:7200 -p 80:80 -p 443:
``` ```
### 版本更新 0.16.8 ### 版本更新 0.17.0
- 首页对网络/磁盘IO进行更细致的展示。 - 面板SSL调整。
### JSDelivr安装地址 ### JSDelivr安装地址
+318 -222
View File
@@ -28,7 +28,7 @@ from flask import request
class config_api: class config_api:
__version = '0.16.8' __version = '0.17.0'
__api_addr = 'data/api.json' __api_addr = 'data/api.json'
# 统一默认配置文件 # 统一默认配置文件
@@ -42,7 +42,7 @@ class config_api:
'bind_domain' : 'data/bind_domain.pl', # 面板域名绑定 'bind_domain' : 'data/bind_domain.pl', # 面板域名绑定
'unauth_status' : 'data/unauthorized_status.pl', # URL路径未成功显示状态 'unauth_status' : 'data/unauthorized_status.pl', # URL路径未成功显示状态
'auth_secret': 'data/auth_secret.pl', # 二次验证密钥 'auth_secret': 'data/auth_secret.pl', # 二次验证密钥
'ssl':'data/ssl.pl', # ssl设置 'ssl':'ssl/choose.pl', # ssl设置
'hook_database' : 'data/hook_database.json', # 数据库钩子 'hook_database' : 'data/hook_database.json', # 数据库钩子
'hook_menu' : 'data/hook_menu.json', # 菜单钩子 'hook_menu' : 'data/hook_menu.json', # 菜单钩子
'hook_global_static' : 'data/hook_global_static.json', # 静态文件钩子 'hook_global_static' : 'data/hook_global_static.json', # 静态文件钩子
@@ -362,19 +362,23 @@ class config_api:
return mw.getJson(cert) return mw.getJson(cert)
def getPanelSslData(self): def getPanelSslData(self):
cert = {} rdata = {}
keyPath = 'ssl/private.pem' choose_file = self.__file['ssl']
certPath = 'ssl/cert.pem' rdata['choose'] = mw.readFile(choose_file)
keyPath = 'ssl/local/private.pem'
certPath = 'ssl/local/cert.pem'
if not os.path.exists(certPath): if not os.path.exists(certPath):
# 不再自动生成证书 mw.createLocalSSL()
# mw.createSSL()
cert['privateKey'] = '' cert = {}
cert['is_https'] = '' cert['privateKey'] = mw.readFile(keyPath)
cert['certPem'] = '' cert['is_https'] = ''
cert['rep'] = os.path.exists('ssl/input.pl') cert['certPem'] = mw.readFile(certPath)
cert['info'] = {'endtime': 0, 'subject': '无', cert['info'] = mw.getCertName(certPath)
'notAfter': '无', 'notBefore': '无', 'issuer': '无'} rdata['local'] = cert
return cert
panel_ssl = mw.getServerDir() + "/web_conf/nginx/vhost/panel.conf" panel_ssl = mw.getServerDir() + "/web_conf/nginx/vhost/panel.conf"
if not os.path.exists(panel_ssl): if not os.path.exists(panel_ssl):
@@ -384,21 +388,68 @@ class config_api:
if ssl_data.find('$server_port !~ 443') != -1: if ssl_data.find('$server_port !~ 443') != -1:
cert['is_https'] = 'checked' cert['is_https'] = 'checked'
keyPath = 'ssl/nginx/private.pem'
certPath = 'ssl/nginx/cert.pem'
cert = {}
cert['privateKey'] = mw.readFile(keyPath) cert['privateKey'] = mw.readFile(keyPath)
cert['certPem'] = mw.readFile(certPath) cert['certPem'] = mw.readFile(certPath)
cert['rep'] = os.path.exists('ssl/input.pl')
cert['info'] = mw.getCertName(certPath) cert['info'] = mw.getCertName(certPath)
return cert
rdata['nginx'] = cert
return rdata
# 面板本地SSL设置
def setPanelLocalSslApi(self):
cert = {}
keyPath = 'ssl/local/private.pem'
certPath = 'ssl/local/cert.pem'
if not os.path.exists(certPath):
mw.createLocalSSL()
choose_file = self.__file['ssl']
mw.writeFile(choose_file, 'local')
mw.restartMw()
return mw.returnJson(True, '设置成功')
# 关闭SSL
def closePanelSslApi(self):
choose_file = self.__file['ssl']
if os.path.exists(choose_file):
os.remove(choose_file)
local_ssl = 'ssl/local'
if os.path.exists(local_ssl):
mw.execShell('rm -rf '+ local_ssl)
nginx_ssl = 'ssl/nginx'
if os.path.exists(nginx_ssl):
mw.execShell('rm -rf '+ nginx_ssl)
mw.restartMw()
return mw.returnJson(True, '关闭SSL成功')
# 保存面板证书 # 保存面板证书
def savePanelSslApi(self): def savePanelSslApi(self):
keyPath = 'ssl/private.pem'
certPath = 'ssl/cert.pem'
checkCert = '/tmp/cert.pl'
choose = request.form.get('choose', '').strip()
certPem = request.form.get('certPem', '').strip() certPem = request.form.get('certPem', '').strip()
privateKey = request.form.get('privateKey', '').strip() privateKey = request.form.get('privateKey', '').strip()
if not mw.inArray(['local','nginx'], choose):
return mw.returnJson(True, '保存错误面板SSL类型!')
keyPath = 'ssl/'+choose+'/private.pem'
certPath = 'ssl/'+choose+'/cert.pem'
checkCert = '/tmp/cert.pl'
if not os.path.exists(keyPath):
return mw.returnJson(False, '【'+choose+'】SSL类型不存在,先申请!')
if(privateKey.find('KEY') == -1): if(privateKey.find('KEY') == -1):
return mw.returnJson(False, '秘钥错误,请检查!') return mw.returnJson(False, '秘钥错误,请检查!')
if(certPem.find('CERTIFICATE') == -1): if(certPem.find('CERTIFICATE') == -1):
@@ -411,258 +462,303 @@ class config_api:
mw.writeFile(certPath, certPem) mw.writeFile(certPath, certPem)
if not mw.checkCert(checkCert): if not mw.checkCert(checkCert):
return mw.returnJson(False, '证书错误,请检查!') return mw.returnJson(False, '证书错误,请检查!')
mw.writeFile('ssl/input.pl', 'True')
return mw.returnJson(True, '证书已保存!') return mw.returnJson(True, '证书已保存!')
# 设置面板SSL证书设置 # 设置面板SSL证书设置
def setPanelHttpToHttpsApi(self): # def setPanelHttpToHttpsApi(self):
bind_domain = self.__file['bind_domain'] # bind_domain = self.__file['bind_domain']
if not os.path.exists(bind_domain): # if not os.path.exists(bind_domain):
return mw.returnJson(False, '先要绑定域名!') # return mw.returnJson(False, '先要绑定域名!')
keyPath = 'ssl/private.pem' # choose_file = self.__file['ssl']
if not os.path.exists(keyPath): # choose = mw.readFile(choose_file)
return mw.returnJson(False, '未申请SSL证书!') # if choose == 'local':
# return mw.returnJson(False, '本地SSL无法使用!')
is_https = request.form.get('https', '').strip() # keyPath = 'ssl/nginx/private.pem'
# if not os.path.exists(keyPath):
# return mw.returnJson(False, '未申请SSL证书!')
panel_ssl = mw.getServerDir() + "/web_conf/nginx/vhost/panel.conf" # is_https = request.form.get('https', '').strip()
if not os.path.exists(panel_ssl):
return mw.returnJson(False, '未开启面板SSL!')
if is_https == 'false': # panel_ssl = mw.getServerDir() + "/web_conf/nginx/vhost/panel.conf"
conf = mw.readFile(panel_ssl) # if not os.path.exists(panel_ssl):
if conf: # return mw.returnJson(False, '未开启面板SSL!')
if conf.find('ssl_certificate') == -1:
return mw.returnJson(False, '当前未开启SSL')
to = "#error_page 404/404.html;\n\
#HTTP_TO_HTTPS_START\n\
if ($server_port !~ 443){\n\
rewrite ^(/.*)$ https://$host$1 permanent;\n\
}\n\
#HTTP_TO_HTTPS_END"
conf = conf.replace('#error_page 404/404.html;', to)
mw.writeFile(panel_ssl, conf)
else:
conf = mw.readFile(panel_ssl)
if conf:
rep = "\n\\s*#HTTP_TO_HTTPS_START(.|\n){1,300}#HTTP_TO_HTTPS_END"
conf = re.sub(rep, '', conf)
rep = "\\s+if.+server_port.+\n.+\n\\s+\\s*}"
conf = re.sub(rep, '', conf)
mw.writeFile(panel_ssl, conf)
mw.restartWeb() # if is_https == 'false':
# conf = mw.readFile(panel_ssl)
# if conf:
# if conf.find('ssl_certificate') == -1:
# return mw.returnJson(False, '当前未开启SSL')
# to = "#error_page 404/404.html;\n\
# #HTTP_TO_HTTPS_START\n\
# if ($server_port !~ 443){\n\
# rewrite ^(/.*)$ https://$host$1 permanent;\n\
# }\n\
# #HTTP_TO_HTTPS_END"
# conf = conf.replace('#error_page 404/404.html;', to)
# mw.writeFile(panel_ssl, conf)
# else:
# conf = mw.readFile(panel_ssl)
# if conf:
# rep = "\n\\s*#HTTP_TO_HTTPS_START(.|\n){1,300}#HTTP_TO_HTTPS_END"
# conf = re.sub(rep, '', conf)
# rep = "\\s+if.+server_port.+\n.+\n\\s+\\s*}"
# conf = re.sub(rep, '', conf)
# mw.writeFile(panel_ssl, conf)
action = '开启' # mw.restartNginx()
if is_https == 'true':
action = '关闭' # action = '开启'
return mw.returnJson(True, action + 'HTTPS跳转成功!') # if is_https == 'true':
# action = '关闭'
# return mw.returnJson(True, action + 'HTTPS跳转成功!')
# 删除面板证书 # 删除面板证书
def delPanelSslApi(self): def delPanelSslApi(self):
bind_domain = self.__file['bind_domain'] ip = mw.getLocalIp()
if not os.path.exists(bind_domain): if mw.isAppleSystem():
return mw.returnJson(False, '未绑定域名!') ip = '127.0.0.1'
siteName = mw.readFile(bind_domain).strip() port = mw.readFile('data/port.pl').strip()
src_letpath = mw.getServerDir() + '/web_conf/letsencrypt/' + siteName choose = request.form.get('choose', '').strip()
dst_letpath = mw.getRunDir() + '/ssl' if not mw.inArray(['local','nginx'], choose):
dst_csrpath = dst_letpath + '/cert.pem' return mw.returnJson(True, '删除错误面板SSL类型!')
dst_keypath = dst_letpath + '/private.pem'
if os.path.exists(src_letpath) or os.path.exists(dst_csrpath): to_panel_url = 'http://'+ip+":"+port+'/config'
if os.path.exists(src_letpath):
mw.execShell('rm -rf ' + src_letpath)
if os.path.exists(dst_csrpath):
mw.execShell('rm -rf ' + dst_csrpath)
if os.path.exists(dst_keypath):
mw.execShell('rm -rf ' + dst_keypath)
# mw.restartWeb()
return mw.returnJson(True, '已经删除SSL!')
# mw.restartWeb() if choose == 'local':
return mw.returnJson(False, '已经不存在SSL!') dst_path = mw.getRunDir() + '/ssl/local'
ssl_file = self.__file['ssl']
if os.path.exists(dst_path):
mw.execShell('rm -rf ' + dst_path)
mw.execShell('rm -rf ' + ssl_file)
mw.restartMw();
return mw.returnJson(True, '删除本地面板SSL成功!',to_panel_url)
else:
return mw.returnJson(True, '已经删除本地面板SSL!',to_panel_url)
if choose == 'nginx':
bind_domain = self.__file['bind_domain']
if not os.path.exists(bind_domain):
return mw.returnJson(False, '未绑定域名!')
siteName = mw.readFile(bind_domain).strip()
src_path = mw.getServerDir() + '/web_conf/letsencrypt/' + siteName
dst_path = mw.getRunDir() + '/ssl/nginx'
dst_csrpath = dst_path + '/cert.pem'
dst_keypath = dst_path + '/private.pem'
if os.path.exists(src_path) or os.path.exists(dst_path):
if os.path.exists(src_letpath):
mw.execShell('rm -rf ' + src_letpath)
if os.path.exists(dst_csrpath):
mw.execShell('rm -rf ' + dst_csrpath)
if os.path.exists(dst_keypath):
mw.execShell('rm -rf ' + dst_keypath)
mw.restartNginx()
return mw.returnJson(True, '删除面板SSL成功!')
mw.restartNginx()
mw.restartMw()
return mw.returnJson(False, '已经删除面板SSL!')
return mw.returnJson(False, '未知类型!')
# 申请面板let证书 # 申请面板let证书
def applyPanelLetSslApi(self): # def applyPanelAcmeSslApi(self):
# check domain is bind? # bind_domain = self.__file['bind_domain']
bind_domain = self.__file['bind_domain'] # if not os.path.exists(bind_domain):
if not os.path.exists(bind_domain): # return mw.returnJson(False, '先要绑定域名!')
return mw.returnJson(False, '先要绑定域名!')
siteName = mw.readFile(bind_domain).strip() # # 生成nginx配置
auth_to = mw.getRunDir() + "/tmp" # domain = mw.readFile(bind_domain)
to_args = { # panel_tpl = mw.getRunDir() + "/data/tpl/nginx_panel.conf"
'domains': [siteName], # dst_panel_path = mw.getServerDir() + "/web_conf/nginx/vhost/panel.conf"
'auth_type': 'http', # if not os.path.exists(dst_panel_path):
'auth_to': auth_to, # reg = r"^([\w\-\*]{1,100}\.){1,4}(\w{1,10}|\w{1,10}\.\w{1,10})$"
} # if not re.match(reg, domain):
# return mw.returnJson(False, '主域名格式不正确')
src_letpath = mw.getServerDir() + '/web_conf/letsencrypt/' + siteName # op_dir = mw.getServerDir() + "/openresty"
src_csrpath = src_letpath + "/fullchain.pem" # 生成证书路径 # if not os.path.exists(op_dir):
src_keypath = src_letpath + "/privkey.pem" # 密钥文件路径 # return mw.returnJson(False, '依赖OpenResty,先安装启动它!')
dst_letpath = mw.getRunDir() + '/ssl' # content = mw.readFile(panel_tpl)
dst_csrpath = dst_letpath + '/cert.pem' # content = content.replace("{$PORT}", "80")
dst_keypath = dst_letpath + '/private.pem' # content = content.replace("{$SERVER_NAME}", domain)
# content = content.replace("{$PANAL_PORT}", mw.readFile('data/port.pl'))
# content = content.replace("{$LOGPATH}", mw.getRunDir() + '/logs')
# content = content.replace("{$PANAL_ADDR}", mw.getRunDir())
# mw.writeFile(dst_panel_path, content)
# mw.restartNginx()
is_already_apply = False # siteName = mw.readFile(bind_domain).strip()
# auth_to = mw.getRunDir() + "/tmp"
# to_args = {
# 'domains': [siteName],
# 'auth_type': 'http',
# 'auth_to': auth_to,
# }
if not os.path.exists(src_letpath): # src_path = mw.getServerDir() + '/web_conf/letsencrypt/' + siteName
import cert_api # src_csrpath = src_path + "/fullchain.pem" # 生成证书路径
data = cert_api.cert_api().applyCertApi(to_args) # src_keypath = src_path + "/privkey.pem" # 密钥文件路径
if not data['status']:
msg = data['msg']
if type(data['msg']) != str:
msg = data['msg'][0]
emsg = data['msg'][1]['challenges'][0]['error']
msg = msg + '<p><span>响应状态:</span>' + str(emsg['status']) + '</p><p><span>错误类型:</span>' + emsg[
'type'] + '</p><p><span>错误代码:</span>' + emsg['detail'] + '</p>'
return mw.returnJson(data['status'], msg, data['msg'])
else:
is_already_apply = True
mw.buildSoftLink(src_csrpath, dst_csrpath, True) # dst_path = mw.getRunDir() + '/ssl/nginx'
mw.buildSoftLink(src_keypath, dst_keypath, True) # dst_csrpath = dst_path + '/cert.pem'
mw.execShell('echo "lets" > "' + dst_letpath + '/README"') # dst_keypath = dst_path + '/private.pem'
data = self.getPanelSslData() # is_already_apply = False
tmp_well_know = auth_to + '/.well-known' # if not os.path.exists(src_path):
if os.path.exists(tmp_well_know): # import cert_api
mw.execShell('rm -rf ' + tmp_well_know) # data = cert_api.cert_api().applyCertApi(to_args)
# if not data['status']:
# msg = data['msg']
# if type(data['msg']) != str:
# msg = data['msg'][0]
# emsg = data['msg'][1]['challenges'][0]['error']
# msg = msg + '<p><span>响应状态:</span>' + str(emsg['status']) + '</p><p><span>错误类型:</span>' + emsg[
# 'type'] + '</p><p><span>错误代码:</span>' + emsg['detail'] + '</p>'
# return mw.returnJson(data['status'], msg, data['msg'])
# else:
# is_already_apply = True
if is_already_apply: # mw.buildSoftLink(src_csrpath, dst_csrpath, True)
return mw.returnJson(True, '重复申请!', data) # mw.buildSoftLink(src_keypath, dst_keypath, True)
# mw.execShell('echo "acme" > "' + dst_path + '/README"')
return mw.returnJson(True, '申请成功!', data) # tmp_well_know = auth_to + '/.well-known'
# if os.path.exists(tmp_well_know):
# mw.execShell('rm -rf ' + tmp_well_know)
# if os.path.exists(dst_path):
# choose_file = self.__file['ssl']
# mw.writeFile(choose_file, 'nginx')
# data = self.getPanelSslData()
# if is_already_apply:
# return mw.returnJson(True, '重复申请!', data)
# return mw.returnJson(True, '申请成功!', data)
def setPanelDomainApi(self): def setPanelDomainApi(self):
domain = request.form.get('domain', '') domain = request.form.get('domain', '')
panel_tpl = mw.getRunDir() + "/data/tpl/nginx_panel.conf"
dst_panel_path = mw.getServerDir() + "/web_conf/nginx/vhost/panel.conf"
cfg_domain = self.__file['bind_domain'] cfg_domain = self.__file['bind_domain']
port = mw.readFile('data/port.pl').strip()
ip = mw.getLocalIp()
if domain == '': if domain == '':
os.remove(cfg_domain) if os.path.exists(cfg_domain):
os.remove(dst_panel_path) os.remove(cfg_domain)
mw.restartWeb()
return mw.returnJson(True, '清空域名成功!')
reg = r"^([\w\-\*]{1,100}\.){1,4}(\w{1,10}|\w{1,10}\.\w{1,10})$" to_panel_url = 'http://'+ip+":"+port+'/config'
if not re.match(reg, domain):
return mw.returnJson(False, '主域名格式不正确') mw.restartMw()
return mw.returnJson(True, '清空域名成功!', to_panel_url)
op_dir = mw.getServerDir() + "/openresty"
if not os.path.exists(op_dir):
return mw.returnJson(False, '依赖OpenResty,先安装启动它!')
content = mw.readFile(panel_tpl)
content = content.replace("{$PORT}", "80")
content = content.replace("{$SERVER_NAME}", domain)
content = content.replace("{$PANAL_PORT}", mw.readFile('data/port.pl'))
content = content.replace("{$LOGPATH}", mw.getRunDir() + '/logs')
content = content.replace("{$PANAL_ADDR}", mw.getRunDir())
mw.writeFile(dst_panel_path, content)
mw.restartWeb()
mw.writeFile(cfg_domain, domain) mw.writeFile(cfg_domain, domain)
return mw.returnJson(True, '设置域名成功!') to_panel_url = 'http://'+domain+":"+port+'/config'
mw.restartMw()
return mw.returnJson(True, '设置域名成功!',to_panel_url)
# 设置面板SSL # 设置面板SSL
def setPanelSslApi(self): # def setPanelSslApi(self):
sslConf = mw.getRunDir() + '/' + self.__file['ssl'] # sslConf = mw.getRunDir() + '/' + self.__file['ssl']
panel_tpl = mw.getRunDir() + "/data/tpl/nginx_panel.conf" # panel_tpl = mw.getRunDir() + "/data/tpl/nginx_panel.conf"
dst_panel_path = mw.getServerDir() + "/web_conf/nginx/vhost/panel.conf" # dst_panel_path = mw.getServerDir() + "/web_conf/nginx/vhost/panel.conf"
if os.path.exists(sslConf): # if os.path.exists(sslConf):
os.system('rm -f ' + sslConf) # os.system('rm -f ' + sslConf)
conf = mw.readFile(dst_panel_path) # conf = mw.readFile(dst_panel_path)
if conf: # if conf:
rep = "\\s+ssl_certificate\\s+.+;\\s+ssl_certificate_key\\s+.+;" # rep = "\\s+ssl_certificate\\s+.+;\\s+ssl_certificate_key\\s+.+;"
conf = re.sub(rep, '', conf) # conf = re.sub(rep, '', conf)
rep = "\\s+ssl_protocols\\s+.+;\n" # rep = "\\s+ssl_protocols\\s+.+;\n"
conf = re.sub(rep, '', conf) # conf = re.sub(rep, '', conf)
rep = "\\s+ssl_ciphers\\s+.+;\n" # rep = "\\s+ssl_ciphers\\s+.+;\n"
conf = re.sub(rep, '', conf) # conf = re.sub(rep, '', conf)
rep = "\\s+ssl_prefer_server_ciphers\\s+.+;\n" # rep = "\\s+ssl_prefer_server_ciphers\\s+.+;\n"
conf = re.sub(rep, '', conf) # conf = re.sub(rep, '', conf)
rep = "\\s+ssl_session_cache\\s+.+;\n" # rep = "\\s+ssl_session_cache\\s+.+;\n"
conf = re.sub(rep, '', conf) # conf = re.sub(rep, '', conf)
rep = "\\s+ssl_session_timeout\\s+.+;\n" # rep = "\\s+ssl_session_timeout\\s+.+;\n"
conf = re.sub(rep, '', conf) # conf = re.sub(rep, '', conf)
rep = "\\s+ssl_ecdh_curve\\s+.+;\n" # rep = "\\s+ssl_ecdh_curve\\s+.+;\n"
conf = re.sub(rep, '', conf) # conf = re.sub(rep, '', conf)
rep = "\\s+ssl_session_tickets\\s+.+;\n" # rep = "\\s+ssl_session_tickets\\s+.+;\n"
conf = re.sub(rep, '', conf) # conf = re.sub(rep, '', conf)
rep = "\\s+ssl_stapling\\s+.+;\n" # rep = "\\s+ssl_stapling\\s+.+;\n"
conf = re.sub(rep, '', conf) # conf = re.sub(rep, '', conf)
rep = "\\s+ssl_stapling_verify\\s+.+;\n" # rep = "\\s+ssl_stapling_verify\\s+.+;\n"
conf = re.sub(rep, '', conf) # conf = re.sub(rep, '', conf)
rep = "\\s+ssl\\s+on;" # rep = "\\s+ssl\\s+on;"
conf = re.sub(rep, '', conf) # conf = re.sub(rep, '', conf)
rep = "\\s+error_page\\s497.+;" # rep = "\\s+error_page\\s497.+;"
conf = re.sub(rep, '', conf) # conf = re.sub(rep, '', conf)
rep = "\\s+if.+server_port.+\n.+\n\\s+\\s*}" # rep = "\\s+if.+server_port.+\n.+\n\\s+\\s*}"
conf = re.sub(rep, '', conf) # conf = re.sub(rep, '', conf)
rep = "\\s+listen\\s+443.*;" # rep = "\\s+listen\\s+443.*;"
conf = re.sub(rep, '', conf) # conf = re.sub(rep, '', conf)
rep = "\\s+listen\\s+\\[\\:\\:\\]\\:443.*;" # rep = "\\s+listen\\s+\\[\\:\\:\\]\\:443.*;"
conf = re.sub(rep, '', conf) # conf = re.sub(rep, '', conf)
mw.writeFile(dst_panel_path, conf) # mw.writeFile(dst_panel_path, conf)
mw.writeLog('面板配置', '面板SSL关闭成功!') # mw.writeLog('面板配置', '面板SSL关闭成功!')
mw.restartWeb() # mw.restartWeb()
return mw.returnJson(True, 'SSL已关闭,请使用http协议访问面板!') # return mw.returnJson(True, 'SSL已关闭,请使用http协议访问面板!')
else: # else:
try: # try:
mw.writeFile(sslConf, 'True') # mw.writeFile(sslConf, 'True')
keyPath = mw.getRunDir() + '/ssl/private.pem' # keyPath = mw.getRunDir() + '/ssl/private.pem'
certPath = mw.getRunDir() + '/ssl/cert.pem' # certPath = mw.getRunDir() + '/ssl/cert.pem'
conf = mw.readFile(dst_panel_path) # conf = mw.readFile(dst_panel_path)
if conf: # if conf:
if conf.find('ssl_certificate') == -1: # if conf.find('ssl_certificate') == -1:
sslStr = """#error_page 404/404.html; # sslStr = """#error_page 404/404.html;
ssl_certificate %s; # ssl_certificate %s;
ssl_certificate_key %s; # ssl_certificate_key %s;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2; # ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:HIGH:!aNULL:!MD5:!RC4:!DHE; # ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:HIGH:!aNULL:!MD5:!RC4:!DHE;
ssl_prefer_server_ciphers on; # ssl_prefer_server_ciphers on;
ssl_session_cache shared:SSL:10m; # ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m; # ssl_session_timeout 10m;
error_page 497 https://$host$request_uri;""" % (certPath, keyPath) # error_page 497 https://$host$request_uri;""" % (certPath, keyPath)
if(conf.find('ssl_certificate') != -1): # if(conf.find('ssl_certificate') != -1):
return mw.returnJson(True, 'SSL开启成功!') # return mw.returnJson(True, 'SSL开启成功!')
conf = conf.replace('#error_page 404/404.html;', sslStr) # conf = conf.replace('#error_page 404/404.html;', sslStr)
rep = "listen\\s+([0-9]+)\\s*[default_server]*;" # rep = "listen\\s+([0-9]+)\\s*[default_server]*;"
tmp = re.findall(rep, conf) # tmp = re.findall(rep, conf)
if not mw.inArray(tmp, '443'): # if not mw.inArray(tmp, '443'):
listen = re.search(rep, conf).group() # listen = re.search(rep, conf).group()
http_ssl = "\n\tlisten 443 ssl http2;" # http_ssl = "\n\tlisten 443 ssl http2;"
http_ssl = http_ssl + "\n\tlisten [::]:443 ssl http2;" # http_ssl = http_ssl + "\n\tlisten [::]:443 ssl http2;"
conf = conf.replace(listen, listen + http_ssl) # conf = conf.replace(listen, listen + http_ssl)
mw.backFile(dst_panel_path) # mw.backFile(dst_panel_path)
mw.writeFile(dst_panel_path, conf) # mw.writeFile(dst_panel_path, conf)
isError = mw.checkWebConfig() # isError = mw.checkWebConfig()
if(isError != True): # if(isError != True):
mw.restoreFile(dst_panel_path) # mw.restoreFile(dst_panel_path)
return mw.returnJson(False, '证书错误: <br><a style="color:red;">' + isError.replace("\n", '<br>') + '</a>') # return mw.returnJson(False, '证书错误: <br><a style="color:red;">' + isError.replace("\n", '<br>') + '</a>')
except Exception as ex: # except Exception as ex:
return mw.returnJson(False, '开启失败:' + str(ex)) # return mw.returnJson(False, '开启失败:' + str(ex))
mw.restartWeb() # mw.restartWeb()
return mw.returnJson(True, '开启成功,请使用https协议访问面板!') # return mw.returnJson(True, '开启成功,请使用https协议访问面板!')
def getApi(self): def getApi(self):
data = {} data = {}
@@ -958,7 +1054,7 @@ class config_api:
mw.writeFile(auth, crypt_data) mw.writeFile(auth, crypt_data)
ip = mw.getHostAddr() ip = mw.getHostAddr()
url = pyotp.totp.TOTP(sec).provisioning_uri(name=ip, issuer_name='mdserver-web') url = pyotp.totp.TOTP(sec).provisioning_uri(name=ip, issuer_name=tag)
rdata = {} rdata = {}
rdata['secret'] = sec rdata['secret'] = sec
+5
View File
@@ -42,8 +42,13 @@ class Sql():
self.__DB_CONN = sqlite3.connect(self.__DB_FILE) self.__DB_CONN = sqlite3.connect(self.__DB_FILE)
self.__DB_CONN.text_factory = str self.__DB_CONN.text_factory = str
except Exception as ex: except Exception as ex:
# print(mw.getTracebackInfo())
return "error: " + str(ex) return "error: " + str(ex)
def changeTextFactoryToBytes(self):
self.__DB_CONN.text_factory = bytes
return True
def autoTextFactory(self): def autoTextFactory(self):
if sys.version_info[0] == 3: if sys.version_info[0] == 3:
self.__DB_CONN.text_factory = lambda x: str( self.__DB_CONN.text_factory = lambda x: str(
+15
View File
@@ -121,6 +121,7 @@ class files_api:
def fileAccessApi(self): def fileAccessApi(self):
filename = request.form.get('filename', '') filename = request.form.get('filename', '')
data = self.getAccess(filename) data = self.getAccess(filename)
data['sys_users'] = self.getSysUserList()
return mw.getJson(data) return mw.getJson(data)
def setFileAccessApi(self): def setFileAccessApi(self):
@@ -933,6 +934,20 @@ class files_api:
data['chown'] = 'www' data['chown'] = 'www'
return data return data
def getSysUserList(self):
pwd_file = '/etc/passwd'
if os.path.exists(pwd_file):
content = mw.readFile(pwd_file)
clist = content.split('\n')
sys_users = []
for line in clist:
if line.find(":")<0:
continue
lines = line.split(":",1)
sys_users.append(lines[0])
return sys_users
return ['root','mysql','www']
# 计算文件数量 # 计算文件数量
def getCount(self, path, search): def getCount(self, path, search):
i = 0 i = 0
+58 -23
View File
@@ -79,10 +79,11 @@ def getRootDir():
def getPluginDir(): def getPluginDir():
return getRunDir() + '/plugins' return getRunDir() + '/plugins'
def getPanelDataDir(): def getPanelDataDir():
return getRunDir() + '/data' return getRunDir() + '/data'
def getMWLogs():
return getRunDir() + '/logs'
def getPanelTmp(): def getPanelTmp():
return getRunDir() + '/tmp' return getRunDir() + '/tmp'
@@ -346,6 +347,9 @@ def restartMw():
import system_api import system_api
system_api.system_api().restartMw() system_api.system_api().restartMw()
def restartNginx(self):
writeFile('data/restart_nginx.pl', 'True')
return True
def checkWebConfig(): def checkWebConfig():
op_dir = getServerDir() + '/openresty/nginx' op_dir = getServerDir() + '/openresty/nginx'
@@ -1287,25 +1291,46 @@ def checkDomainPanel():
domain = readFile('data/bind_domain.pl') domain = readFile('data/bind_domain.pl')
port = readFile('data/port.pl').strip() port = readFile('data/port.pl').strip()
npid = getServerDir() + "/openresty/nginx/logs/nginx.pid" scheme = 'http'
if not os.path.exists(npid):
choose_file = getRunDir()+'/ssl/choose.pl'
if os.path.exists(choose_file):
choose = readFile(choose_file).strip()
if not inArray(['local','nginx'], choose):
return False
else:
return False return False
nconf = getServerDir() + "/web_conf/nginx/vhost/panel.conf" local_ssl = getRunDir()+'/ssl/local'
if os.path.exists(nconf): if choose == 'local':
port = "80" scheme = 'https'
if domain: if choose == 'nginx':
client_ip = getClientIp() # print(port)
if client_ip in ['127.0.0.1', 'localhost', '::1']: npid = getServerDir() + "/openresty/nginx/logs/nginx.pid"
if not os.path.exists(npid):
return False return False
if tmp.strip().lower() != domain.strip().lower():
from flask import Flask, redirect, request, url_for nconf = getServerDir() + "/web_conf/nginx/vhost/panel.conf"
to = "http://" + domain + ":" + str(port) if os.path.exists(nconf):
return redirect(to, code=302) port = "80"
if not domain:
return False
client_ip = getClientIp()
if client_ip in ['127.0.0.1', 'localhost', '::1']:
return False
if tmp.strip().lower() != domain.strip().lower():
from flask import Flask, redirect, request, url_for
to = scheme + "://" + domain + ":" + str(port)
# print(to)
return redirect(to, code=302)
return False return False
def createLinuxUser(user, group): def createLinuxUser(user, group):
execShell("groupadd {}".format(group)) execShell("groupadd {}".format(group))
execShell('useradd -s /sbin/nologin -g {} {}'.format(user, group)) execShell('useradd -s /sbin/nologin -g {} {}'.format(user, group))
@@ -1810,28 +1835,38 @@ def getCertName(certPath):
return None return None
def createSSL(): def createLocalSSL():
if not os.path.exists('ssl/local'):
execShell('mkdir -p ssl/local')
# 自签证书 # 自签证书
if os.path.exists('ssl/input.pl'): # if os.path.exists('ssl/local/input.pl'):
return True # return True
client_ip = getClientIp()
import OpenSSL import OpenSSL
key = OpenSSL.crypto.PKey() key = OpenSSL.crypto.PKey()
key.generate_key(OpenSSL.crypto.TYPE_RSA, 2048) key.generate_key(OpenSSL.crypto.TYPE_RSA, 2048)
cert = OpenSSL.crypto.X509() cert = OpenSSL.crypto.X509()
cert.set_serial_number(0) cert.set_serial_number(0)
cert.get_subject().CN = getLocalIp()
if client_ip == '127.0.0.1':
cert.get_subject().CN = '127.0.0.1'
else:
cert.get_subject().CN = getLocalIp()
cert.set_issuer(cert.get_subject()) cert.set_issuer(cert.get_subject())
cert.gmtime_adj_notBefore(0) cert.gmtime_adj_notBefore(0)
cert.gmtime_adj_notAfter(86400 * 3650) cert.gmtime_adj_notAfter(86400 * 3650)
cert.set_pubkey(key) cert.set_pubkey(key)
cert.sign(key, 'md5') cert.sign(key, 'md5')
cert_ca = OpenSSL.crypto.dump_certificate( cert_ca = OpenSSL.crypto.dump_certificate(OpenSSL.crypto.FILETYPE_PEM, cert)
OpenSSL.crypto.FILETYPE_PEM, cert) private_key = OpenSSL.crypto.dump_privatekey(OpenSSL.crypto.FILETYPE_PEM, key)
private_key = OpenSSL.crypto.dump_privatekey(
OpenSSL.crypto.FILETYPE_PEM, key)
if len(cert_ca) > 100 and len(private_key) > 100: if len(cert_ca) > 100 and len(private_key) > 100:
writeFile('ssl/cert.pem', cert_ca, 'wb+') writeFile('ssl/local/cert.pem', cert_ca, 'wb+')
writeFile('ssl/private.pem', private_key, 'wb+') writeFile('ssl/local/private.pem', private_key, 'wb+')
return True return True
return False return False
+17 -6
View File
@@ -1960,6 +1960,19 @@ class site_api:
proxy_site_path) if os.path.exists(proxy_site_path) else "" proxy_site_path) if os.path.exists(proxy_site_path) else ""
data = json.loads(data_content) if data_content != "" else [] data = json.loads(data_content) if data_content != "" else []
proxy_action = 'add'
if _id == "":
_id = mw.md5("{}".format(_name))
else:
proxy_action = 'edit'
if proxy_action == "add":
for item in data:
if item["name"] == _name:
return mw.returnJson(False, "名称重复!!")
if item["from"] == _from:
return mw.returnJson(False, "代理目录已存在!!")
tpl = "#PROXY-START\n\ tpl = "#PROXY-START\n\
location ^~ {from} {\n\ location ^~ {from} {\n\
proxy_pass {to};\n\ proxy_pass {to};\n\
@@ -2016,15 +2029,9 @@ location ^~ {from} {\n\
else: else:
tpl = tpl.replace("{proxy_cache}", tpl_proxy_nocache, 999) tpl = tpl.replace("{proxy_cache}", tpl_proxy_nocache, 999)
proxy_action = 'add'
if _id == "":
_id = mw.md5("{}".format(_name))
else:
proxy_action = 'edit'
conf_proxy = "{}/{}.conf".format(self.getProxyPath(_siteName), _id) conf_proxy = "{}/{}.conf".format(self.getProxyPath(_siteName), _id)
conf_bk = "{}/{}.conf.txt".format(self.getProxyPath(_siteName), _id) conf_bk = "{}/{}.conf.txt".format(self.getProxyPath(_siteName), _id)
mw.writeFile(conf_proxy, tpl) mw.writeFile(conf_proxy, tpl)
rule_test = mw.checkWebConfig() rule_test = mw.checkWebConfig()
@@ -2655,6 +2662,10 @@ location ^~ {from} {\n\
mw.writeFile(rewrite_file, '') mw.writeFile(rewrite_file, '')
def add(self, webname, port, ps, path, version): def add(self, webname, port, ps, path, version):
site_root_dir = mw.getWwwDir()
if site_root_dir == path.rstrip('/'):
return mw.returnJson(False, '不要以网站根目录创建站点!')
siteMenu = json.loads(webname) siteMenu = json.loads(webname)
self.siteName = self.toPunycode( self.siteName = self.toPunycode(
siteMenu['domain'].strip().split(':')[0]).strip() siteMenu['domain'].strip().split(':')[0]).strip()
+2 -2
View File
@@ -686,8 +686,8 @@ class system_api:
for disk_name in diskio_group.keys(): for disk_name in diskio_group.keys():
diskInfo[disk_name] = {} diskInfo[disk_name] = {}
# print('disk_name',disk_name) # print('disk_name',disk_name)
# print('diskio_group',diskio_group[disk_name]) # print(diskio_group[disk_name].write_time , diskio_cache[disk_name].write_time)
# print('diskio_cache',diskio_cache[disk_name]) # 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]['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]['write_count'] = int((diskio_group[disk_name].write_count - diskio_cache[disk_name].write_count) / stime)
+29 -5
View File
@@ -17,37 +17,50 @@ class ORM:
__DB_CUR = None __DB_CUR = None
__DB_ERR = None __DB_ERR = None
__DB_CNF = '/etc/my.cnf' __DB_CNF = '/etc/my.cnf'
__DB_TIMEOUT=1
__DB_SOCKET = '/www/server/mysql/mysql.sock' __DB_SOCKET = '/www/server/mysql/mysql.sock'
__DB_CHARSET = "utf8" __DB_CHARSET = "utf8"
def __Conn(self): def __Conn(self):
# print(self.__DB_HOST, self.__DB_USER)
'''连接数据库''' '''连接数据库'''
try: try:
if os.path.exists(self.__DB_SOCKET): 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: try:
self.__DB_CONN = pymysql.connect(host=self.__DB_HOST, user=self.__DB_USER, passwd=self.__DB_PASS, self.__DB_CONN = pymysql.connect(host=self.__DB_HOST, user=self.__DB_USER, passwd=self.__DB_PASS,
database=self.__DB_NAME, database=self.__DB_NAME,
port=int(self.__DB_PORT), charset=self.__DB_CHARSET, connect_timeout=1, port=int(self.__DB_PORT), charset=self.__DB_CHARSET, connect_timeout=self.__DB_TIMEOUT,
unix_socket=self.__DB_SOCKET, cursorclass=pymysql.cursors.DictCursor) unix_socket=self.__DB_SOCKET, cursorclass=pymysql.cursors.DictCursor)
except Exception as e: except Exception as e:
self.__DB_HOST = '127.0.0.1' self.__DB_HOST = '127.0.0.1'
self.__DB_CONN = pymysql.connect(host=self.__DB_HOST, user=self.__DB_USER, passwd=self.__DB_PASS, self.__DB_CONN = pymysql.connect(host=self.__DB_HOST, user=self.__DB_USER, passwd=self.__DB_PASS,
database=self.__DB_NAME, database=self.__DB_NAME,
port=int(self.__DB_PORT), charset=self.__DB_CHARSET, connect_timeout=1, port=int(self.__DB_PORT), charset=self.__DB_CHARSET, connect_timeout=self.__DB_TIMEOUT,
unix_socket=self.__DB_SOCKET, cursorclass=pymysql.cursors.DictCursor) unix_socket=self.__DB_SOCKET, cursorclass=pymysql.cursors.DictCursor)
else: else:
try: try:
self.__DB_CONN = pymysql.connect(host=self.__DB_HOST, user=self.__DB_USER, passwd=self.__DB_PASS, self.__DB_CONN = pymysql.connect(host=self.__DB_HOST, user=self.__DB_USER, passwd=self.__DB_PASS,
database=self.__DB_NAME, database=self.__DB_NAME,
port=int(self.__DB_PORT), charset=self.__DB_CHARSET, connect_timeout=1, port=int(self.__DB_PORT), charset=self.__DB_CHARSET, connect_timeout=self.__DB_TIMEOUT,
cursorclass=pymysql.cursors.DictCursor) cursorclass=pymysql.cursors.DictCursor)
except Exception as e: except Exception as e:
self.__DB_HOST = '127.0.0.1' self.__DB_HOST = '127.0.0.1'
self.__DB_CONN = pymysql.connect(host=self.__DB_HOST, user=self.__DB_USER, passwd=self.__DB_PASS, self.__DB_CONN = pymysql.connect(host=self.__DB_HOST, user=self.__DB_USER, passwd=self.__DB_PASS,
database=self.__DB_NAME, database=self.__DB_NAME,
port=int(self.__DB_PORT), charset=self.__DB_CHARSET, connect_timeout=1, port=int(self.__DB_PORT), charset=self.__DB_CHARSET, connect_timeout=self.__DB_TIMEOUT,
cursorclass=pymysql.cursors.DictCursor) cursorclass=pymysql.cursors.DictCursor)
self.__DB_CUR = self.__DB_CONN.cursor() self.__DB_CUR = self.__DB_CONN.cursor()
@@ -80,6 +93,10 @@ class ORM:
def getPwd(self): def getPwd(self):
return self.__DB_PASS return self.__DB_PASS
def setTimeout(self, timeout = 1):
self.__DB_TIMEOUT = timeout
return True
def setDbName(self, name): def setDbName(self, name):
self.__DB_NAME = name self.__DB_NAME = name
@@ -95,6 +112,13 @@ class ORM:
except Exception as ex: except Exception as ex:
return ex return ex
def ping(self):
try:
self.__DB_CONN.ping()
except Exception as e:
print(e)
return True
def query(self, sql): def query(self, sql):
# 执行SQL语句返回数据集 # 执行SQL语句返回数据集
if not self.__Conn(): if not self.__Conn():
+9
View File
@@ -64,6 +64,11 @@ mw_start_debug2(){
gunicorn -b :7200 -k geventwebsocket.gunicorn.workers.GeventWebSocketWorker -w 1 app:app gunicorn -b :7200 -k geventwebsocket.gunicorn.workers.GeventWebSocketWorker -w 1 app:app
} }
mw_start_debug3(){
gunicorn -c setting.py app:app
python3 task.py
}
mw_stop() mw_stop()
{ {
@@ -96,4 +101,8 @@ case "$1" in
mw_stop mw_stop
mw_start_debug2 mw_start_debug2
;; ;;
'debug3')
mw_stop
mw_start_debug3
;;
esac esac
+2 -1
View File
@@ -5,7 +5,8 @@
``` ```
/etc/init.d/mw default | 显示登录信息 /etc/init.d/mw default | 显示登录信息
/etc/init.d/mw db | 快捷连接数据库 /etc/init.d/mw db | 快捷连接MySQL
/etc/init.d/mw redis | 快捷连接Redis
---------------------------------------- ----------------------------------------
mw open | 开启面板 mw open | 开启面板
mw close | 关闭面板 mw close | 关闭面板
+1
View File
@@ -116,6 +116,7 @@ def initConf():
"/www/server/php/82/var/log", "/www/server/php/82/var/log",
"/www/server/php/83/var/log", "/www/server/php/83/var/log",
"/www/server/php/84/var/log", "/www/server/php/84/var/log",
"/www/server/openresty/nginx/logs",
"/www/server/phpmyadmin", "/www/server/phpmyadmin",
"/www/server/redis/data", "/www/server/redis/data",
"/www/server/cron", "/www/server/cron",
+10
View File
@@ -5,6 +5,16 @@
white-space: nowrap; white-space: nowrap;
vertical-align: middle; vertical-align: middle;
} }
#select_db xm-select{
min-height: 30px;
line-height: 30px;
}
#select_db xm-select *{
font-size: 12px;
}
</style> </style>
<div class="bt-form"> <div class="bt-form">
+10 -3
View File
@@ -123,9 +123,16 @@
<input name="mongodb_field_value" type="text" class="bt-input-text" placeholder="请输入查找内容"> <input name="mongodb_field_value" type="text" class="bt-input-text" placeholder="请输入查找内容">
</div> </div>
<button type="button" class="mongodb_find btn btn-success btn-sm mr5 ml5" style="float:right;"> <div style="float:right;">
<span>查找</span>
</button> <button type="button" class="mongodb_find btn btn-success btn-sm mr5 ml5" >
<span>查找</span>
</button>
<button type="button" class="mongodb_refresh btn btn-success btn-sm mr5 ml5">
<span>刷新</span>
</button>
</div>
</div> </div>
<div class="pull-right" style="padding-top: 5px;"> <div class="pull-right" style="padding-top: 5px;">
+5 -1
View File
@@ -738,6 +738,10 @@ function mongodbInitField(f, data){
} }
mongodbDataList(1); mongodbDataList(1);
}); });
$('#mongodb .mongodb_refresh').unbind('click').click(function(){
mongodbDataList(1);
});
} }
var mogodb_db_list; var mogodb_db_list;
@@ -1252,7 +1256,7 @@ function redisBatchClear(){
xm_db_list = xmSelect.render({ xm_db_list = xmSelect.render({
el: '#select_db', el: '#select_db',
repeat: true, repeat: false,
toolbar: {show: true}, toolbar: {show: true},
data: idx_db, data: idx_db,
}); });
+349 -7
View File
@@ -1948,7 +1948,7 @@ def addMasterRepSlaveUser(version=''):
if isError != None: if isError != None:
return isError return isError
sql_select = "grant select,lock tables,PROCESS on *.* to " + username + "@'%';" sql_select = "grant select,reload,REPLICATION CLIENT,PROCESS on *.* to " + username + "@'%';"
pdb.execute(sql_select) pdb.execute(sql_select)
pdb.execute('FLUSH PRIVILEGES;') pdb.execute('FLUSH PRIVILEGES;')
@@ -2583,6 +2583,293 @@ def dumpMysqlData(version=''):
return 'ok' return 'ok'
return 'fail' return 'fail'
############### --- 重要 数据补足同步 ---- ###########
def getSyncMysqlDB(dbname,sign = ''):
conn = pSqliteDb('slave_sync_user')
if sign != '':
data = conn.field('ip,port,user,pass,mode,cmd').where('ip=?', (sign,)).find()
else:
data = conn.field('ip,port,user,pass,mode,cmd').find()
user = data['user']
apass = data['pass']
port = data['port']
ip = data['ip']
# 远程数据
sync_db = mw.getMyORM()
# MySQLdb |
sync_db.setPort(port)
sync_db.setHost(ip)
sync_db.setUser(user)
sync_db.setPwd(apass)
sync_db.setDbName(dbname)
sync_db.setTimeout(60)
return sync_db
def syncDatabaseRepairTempFile():
tmp_log = mw.getMWLogs()+ '/mariadb-check.log'
return tmp_log
def syncDatabaseRepairLog(version=''):
import subprocess
args = getArgs()
data = checkArgs(args, ['db','sign','op'])
if not data[0]:
return data[1]
sync_args_db = args['db']
sync_args_sign = args['sign']
op = args['op']
tmp_log = syncDatabaseRepairTempFile()
cmd = 'cd '+mw.getServerDir()+'/mdserver-web && source bin/activate && python3 plugins/mariadb/index.py sync_database_repair {"db":"'+sync_args_db+'","sign":"'+sync_args_sign+'"}'
# print(cmd)
if op == 'get':
log = mw.getLastLine(tmp_log, 15)
return mw.returnJson(True, log)
if op == 'cmd':
return mw.returnJson(True, 'ok', cmd)
if op == 'do':
os.system(' echo "开始执行" > '+ tmp_log)
os.system(cmd +' >> '+ tmp_log +' &')
# time.sleep(10)
# mw.execShell('rm -rf '+tmp_log)
return mw.returnJson(True, 'ok')
return mw.returnJson(False, '无效请求!')
def syncDatabaseRepair(version=''):
time_stats_s = time.time()
tmp_log = syncDatabaseRepairTempFile()
from pymysql.converters import escape_string
args = getArgs()
data = checkArgs(args, ['db','sign'])
if not data[0]:
return data[1]
sync_args_db = args['db']
sync_args_sign = args['sign']
# 本地数据
local_db = pMysqlDb()
# 远程数据
sync_db = getSyncMysqlDB(sync_args_db,sync_args_sign)
tables = local_db.query('show tables from `%s`' % sync_args_db)
table_key = "Tables_in_" + sync_args_db
inconsistent_table = []
tmp_dir = '/tmp/sync_db_repair'
mw.execShell('mkdir -p '+tmp_dir)
for tb in tables:
table_name = sync_args_db+'.'+tb[table_key]
table_check_file = tmp_dir+'/'+table_name+'.txt'
if os.path.exists(table_check_file):
# print(table_name+', 已检查OK')
continue
primary_key_sql = "SHOW INDEX FROM "+table_name+" WHERE Key_name = 'PRIMARY';";
primary_key_data = local_db.query(primary_key_sql)
# print(primary_key_sql,primary_key_data)
pkey_name = '*'
if len(primary_key_data) == 1:
pkey_name = primary_key_data[0]['Column_name']
# print(pkey_name)
if pkey_name != '*' :
# 智能校验(由于服务器同步可能会慢,比较总数总是对不上)
cmd_local_newpk_sql = 'select ' + pkey_name + ' from ' + table_name + " order by " + pkey_name + " desc limit 1"
cmd_local_newpk_data = local_db.query(cmd_local_newpk_sql)
# print(cmd_local_newpk_data)
if len(cmd_local_newpk_data) == 1:
# 比较总数
cmd_count_sql = 'select count('+pkey_name+') as num from '+table_name + ' where '+pkey_name + ' <= '+ str(cmd_local_newpk_data[0][pkey_name])
local_count_data = local_db.query(cmd_count_sql)
sync_count_data = sync_db.query(cmd_count_sql)
if local_count_data != sync_count_data:
print(cmd_count_sql)
print("all data compare: ",local_count_data, sync_count_data)
else:
print(table_name+' smart compare check ok.')
mw.writeFile(tmp_log, table_name+' smart compare check ok.\n','a+')
mw.execShell("echo 'ok' > "+table_check_file)
continue
# 比较总数
cmd_count_sql = 'select count('+pkey_name+') as num from '+table_name
local_count_data = local_db.query(cmd_count_sql)
sync_count_data = sync_db.query(cmd_count_sql)
if local_count_data != sync_count_data:
print("all data compare: ",local_count_data, sync_count_data)
inconsistent_table.append(table_name)
diff = sync_count_data[0]['num'] - local_count_data[0]['num']
print(table_name+', need sync. diff,'+str(diff))
mw.writeFile(tmp_log, table_name+', need sync. diff,'+str(diff)+'\n','a+')
else:
print(table_name+' check ok.')
mw.writeFile(tmp_log, table_name+' check ok.\n','a+')
mw.execShell("echo 'ok' > "+table_check_file)
# inconsistent_table = ['xx.xx']
# 数据对齐
for table_name in inconsistent_table:
is_break = False
while not is_break:
local_db.ping()
# 远程数据
sync_db.ping()
print("check table:"+table_name)
mw.writeFile(tmp_log, "check table:"+table_name+'\n','a+')
table_name_pos = 0
table_name_pos_file = tmp_dir+'/'+table_name+'.pos.txt'
primary_key_sql = "SHOW INDEX FROM "+table_name+" WHERE Key_name = 'PRIMARY';";
primary_key_data = local_db.query(primary_key_sql)
pkey_name = primary_key_data[0]['Column_name']
if os.path.exists(table_name_pos_file):
table_name_pos = mw.readFile(table_name_pos_file)
data_select_sql = 'select * from '+table_name + ' where '+pkey_name+' > '+str(table_name_pos)+' limit 10000'
print(data_select_sql)
local_select_data = local_db.query(data_select_sql)
time_s = time.time()
sync_select_data = sync_db.query(data_select_sql)
print(f'sync query cos:{time.time() - time_s:.4f}s')
mw.writeFile(tmp_log, f'sync query cos:{time.time() - time_s:.4f}s\n','a+')
# print(local_select_data)
# print(sync_select_data)
# print(len(local_select_data))
# print(len(sync_select_data))
print('pos:',str(table_name_pos),'local compare sync,',local_select_data == sync_select_data)
cmd_count_sql = 'select count('+pkey_name+') as num from '+table_name
local_count_data = local_db.query(cmd_count_sql)
time_s = time.time()
sync_count_data = sync_db.query(cmd_count_sql)
print(f'sync count data cos:{time.time() - time_s:.4f}s')
print(local_count_data,sync_count_data)
# 数据同步有延迟,相等即任务数据补足完成
if local_count_data[0]['num'] == sync_count_data[0]['num']:
is_break = True
break
diff = sync_count_data[0]['num'] - local_count_data[0]['num']
print("diff," + str(diff)+' line data!')
if local_select_data == sync_select_data:
data_count = len(local_select_data)
if data_count == 0:
# mw.writeFile(table_name_pos_file, '0')
print(table_name+",data is equal ok..")
is_break = True
break
# print(table_name,data_count)
pos = local_select_data[data_count-1][pkey_name]
print('pos',pos)
progress = pos/sync_count_data[0]['num']
print('progress,%.2f' % progress+'%')
mw.writeFile(table_name_pos_file, str(pos))
else:
sync_select_data_len = len(sync_select_data)
skip_idx = 0
# 主库PK -> 查询本地 | 保证一致
if sync_select_data_len > 0:
for idx in range(sync_select_data_len):
sync_idx_data = sync_select_data[idx]
local_idx_data = None
if idx in local_select_data:
local_idx_data = local_select_data[idx]
if sync_select_data[idx] == local_idx_data:
skip_idx = idx
pos = local_select_data[idx][pkey_name]
mw.writeFile(table_name_pos_file, str(pos))
# print(insert_data)
local_inquery_sql = 'select * from ' + table_name+ ' where ' +pkey_name+' = '+ str(sync_idx_data[pkey_name])
# print(local_inquery_sql)
ldata = local_db.query(local_inquery_sql)
# print('ldata:',ldata)
if len(ldata) == 0:
print("id:"+ str(sync_idx_data[pkey_name])+ " no exists, insert")
insert_sql = 'insert into ' + table_name
field_str = ''
value_str = ''
for field in sync_idx_data:
field_str += '`'+field+'`,'
value_str += '\''+escape_string(str(sync_idx_data[field]))+'\','
field_str = '(' +field_str.strip(',')+')'
value_str = '(' +value_str.strip(',')+')'
insert_sql = insert_sql+' '+field_str+' values'+value_str+';'
print(insert_sql)
r = local_db.execute(insert_sql)
print(r)
else:
# print('compare sync->local:',sync_idx_data == ldata[0] )
if ldata[0] == sync_idx_data:
continue
print("id:"+ str(sync_idx_data[pkey_name])+ " data is not equal, update")
update_sql = 'update ' + table_name
field_str = ''
value_str = ''
for field in sync_idx_data:
if field == pkey_name:
continue
field_str += '`'+field+'`=\''+escape_string(str(sync_idx_data[field]))+'\','
field_str = field_str.strip(',')
update_sql = update_sql+' set '+field_str+' where '+pkey_name+'=\''+str(sync_idx_data[pkey_name])+'\';'
print(update_sql)
r = local_db.execute(update_sql)
print(r)
# 本地PK -> 查询主库 | 保证一致
# local_select_data_len = len(local_select_data)
# if local_select_data_len > 0:
# for idx in range(local_select_data_len):
# if idx < skip_idx:
# continue
# local_idx_data = local_select_data[idx]
# print('local idx check', idx, skip_idx)
# local_inquery_sql = 'select * from ' + table_name+ ' where ' +pkey_name+' = '+ str(local_idx_data[pkey_name])
# print(local_inquery_sql)
# sdata = sync_db.query(local_inquery_sql)
# sdata_len = len(sdata)
# print('sdata:',sdata,sdata_len)
# if sdata_len == 0:
# delete_sql = 'delete from ' + table_name + ' where ' +pkey_name+' = '+ str(local_idx_data[pkey_name])
# print(delete_sql)
# r = local_db.execute(delete_sql)
# print(r)
# break
if is_break:
print("break all")
break
time.sleep(3)
print(f'data check cos:{time.time() - time_stats_s:.4f}s')
print("data supplementation completed")
mw.execShell('rm -rf '+tmp_dir)
return 'ok'
############### --- 重要 同步---- ########### ############### --- 重要 同步---- ###########
@@ -2647,11 +2934,18 @@ def doFullSync(version=''):
def doFullSyncUser(version=''): def doFullSyncUser(version=''):
which_pv = mw.execShell('which pv')
is_exist_pv = False
if not os.path.exists(which_pv[0]):
is_exist_pv = True
args = getArgs() args = getArgs()
data = checkArgs(args, ['db', 'sign']) data = checkArgs(args, ['db', 'sign'])
if not data[0]: if not data[0]:
return data[1] return data[1]
time_all_s = time.time()
sync_db = args['db'] sync_db = args['db']
sync_sign = args['sign'] sync_sign = args['sign']
@@ -2675,23 +2969,67 @@ def doFullSyncUser(version=''):
os.system("rm -rf " + bak_file) os.system("rm -rf " + bak_file)
writeDbSyncStatus({'code': 0, 'msg': '开始同步...', 'progress': 0}) writeDbSyncStatus({'code': 0, 'msg': '开始同步...', 'progress': 0})
dmp_option = ''
mode = recognizeDbMode()
# if mode == 'gtid':
# dmp_option = ' --set-gtid-purged=off '
writeDbSyncStatus({'code': 1, 'msg': '远程导出数据...', 'progress': 10}) writeDbSyncStatus({'code': 1, 'msg': '远程导出数据...', 'progress': 10})
find_run_dump = mw.execShell('ps -ef | grep mariadb-dump | grep -v grep')
if find_run_dump[0] != "":
print("正在远程导出数据中,别着急...")
writeDbSyncStatus({'code': 3.1, 'msg': '正在远程导出数据中,别着急...', 'progress': 39})
return False
time_s = time.time()
if not os.path.exists(bak_file): if not os.path.exists(bak_file):
dmp_option += " --master-data=1 --apply-slave-statements --include-master-host-port "
# https://mariadb.com/kb/zh-cn/mariadb-dump/ # https://mariadb.com/kb/zh-cn/mariadb-dump/
dump_sql_data = getServerDir() + "/bin/mariadb-dump -f --opt --default-character-set=utf8 --single-transaction -h" + ip + " -P" + \ dump_sql_data = getServerDir() + "/bin/mariadb-dump " + dmp_option + " -f --default-character-set=utf8 --single-transaction --compress -q -h" + ip + " -P" + \
port + " -u" + user + " -p'" + apass + "' " + sync_db + ">" + bak_file port + " -u" + user + " -p'" + apass + "' " + sync_db + ">" + bak_file
print(dump_sql_data) print(dump_sql_data)
mw.execShell(dump_sql_data) mw.execShell(dump_sql_data)
writeDbSyncStatus({'code': 2, 'msg': '本地导入数据...', 'progress': 40}) time_e = time.time()
export_cos = time_e - time_s
print("export cos:", export_cos)
writeDbSyncStatus({'code': 3, 'msg': '导出耗时:'+str(int(export_cos))+'秒,正在到本地导入数据中...', 'progress': 40})
find_run_import = mw.execShell('ps -ef | grep mariadb| grep '+ bak_file +' | grep -v grep')
if find_run_import[0] != "":
print("正在导入数据中,别着急...")
writeDbSyncStatus({'code': 4.1, 'msg': '正在导入数据中,别着急...', 'progress': 59})
return False
# if os.path.exists(bak_file):
# db.execute('reset master')
time_s = time.time()
if os.path.exists(bak_file): if os.path.exists(bak_file):
pwd = pSqliteDb('config').where('id=?', (1,)).getField('mysql_root') pwd = pSqliteDb('config').where('id=?', (1,)).getField('mysql_root')
sock = getSocketFile() sock = getSocketFile()
my_import_cmd = getServerDir() + '/bin/mariadb -S ' + sock + " -uroot -p'" + pwd + \
"' " + sync_db + '<' + bak_file if is_exist_pv:
print(my_import_cmd) my_import_cmd = getServerDir() + '/bin/mariadb -S ' + sock + " -uroot -p'" + pwd + "' " + sync_db
mw.execShell(my_import_cmd) my_import_cmd = "pv -t -p " + bak_file + '|' + my_import_cmd
print(my_import_cmd)
os.system(my_import_cmd)
else:
my_import_cmd = getServerDir() + '/bin/mariadb -S ' + sock + " -uroot -p'" + pwd + \
"' " + sync_db + '<' + bak_file
print(my_import_cmd)
mw.execShell(my_import_cmd)
time_e = time.time()
import_cos = time_e - time_s
print("import cos:", import_cos)
writeDbSyncStatus({'code': 4, 'msg': '导入耗时:'+str(int(import_cos))+'秒', 'progress': 60})
time.sleep(3)
pinfo = parseSlaveSyncCmd(data['cmd']) pinfo = parseSlaveSyncCmd(data['cmd'])
# print(pinfo) # print(pinfo)
@@ -3026,5 +3364,9 @@ if __name__ == "__main__":
print(doFullSync(version)) print(doFullSync(version))
elif func == 'dump_mysql_data': elif func == 'dump_mysql_data':
print(dumpMysqlData(version)) print(dumpMysqlData(version))
elif func == 'sync_database_repair':
print(syncDatabaseRepair())
elif func == 'sync_database_repair_log':
print(syncDatabaseRepairLog())
else: else:
print('error') print('error')
+6
View File
@@ -7,6 +7,12 @@ rootPath=$(dirname "$curPath")
rootPath=$(dirname "$rootPath") rootPath=$(dirname "$rootPath")
serverPath=$(dirname "$rootPath") serverPath=$(dirname "$rootPath")
# cd /www/server/mdserver-web/plugins/mariadb && bash install.sh install 8.2
# cd /www/server/mdserver-web && source bin/activate && python3 plugins/mariadb/index.py try_slave_sync_bugfix {}
# cd /www/server/mdserver-web && source bin/activate && python3 plugins/mariadb/index.py do_full_sync {"db":"xxx","sign":"","begin":1}
# cd /www/server/mdserver-web && source bin/activate && python3 plugins/mariadb/index.py sync_database_repair {"db":"xxx","sign":""}
# cd /www/server/mdserver-web && source bin/activate && python3 plugins/mariadb/index.py init_slave_status
# cd /www/server/mdserver-web && source bin/activate && python3 plugins/mariadb/index.py install_pre_inspection
install_tmp=${rootPath}/tmp/mw_install.pl install_tmp=${rootPath}/tmp/mw_install.pl
+81 -2
View File
@@ -874,7 +874,7 @@ function openPhpmyadmin(name,username,password){
} }
if (rdata.data['cfg']['choose'] != 'mariadb'){ if (rdata.data['cfg']['choose'] != 'mariadb'){
layer.msg('当前为['+rdata.choose+']模式,若要使用请修改phpMyAdmin访问切换.',{icon:2,shade: [0.3, '#000']}); layer.msg('当前为['+rdata.data['cfg']['choose']+']模式,若要使用请修改phpMyAdmin访问切换.',{icon:2,shade: [0.3, '#000']});
return; return;
} }
var home_page = rdata.data['home_page']; var home_page = rdata.data['home_page'];
@@ -1978,6 +1978,84 @@ function getFullSyncStatus(db){
} }
function dataSyncVerify(db){
var reqTimer = null;
function requestLogs(layerIndex){
myPostN('sync_database_repair_log', {db:db, sign:'',op:'get'}, function(rdata){
var rdata = $.parseJSON(rdata.data);
if(!rdata.status) {
layer.close(layerIndex);
layer.msg(rdata.msg,{icon:2, time:2000});
clearInterval(reqTimer);
return;
};
if (rdata.msg == ''){
rdata.msg = '暂无数据!';
}
$("#data_verify_log").html(rdata.msg);
//滚动到最低
var ob = document.getElementById('data_verify_log');
ob.scrollTop = ob.scrollHeight;
});
}
layer.open({
type: 1,
title: '同步数据库['+db+']数据校验',
area: '500px',
btn:[ "开始","取消","手动"],
content:"<div class='bt-form'>\
"+'<pre id="data_verify_log" style="overflow: auto; border: 0px none; line-height:23px;padding: 5px; margin: 0px; white-space: pre-wrap; height: 395px; background-color: rgb(51,51,51);color:#f1f1f1;border-radius:0px;font-family:"></pre>'+"\
</div>",
cancel: function(){
if (reqTimer){
clearInterval(reqTimer);
}
},
yes:function(index,layer_index){
myPostN('sync_database_repair_log', {db:db, sign:'',op:'do'}, function(data){});
layer.msg("执行成功");
requestLogs(layer_index);
reqTimer = setInterval(function(){
requestLogs(layer_index);
},3000);
},
success:function(){
},
btn3: function(){
myPostN('sync_database_repair_log', {db:db, sign:'',op:'cmd'}, function(rdata){
var rdata = $.parseJSON(rdata.data);
layer.open({
title: "手动执行命令CMD",
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);
}
});
});
return false;
}
});
}
function addSlaveSSH(ip=''){ function addSlaveSSH(ip=''){
myPost('get_slave_ssh_by_ip', {ip:ip}, function(rdata){ myPost('get_slave_ssh_by_ip', {ip:ip}, function(rdata){
@@ -2633,7 +2711,8 @@ function masterOrSlaveConf(version=''){
list += '<td>' + rdata.data[i]['name'] +'</td>'; list += '<td>' + rdata.data[i]['name'] +'</td>';
list += '<td style="text-align:right">' + list += '<td style="text-align:right">' +
'<a href="javascript:;" class="btlink" onclick="setDbSlave(\''+rdata.data[i]['name']+'\')" title="加入|退出">'+(rdata.data[i]['slave']?'退出':'加入')+'</a> | ' + '<a href="javascript:;" class="btlink" onclick="setDbSlave(\''+rdata.data[i]['name']+'\')" title="加入|退出">'+(rdata.data[i]['slave']?'退出':'加入')+'</a> | ' +
'<a href="javascript:;" class="btlink" onclick="getFullSyncStatus(\''+rdata.data[i]['name']+'\')" title="同步">同步</a>' + '<a href="javascript:;" class="btlink" onclick="getFullSyncStatus(\''+rdata.data[i]['name']+'\')" title="同步">同步</a> | ' +
'<a href="javascript:;" class="btlink" onclick="dataSyncVerify(\''+rdata.data[i]['name']+'\')" title="数据校验">数据校验</a>' +
'</td>'; '</td>';
list += '</tr>'; list += '</tr>';
} }
+1 -1
View File
@@ -65,7 +65,7 @@ class backupTools:
# mw.execShell(db_path + "/bin/mysqldump --defaults-file=" + my_conf_path + " --skip-lock-tables --default-character-set=utf8 " + # mw.execShell(db_path + "/bin/mysqldump --defaults-file=" + my_conf_path + " --skip-lock-tables --default-character-set=utf8 " +
# name + " | gzip > " + filename) # name + " | gzip > " + filename)
cmd = db_path + "/bin/mysqldump --defaults-file=" + my_conf_path + " --single-transaction --quick --default-character-set=utf8 " + \ cmd = db_path + "/bin/mariadb-dump --defaults-file=" + my_conf_path + " --single-transaction --quick --default-character-set=utf8 " + \
name + " | gzip > " + filename name + " | gzip > " + filename
mw.execShell(cmd) mw.execShell(cmd)
+1
View File
@@ -63,6 +63,7 @@
<p onclick="mongoStatus();">负载状态</p> <p onclick="mongoStatus();">负载状态</p>
<p onclick="mongoReplStatus();">复制状态</p> <p onclick="mongoReplStatus();">复制状态</p>
<p onclick="pluginLogs('mongodb','','run_log');">日志</p> <p onclick="pluginLogs('mongodb','','run_log');">日志</p>
<p onclick="mgdbReadme();">相关说明</p>
</div> </div>
<div class="bt-w-con pd15"> <div class="bt-w-con pd15">
<div class="soft-man-con" style="height:455px;overflow: auto;"></div> <div class="soft-man-con" style="height:455px;overflow: auto;"></div>
+24 -12
View File
@@ -18,7 +18,6 @@ if mw.isAppleSystem():
# /usr/lib/systemd/system/mongod.service # /usr/lib/systemd/system/mongod.service
# /var/lib/mongo
# python3 /www/server/mdserver-web/plugins/mongodb/index.py repl_init # python3 /www/server/mdserver-web/plugins/mongodb/index.py repl_init
# python3 /www/server/mdserver-web/plugins/mongodb/index.py run_repl_info # python3 /www/server/mdserver-web/plugins/mongodb/index.py run_repl_info
@@ -107,6 +106,9 @@ def getConfIp():
data = getConfigData() data = getConfigData()
return data['net']['bindIp'] return data['net']['bindIp']
def getConfLocalIp():
return '127.0.0.1'
def getConfPort(): def getConfPort():
data = getConfigData() data = getConfigData()
return data['net']['port'] return data['net']['port']
@@ -186,7 +188,7 @@ def mongdbClientS():
import pymongo import pymongo
port = getConfPort() port = getConfPort()
auth = getConfAuth() auth = getConfAuth()
ip = getConfIp() ip = getConfLocalIp()
mg_root = pSqliteDb('config').where('id=?', (1,)).getField('mg_root') mg_root = pSqliteDb('config').where('id=?', (1,)).getField('mg_root')
if auth == 'disabled': if auth == 'disabled':
@@ -200,7 +202,7 @@ def mongdbClient():
import pymongo import pymongo
port = getConfPort() port = getConfPort()
auth = getConfAuth() auth = getConfAuth()
ip = getConfIp() ip = getConfLocalIp()
mg_root = pSqliteDb('config').where('id=?', (1,)).getField('mg_root') mg_root = pSqliteDb('config').where('id=?', (1,)).getField('mg_root')
# print(ip,port,auth,mg_root) # print(ip,port,auth,mg_root)
if auth == 'disabled': if auth == 'disabled':
@@ -488,14 +490,13 @@ def runDocInfo():
def runReplInfo(): def runReplInfo():
client = mongdbClient() client = mongdbClient()
db = client.admin db = client.admin
result = {}
try: try:
serverStatus = db.command('serverStatus') serverStatus = db.command('serverStatus')
except Exception as e: except Exception as e:
return mw.returnJson(False, str(e)) return mw.returnJson(False, str(e))
d = getConfigData() d = getConfigData()
result = {}
if 'replication' in d and 'replSetName' in d['replication']: if 'replication' in d and 'replSetName' in d['replication']:
result['repl_name'] = d['replication']['replSetName'] result['repl_name'] = d['replication']['replSetName']
@@ -518,7 +519,22 @@ def runReplInfo():
hosts = mw.getDefault(repl,'hosts', '') hosts = mw.getDefault(repl,'hosts', '')
result['hosts'] = ','.join(hosts) result['hosts'] = ','.join(hosts)
result['members'] = []
try:
members_list = []
replStatus = db.command('replSetGetStatus')
if 'members' in replStatus:
members = replStatus['members']
for m in members:
t = {}
t['name'] = m['name']
t['stateStr'] = m['stateStr']
t['uptime'] = m['uptime']
members_list.append(t)
result['members'] = members_list
except Exception as e:
pass
return mw.returnJson(True, 'OK', result) return mw.returnJson(True, 'OK', result)
def getDbList(): def getDbList():
@@ -602,7 +618,7 @@ def addDb():
username = data_name username = data_name
client[data_name].chat.insert_one({}) client[data_name].zchat.insert_one({})
user_roles = [{'role': 'dbOwner', 'db': data_name}, {'role': 'userAdmin', 'db': data_name}] user_roles = [{'role': 'dbOwner', 'db': data_name}, {'role': 'userAdmin', 'db': data_name}]
if auth_status: if auth_status:
# db.command("dropUser", username) # db.command("dropUser", username)
@@ -787,7 +803,7 @@ def toDbBase(find):
data_name = find['name'] data_name = find['name']
db = client[data_name] db = client[data_name]
db.chat.insert_one({}) db.zchat.insert_one({})
user_roles = [{'role': 'dbOwner', 'db': data_name}, {'role': 'userAdmin', 'db': data_name}] user_roles = [{'role': 'dbOwner', 'db': data_name}, {'role': 'userAdmin', 'db': data_name}]
try: try:
db_admin.command("createUser", find['username'], pwd=find['password'], roles=user_roles) db_admin.command("createUser", find['username'], pwd=find['password'], roles=user_roles)
@@ -1011,10 +1027,6 @@ def replSetNode():
add_node = args['node'].strip() add_node = args['node'].strip()
idx = int(args['idx']) idx = int(args['idx'])
priority = -1 priority = -1
if 'priority' in args: if 'priority' in args:
priority = args['priority'].strip() priority = args['priority'].strip()
+78 -59
View File
@@ -159,6 +159,12 @@ function mongoReplStatus() {
<tr><th>me</th><td>' + rdata.me + '</td><td>本机</td></tr>'; <tr><th>me</th><td>' + rdata.me + '</td><td>本机</td></tr>';
} }
var tbody_members = '';
var member_list = rdata['members'];
for (var i = 0; i < member_list.length; i++) {
tbody_members += '<tr><th>'+member_list[i]['name']+'</th><td>' + member_list[i]['stateStr'] + '</td><td>'+member_list[i]['uptime']+'</td></tr>';
}
// console.log(rdata); // console.log(rdata);
var repl_on = 'btn-danger'; var repl_on = 'btn-danger';
var repl_on_title = '未开启'; var repl_on_title = '未开启';
@@ -176,9 +182,14 @@ function mongoReplStatus() {
con += '<div class="divtable">\ con += '<div class="divtable">\
<table class="table table-hover table-bordered" style="width: 660px;">\ <table class="table table-hover table-bordered" style="width: 660px;">\
<thead><th>字段</th><th>当前值</th><th>说明</th></thead>\ <thead><th>字段</th><th>当前值</th><th>说明</th></thead>\
<tbody>\ <tbody>'+tbody+'<tbody>\
'+tbody+'\ </table>\
<tbody>\ </div>';
con += '<div class="divtable" style="margin-top:5px;">\
<table class="table table-hover table-bordered" style="width: 660px;">\
<thead><th>IP</th><th>状态</th><th>在线</th></thead>\
<tbody>'+tbody_members+'<tbody>\
</table>\ </table>\
</div>'; </div>';
@@ -188,67 +199,66 @@ function mongoReplStatus() {
//设置副本名称 //设置副本名称
function mongoReplCfgReplSetName(){ function mongoReplCfgReplSetName(){
mgPost('run_doc_info', '', '', function(rdata){ // <select class='bt-input-text mr5' name='replSetName' style='width:100px'><option value=''></option></select>
var rdata = $.parseJSON(rdata.data); layer.open({
type: 1,
area: '300px',
title: '设置副本名称',
closeBtn: 1,
shift: 5,
shadeClose: false,
btn:["提交","关闭"],
content: "<form class='bt-form pd20' id='mod_pwd'>\
<div class='line'>\
<span class='tname'>同步副本名称</span>\
<div class='info-r'>\
<input class='bt-input-text mr5' name='replSetName' style='width:100px' />\
</div>\
</div>\
</form>",
layer.open({ success: function(){
type: 1, // // console.log(rdata);
area: '300px', // var rlist = rdata['dbs'];
title: '设置副本名称', // var dbs = [];
closeBtn: 1, // var selectHtml = '';
shift: 5, // for (var i = 0; i < rlist.length; i++) {
shadeClose: false, // // console.log(rlist[i]['db']);
btn:["提交","关闭"], // var dbname = rlist[i]['db'];
content: "<form class='bt-form pd20' id='mod_pwd'>\
<div class='line'>\
<span class='tname'>同步副本</span>\
<div class='info-r'>\
<select class='bt-input-text mr5' name='replSetName' style='width:100px'>\
<option value=''></option>\
</select>\
</div>\
</div>\
</form>",
success: function(){ // if (['admin','local','config'].includes(dbname)){
// console.log(rdata); // } else {
var rlist = rdata['dbs']; // dbs.push(dbname);
var dbs = []; // }
var selectHtml = ''; // }
for (var i = 0; i < rlist.length; i++) {
// console.log(rlist[i]['db']);
var dbname = rlist[i]['db'];
if (['admin','local','config'].includes(dbname)){ // if (dbs.length == 0 ){
} else { // selectHtml += "<option value=''>无</option>";
dbs.push(dbname); // }
}
}
if (dbs.length == 0 ){ // for (index in dbs) {
selectHtml += "<option value=''>无</option>"; // selectHtml += "<option value='"+dbs[index]+"'>"+dbs[index]+"</option>";
} // }
for (index in dbs) { // $('select[name="replSetName"]').html(selectHtml);
selectHtml += "<option value='"+dbs[index]+"'>"+dbs[index]+"</option>"; },
} yes:function(index){
var data = {};
$('select[name="replSetName"]').html(selectHtml); data['name'] = $('input[name=replSetName]').val();
}, if (data['name'] == ''){
yes:function(index){ layer.msg("副本名称不能为空");
var data = {}; return;
data['name'] = $('select[name=replSetName]').val(); }
mgPost('repl_set_name', '',data, function(data){ mgPost('repl_set_name', '',data, function(data){
var rdata = $.parseJSON(data.data); var rdata = $.parseJSON(data.data);
showMsg(rdata.msg,function(){ showMsg(rdata.msg,function(){
if (rdata['status']){ if (rdata['status']){
layer.close(index); layer.close(index);
mongoReplCfgInit(); mongoReplCfgInit();
} }
},{icon: rdata.status ? 1 : 2}); },{icon: rdata.status ? 1 : 2});
}); });
} }
});
}); });
} }
@@ -1193,3 +1203,12 @@ function importDbExternal(file,name){
}); });
} }
function mgdbReadme(){
var readme = '<ul class="help-info-text c7">';
readme += '<li>认证同步说明</li>';
readme += '<li>root/用户,配置Key完全一致才能同步。</li>';
readme += '</ul>';
$('.soft-man-con').html(readme);
}
+1 -1
View File
@@ -63,7 +63,7 @@ class backupTools:
if len(mycnf) > 100: if len(mycnf) > 100:
mw.writeFile(db_path + '/etc/my.cnf', mycnf) mw.writeFile(db_path + '/etc/my.cnf', mycnf)
cmd = db_path + "/bin/usr/bin/mysqldump --defaults-file=" + my_conf_path + " --single-transaction --quick --default-character-set=utf8 " + \ cmd = db_path + "/bin/usr/bin/mysqldump --defaults-file=" + my_conf_path + " --single-transaction -q --default-character-set=utf8 " + \
name + " | gzip > " + filename name + " | gzip > " + filename
mw.execShell(cmd) mw.execShell(cmd)
+1 -1
View File
@@ -65,7 +65,7 @@ class backupTools:
if len(content) > 100: if len(content) > 100:
mw.writeFile(my_cnf, content) mw.writeFile(my_cnf, content)
cmd = db_path + "/bin/usr/bin/mysqldump --defaults-file=" + my_cnf + " --single-transaction --quick --default-character-set=utf8 " + \ cmd = db_path + "/bin/usr/bin/mysqldump --defaults-file=" + my_cnf + " --single-transaction -q --default-character-set=utf8 " + \
name + " | gzip > " + filename name + " | gzip > " + filename
mw.execShell(cmd) mw.execShell(cmd)
+27
View File
@@ -0,0 +1,27 @@
```
show global variables like '%gtid%';
show global variables like 'server_uuid';
```
```
# 不锁表,需要删除原来数据表
# tables = db.query('show tables from `%s`' % sync_db_import)
# table_key = "Tables_in_" + sync_db_import
# for tname in tables:
# drop_db_cmd = 'drop table if exists '+sync_db_import+'.'+tname[table_key]
# # print(drop_db_cmd)
# db.query(drop_db_cmd)
```
```
# 修改同步位置
# master_info = sync_mdb.query('show master status')
# slave_info = db.query('show slave status')
# if len(master_info)>0:
# channel_name = slave_info[0]['Channel_Name']
# change_cmd = "CHANGE MASTER TO MASTER_LOG_FILE='"+master_info[0]['File']+"', MASTER_LOG_POS="+str(master_info[0]['Position'])+" for channel '"+channel_name+"';"
# print(change_cmd)
# r = db.execute(change_cmd)
# print(r)
```
+2
View File
@@ -50,7 +50,9 @@ CREATE TABLE IF NOT EXISTS `slave_sync_user` (
`pass` TEXT, `pass` TEXT,
`mode` TEXT, `mode` TEXT,
`cmd` TEXT, `cmd` TEXT,
`db` TEXT,
`addtime` TEXT `addtime` TEXT
); );
ALTER TABLE `slave_sync_user` ADD COLUMN `db` TEXT DEFAULT '';
+501 -56
View File
@@ -63,12 +63,12 @@ def getArgs():
if t.strip() == '': if t.strip() == '':
tmp = [] tmp = []
else: else:
t = t.split(':', 1) t = t.split(':',1)
tmp[t[0]] = t[1] tmp[t[0]] = t[1]
tmp[t[0]] = t[1] tmp[t[0]] = t[1]
elif args_len > 1: elif args_len > 1:
for i in range(len(args)): for i in range(len(args)):
t = args[i].split(':', 1) t = args[i].split(':',1)
tmp[t[0]] = t[1] tmp[t[0]] = t[1]
return tmp return tmp
@@ -745,7 +745,7 @@ def getMyDbPos():
return tmp.groups()[0].strip() return tmp.groups()[0].strip()
def setMyDbPos(): def setMyDbPos(version):
args = getArgs() args = getArgs()
data = checkArgs(args, ['datadir']) data = checkArgs(args, ['datadir'])
if not data[0]: if not data[0]:
@@ -760,7 +760,7 @@ def setMyDbPos():
mw.execShell('mkdir -p ' + t_datadir) mw.execShell('mkdir -p ' + t_datadir)
# mw.execShell('/etc/init.d/mysqld stop') # mw.execShell('/etc/init.d/mysqld stop')
stop() stop(version)
mw.execShell('cp -rf ' + s_datadir + '/* ' + t_datadir + '/') mw.execShell('cp -rf ' + s_datadir + '/* ' + t_datadir + '/')
mw.execShell('chown -R mysql mysql ' + t_datadir) mw.execShell('chown -R mysql mysql ' + t_datadir)
mw.execShell('chmod -R 755 ' + t_datadir) mw.execShell('chmod -R 755 ' + t_datadir)
@@ -774,7 +774,7 @@ def setMyDbPos():
mycnf = mycnf.replace(s_datadir, t_datadir) mycnf = mycnf.replace(s_datadir, t_datadir)
mw.writeFile(myfile, mycnf) mw.writeFile(myfile, mycnf)
start() start(version)
result = mw.execShell( result = mw.execShell(
'ps aux|grep mysqld| grep -v grep|grep -v python') 'ps aux|grep mysqld| grep -v grep|grep -v python')
@@ -784,7 +784,7 @@ def setMyDbPos():
else: else:
mw.execShell('pkill -9 mysqld') mw.execShell('pkill -9 mysqld')
mw.writeFile(myfile, mw.readFile(path + '/etc/my_backup.cnf')) mw.writeFile(myfile, mw.readFile(path + '/etc/my_backup.cnf'))
start() start(version)
return mw.returnJson(False, '文件迁移失败!') return mw.returnJson(False, '文件迁移失败!')
@@ -2349,7 +2349,7 @@ def addMasterRepSlaveUser(version=''):
if isError != None: if isError != None:
return isError return isError
sql_select = "grant select,lock tables,PROCESS on *.* to " + username + "@'%';" sql_select = "grant select,reload,REPLICATION CLIENT,PROCESS on *.* to " + username + "@'%';"
pdb.execute(sql_select) pdb.execute(sql_select)
pdb.execute('FLUSH PRIVILEGES;') pdb.execute('FLUSH PRIVILEGES;')
@@ -2394,29 +2394,31 @@ def getMasterRepSlaveUserCmd(version):
if sid != '': if sid != '':
channel_name = " for channel 'r{}';".format(sid) channel_name = " for channel 'r{}';".format(sid)
if mode == "gtid": mdb8 = ['8.0','8.1','8.2','8.3','8.4']
sql = "CHANGE MASTER TO MASTER_HOST='" + ip + "', MASTER_PORT=" + port + ", MASTER_USER='" + \ sql = ''
clist[0]['username'] + "', MASTER_PASSWORD='" + \ if not mw.inArray(mdb8,version):
clist[0]['password'] + "', MASTER_AUTO_POSITION=1" + channel_name base_sql = "CHANGE MASTER TO MASTER_HOST='" + ip + "', MASTER_PORT=" + port + ", MASTER_USER='" + \
if version == '8.0': clist[0]['username'] + "', MASTER_PASSWORD='" + \
sql = "CHANGE REPLICATION SOURCE TO SOURCE_HOST='" + ip + "', SOURCE_PORT=" + port + ", SOURCE_USER='" + \ clist[0]['password'] + "'"
clist[0]['username'] + "', SOURCE_PASSWORD='" + \
clist[0]['password'] + \
"', MASTER_AUTO_POSITION=1" + channel_name
else:
sql = "CHANGE MASTER TO MASTER_HOST='" + ip + "', MASTER_PORT=" + port + ", MASTER_USER='" + \
clist[0]['username'] + "', MASTER_PASSWORD='" + \
clist[0]['password'] + \
"', MASTER_LOG_FILE='" + mstatus[0]["File"] + \
"',MASTER_LOG_POS=" + str(mstatus[0]["Position"]) + channel_name
if version == "8.0": sql += base_sql +';'
sql = "CHANGE REPLICATION SOURCE TO SOURCE_HOST='" + ip + "', SOURCE_PORT=" + port + ", SOURCE_USER='" + \ sql += "<br/><hr/>"
# sql += base_sql + ", MASTER_AUTO_POSITION=1" + channel_name
sql += base_sql + channel_name
sql += "<br/><hr/>"
sql += base_sql + "', MASTER_LOG_FILE='" + mstatus[0]["File"] + "',MASTER_LOG_POS=" + str(mstatus[0]["Position"]) + channel_name
else:
base_sql = "CHANGE REPLICATION SOURCE TO SOURCE_HOST='" + ip + "', SOURCE_PORT=" + port + ", SOURCE_USER='" + \
clist[0]['username'] + "', SOURCE_PASSWORD='" + \ clist[0]['username'] + "', SOURCE_PASSWORD='" + \
clist[0]['password'] + \ clist[0]['password']+"'"
"', SOURCE_LOG_FILE='" + mstatus[0]["File"] + \ sql += base_sql +';'
"',SOURCE_LOG_POS=" + \ sql += "<br/><hr/>"
str(mstatus[0]["Position"]) + channel_name # sql += base_sql + ", MASTER_AUTO_POSITION=1" + channel_name
sql += base_sql + channel_name
sql += "<br/><hr/>"
sql += base_sql + "', SOURCE_LOG_FILE='" + mstatus[0]["File"] + "',SOURCE_LOG_POS=" + str(mstatus[0]["Position"]) + channel_name
data = {} data = {}
data['cmd'] = sql data['cmd'] = sql
@@ -2769,6 +2771,7 @@ def initSlaveStatus(version=''):
return mw.returnJson(False, 'MySQL未启动', []) return mw.returnJson(False, 'MySQL未启动', [])
mode_file = getSyncModeFile() mode_file = getSyncModeFile()
# print(mode_file)
if not os.path.exists(mode_file): if not os.path.exists(mode_file):
return mw.returnJson(False, '需要先设置同步配置') return mw.returnJson(False, '需要先设置同步配置')
@@ -2842,7 +2845,7 @@ def initSlaveStatusSyncUser(version=''):
if slave_t['mode'] == '1': if slave_t['mode'] == '1':
mode_name = 'gtid' mode_name = 'gtid'
# print(local_mode, mode_name)
if local_mode != mode_name: if local_mode != mode_name:
msg += base_t + '->同步模式不一致' msg += base_t + '->同步模式不一致'
continue continue
@@ -2856,8 +2859,9 @@ def initSlaveStatusSyncUser(version=''):
pinfo = parseSlaveSyncCmd(cmd_sql) pinfo = parseSlaveSyncCmd(cmd_sql)
except Exception as e: except Exception as e:
return mw.returnJson(False, base_t + '->CMD同步命令不合规范!') return mw.returnJson(False, base_t + '->CMD同步命令不合规范!')
# print(u['cmd']) # print(cmd_sql)
t = pdb.query(cmd_sql) t = pdb.query(cmd_sql)
# print(t)
isError = isSqlError(t) isError = isSqlError(t)
if isError: if isError:
return isError return isError
@@ -2972,18 +2976,21 @@ def setSlaveStatus(version=''):
return mw.returnJson(True, '设置成功!') return mw.returnJson(True, '设置成功!')
def deleteSlaveFunc(sign = ''):
def deleteSlave(version=''):
args = getArgs()
db = pMysqlDb() db = pMysqlDb()
if 'sign' in args: if sign != '':
sign = args['sign']
db.query("stop slave for channel '{}'".format(sign)) db.query("stop slave for channel '{}'".format(sign))
db.query("reset slave all for channel '{}'".format(sign)) db.query("reset slave all for channel '{}'".format(sign))
else: else:
db.query('stop slave') db.query('stop slave')
db.query('reset slave all') db.query('reset slave all')
def deleteSlave(version=''):
args = getArgs()
sign = ''
if 'sign' in args:
sign = args['sign']
deleteSlaveFunc(sign)
return mw.returnJson(True, '删除成功!') return mw.returnJson(True, '删除成功!')
@@ -3018,6 +3025,291 @@ def dumpMysqlData(version=''):
return 'fail' return 'fail'
############### --- 重要 数据补足同步 ---- ###########
def getSyncMysqlDB(dbname,sign = ''):
conn = pSqliteDb('slave_sync_user')
if sign != '':
data = conn.field('ip,port,user,pass,mode,cmd').where('ip=?', (sign,)).find()
else:
data = conn.field('ip,port,user,pass,mode,cmd').find()
user = data['user']
apass = data['pass']
port = data['port']
ip = data['ip']
# 远程数据
sync_db = mw.getMyORM()
# MySQLdb |
sync_db.setPort(port)
sync_db.setHost(ip)
sync_db.setUser(user)
sync_db.setPwd(apass)
sync_db.setDbName(dbname)
sync_db.setTimeout(60)
return sync_db
def syncDatabaseRepairTempFile():
tmp_log = mw.getMWLogs()+ '/mysql-check.log'
return tmp_log
def syncDatabaseRepairLog(version=''):
import subprocess
args = getArgs()
data = checkArgs(args, ['db','sign','op'])
if not data[0]:
return data[1]
sync_args_db = args['db']
sync_args_sign = args['sign']
op = args['op']
tmp_log = syncDatabaseRepairTempFile()
cmd = 'cd '+mw.getServerDir()+'/mdserver-web && source bin/activate && python3 plugins/mysql/index.py sync_database_repair {"db":"'+sync_args_db+'","sign":"'+sync_args_sign+'"}'
# print(cmd)
if op == 'get':
log = mw.getLastLine(tmp_log, 15)
return mw.returnJson(True, log)
if op == 'cmd':
return mw.returnJson(True, 'ok', cmd)
if op == 'do':
os.system(' echo "开始执行" > '+ tmp_log)
os.system(cmd +' >> '+ tmp_log +' &')
return mw.returnJson(True, 'ok')
return mw.returnJson(False, '无效请求!')
def syncDatabaseRepair(version=''):
time_stats_s = time.time()
tmp_log = syncDatabaseRepairTempFile()
from pymysql.converters import escape_string
args = getArgs()
data = checkArgs(args, ['db','sign'])
if not data[0]:
return data[1]
sync_args_db = args['db']
sync_args_sign = args['sign']
# 本地数据
local_db = pMysqlDb()
# 远程数据
sync_db = getSyncMysqlDB(sync_args_db,sync_args_sign)
tables = local_db.query('show tables from `%s`' % sync_args_db)
table_key = "Tables_in_" + sync_args_db
inconsistent_table = []
tmp_dir = '/tmp/sync_db_repair'
mw.execShell('mkdir -p '+tmp_dir)
for tb in tables:
table_name = sync_args_db+'.'+tb[table_key]
table_check_file = tmp_dir+'/'+table_name+'.txt'
if os.path.exists(table_check_file):
# print(table_name+', 已检查OK')
continue
primary_key_sql = "SHOW INDEX FROM "+table_name+" WHERE Key_name = 'PRIMARY';";
primary_key_data = local_db.query(primary_key_sql)
# print(primary_key_sql,primary_key_data)
pkey_name = '*'
if len(primary_key_data) == 1:
pkey_name = primary_key_data[0]['Column_name']
# print(pkey_name)
if pkey_name != '*' :
# 智能校验(由于服务器同步可能会慢,比较总数总是对不上)
cmd_local_newpk_sql = 'select ' + pkey_name + ' from ' + table_name + " order by " + pkey_name + " desc limit 1"
cmd_local_newpk_data = local_db.query(cmd_local_newpk_sql)
# print(cmd_local_newpk_data)
if len(cmd_local_newpk_data) == 1:
# 比较总数
cmd_count_sql = 'select count('+pkey_name+') as num from '+table_name + ' where '+pkey_name + ' <= '+ str(cmd_local_newpk_data[0][pkey_name])
local_count_data = local_db.query(cmd_count_sql)
sync_count_data = sync_db.query(cmd_count_sql)
if local_count_data != sync_count_data:
print(cmd_count_sql)
print("all data compare: ",local_count_data, sync_count_data)
else:
print(table_name+' smart compare check ok.')
mw.writeFile(tmp_log, table_name+' smart compare check ok.\n','a+')
mw.execShell("echo 'ok' > "+table_check_file)
continue
# 比较总数
cmd_count_sql = 'select count('+pkey_name+') as num from '+table_name
local_count_data = local_db.query(cmd_count_sql)
sync_count_data = sync_db.query(cmd_count_sql)
if local_count_data != sync_count_data:
print("all data compare: ",local_count_data, sync_count_data)
inconsistent_table.append(table_name)
diff = sync_count_data[0]['num'] - local_count_data[0]['num']
print(table_name+', need sync. diff,'+str(diff))
mw.writeFile(tmp_log, table_name+', need sync. diff,'+str(diff)+'\n','a+')
else:
print(table_name+' check ok.')
mw.writeFile(tmp_log, table_name+' check ok.\n','a+')
mw.execShell("echo 'ok' > "+table_check_file)
# inconsistent_table = ['xx.xx']
# 数据对齐
for table_name in inconsistent_table:
is_break = False
while not is_break:
local_db.ping()
# 远程数据
sync_db.ping()
print("check table:"+table_name)
mw.writeFile(tmp_log, "check table:"+table_name+'\n','a+')
table_name_pos = 0
table_name_pos_file = tmp_dir+'/'+table_name+'.pos.txt'
primary_key_sql = "SHOW INDEX FROM "+table_name+" WHERE Key_name = 'PRIMARY';";
primary_key_data = local_db.query(primary_key_sql)
pkey_name = primary_key_data[0]['Column_name']
if os.path.exists(table_name_pos_file):
table_name_pos = mw.readFile(table_name_pos_file)
data_select_sql = 'select * from '+table_name + ' where '+pkey_name+' > '+str(table_name_pos)+' limit 10000'
print(data_select_sql)
local_select_data = local_db.query(data_select_sql)
time_s = time.time()
sync_select_data = sync_db.query(data_select_sql)
print(f'sync query cos:{time.time() - time_s:.4f}s')
mw.writeFile(tmp_log, f'sync query cos:{time.time() - time_s:.4f}s\n','a+')
# print(local_select_data)
# print(sync_select_data)
# print(len(local_select_data))
# print(len(sync_select_data))
print('pos:',str(table_name_pos),'local compare sync,',local_select_data == sync_select_data)
cmd_count_sql = 'select count('+pkey_name+') as num from '+table_name
local_count_data = local_db.query(cmd_count_sql)
time_s = time.time()
sync_count_data = sync_db.query(cmd_count_sql)
print(f'sync count data cos:{time.time() - time_s:.4f}s')
print(local_count_data,sync_count_data)
# 数据同步有延迟,相等即任务数据补足完成
if local_count_data[0]['num'] == sync_count_data[0]['num']:
is_break = True
break
diff = sync_count_data[0]['num'] - local_count_data[0]['num']
print("diff," + str(diff)+' line data!')
if local_select_data == sync_select_data:
data_count = len(local_select_data)
if data_count == 0:
# mw.writeFile(table_name_pos_file, '0')
print(table_name+",data is equal ok..")
is_break = True
break
# print(table_name,data_count)
pos = local_select_data[data_count-1][pkey_name]
print('pos',pos)
progress = pos/sync_count_data[0]['num']
print('progress,%.2f' % progress+'%')
mw.writeFile(table_name_pos_file, str(pos))
else:
sync_select_data_len = len(sync_select_data)
skip_idx = 0
# 主库PK -> 查询本地 | 保证一致
if sync_select_data_len > 0:
for idx in range(sync_select_data_len):
sync_idx_data = sync_select_data[idx]
local_idx_data = None
if idx in local_select_data:
local_idx_data = local_select_data[idx]
if sync_select_data[idx] == local_idx_data:
skip_idx = idx
pos = local_select_data[idx][pkey_name]
mw.writeFile(table_name_pos_file, str(pos))
# print(insert_data)
local_inquery_sql = 'select * from ' + table_name+ ' where ' +pkey_name+' = '+ str(sync_idx_data[pkey_name])
# print(local_inquery_sql)
ldata = local_db.query(local_inquery_sql)
# print('ldata:',ldata)
if len(ldata) == 0:
print("id:"+ str(sync_idx_data[pkey_name])+ " no exists, insert")
insert_sql = 'insert into ' + table_name
field_str = ''
value_str = ''
for field in sync_idx_data:
field_str += '`'+field+'`,'
value_str += '\''+escape_string(str(sync_idx_data[field]))+'\','
field_str = '(' +field_str.strip(',')+')'
value_str = '(' +value_str.strip(',')+')'
insert_sql = insert_sql+' '+field_str+' values'+value_str+';'
print(insert_sql)
r = local_db.execute(insert_sql)
print(r)
else:
# print('compare sync->local:',sync_idx_data == ldata[0] )
if ldata[0] == sync_idx_data:
continue
print("id:"+ str(sync_idx_data[pkey_name])+ " data is not equal, update")
update_sql = 'update ' + table_name
field_str = ''
value_str = ''
for field in sync_idx_data:
if field == pkey_name:
continue
field_str += '`'+field+'`=\''+escape_string(str(sync_idx_data[field]))+'\','
field_str = field_str.strip(',')
update_sql = update_sql+' set '+field_str+' where '+pkey_name+'=\''+str(sync_idx_data[pkey_name])+'\';'
print(update_sql)
r = local_db.execute(update_sql)
print(r)
# 本地PK -> 查询主库 | 保证一致
# local_select_data_len = len(local_select_data)
# if local_select_data_len > 0:
# for idx in range(local_select_data_len):
# if idx < skip_idx:
# continue
# local_idx_data = local_select_data[idx]
# print('local idx check', idx, skip_idx)
# local_inquery_sql = 'select * from ' + table_name+ ' where ' +pkey_name+' = '+ str(local_idx_data[pkey_name])
# print(local_inquery_sql)
# sdata = sync_db.query(local_inquery_sql)
# sdata_len = len(sdata)
# print('sdata:',sdata,sdata_len)
# if sdata_len == 0:
# delete_sql = 'delete from ' + table_name + ' where ' +pkey_name+' = '+ str(local_idx_data[pkey_name])
# print(delete_sql)
# r = local_db.execute(delete_sql)
# print(r)
# break
if is_break:
print("break all")
break
time.sleep(3)
print(f'data check cos:{time.time() - time_stats_s:.4f}s')
print("data supplementation completed")
mw.execShell('rm -rf '+tmp_dir)
return 'ok'
############### --- 重要 同步---- ########### ############### --- 重要 同步---- ###########
def asyncTmpfile(): def asyncTmpfile():
@@ -3028,7 +3320,20 @@ def asyncTmpfile():
def writeDbSyncStatus(data): def writeDbSyncStatus(data):
path = asyncTmpfile() path = asyncTmpfile()
mw.writeFile(path, json.dumps(data)) mw.writeFile(path, json.dumps(data))
return True
def fullSyncCmd():
time_all_s = time.time()
args = getArgs()
data = checkArgs(args, ['db', 'sign'])
if not data[0]:
return data[1]
db = args['db']
sign = args['sign']
cmd = 'cd '+mw.getServerDir()+'/mdserver-web && source bin/activate && python3 plugins/mysql/index.py do_full_sync {"db":"'+db+'","sign":"'+sign+'"}'
return mw.returnJson(True,'ok',cmd)
def doFullSync(version=''): def doFullSync(version=''):
mode_file = getSyncModeFile() mode_file = getSyncModeFile()
@@ -3042,7 +3347,53 @@ def doFullSync(version=''):
return doFullSyncUser(version) return doFullSyncUser(version)
def isSimpleSyncCmd(sql):
new_sql = sql.lower()
if new_sql.find('master_auto_position') > 0:
return False
return True
def getChannelNameForCmd(cmd):
cmd = cmd.lower()
cmd_arr = cmd.split('channel')
if len(cmd_arr) == 2:
cmd_channel_info = cmd_arr[1]
channel_name = cmd_channel_info.strip()
channel_name = channel_name.strip(';')
channel_name = channel_name.strip("'")
return channel_name
return ''
def doFullSyncUserImportContentForChannel(file, channel_name):
# print(file, channel_name)
content = mw.readFile(file)
content = content.replace('STOP SLAVE;', "STOP SLAVE for channel '{}';".format(channel_name))
content = content.replace('START SLAVE;', "START SLAVE for channel '{}';".format(channel_name))
find_head = "CHANGE MASTER TO "
find_re = find_head+"(.*?);"
find_r = re.search(find_re, content, re.I|re.M)
if find_r:
find_rg = find_r.groups()
if len(find_rg)>0:
find_str = find_head+find_rg[0]
if find_str.lower().find('channel')==-1:
content = content.replace(find_str+';', find_str+" for channel '{}';".format(channel_name))
mw.writeFile(file,content)
return True
def doFullSyncUser(version=''): def doFullSyncUser(version=''):
which_pv = mw.execShell('which pv')
is_exist_pv = False
if not os.path.exists(which_pv[0]):
is_exist_pv = True
time_all_s = time.time()
args = getArgs() args = getArgs()
data = checkArgs(args, ['db', 'sign']) data = checkArgs(args, ['db', 'sign'])
if not data[0]: if not data[0]:
@@ -3063,6 +3414,9 @@ def doFullSyncUser(version=''):
db = pMysqlDb() db = pMysqlDb()
# 重置
# deleteSlaveFunc(sync_sign)
conn = pSqliteDb('slave_sync_user') conn = pSqliteDb('slave_sync_user')
if sync_sign != '': if sync_sign != '':
data = conn.field('ip,port,user,pass,mode,cmd').where( data = conn.field('ip,port,user,pass,mode,cmd').where(
@@ -3070,10 +3424,15 @@ def doFullSyncUser(version=''):
else: else:
data = conn.field('ip,port,user,pass,mode,cmd').find() data = conn.field('ip,port,user,pass,mode,cmd').find()
# print(data)
user = data['user'] user = data['user']
apass = data['pass'] apass = data['pass']
port = data['port'] port = data['port']
ip = data['ip'] ip = data['ip']
cmd = data['cmd']
channel_name = getChannelNameForCmd(cmd)
sync_mdb = getSyncMysqlDB(sync_db,sync_sign)
bak_file = '/tmp/tmp.sql' bak_file = '/tmp/tmp.sql'
if os.path.exists(bak_file): if os.path.exists(bak_file):
@@ -3087,41 +3446,111 @@ def doFullSyncUser(version=''):
time.sleep(1) time.sleep(1)
writeDbSyncStatus({'code': 1, 'msg': '正在停止从库...', 'progress': 15}) writeDbSyncStatus({'code': 1, 'msg': '正在停止从库...', 'progress': 15})
if version == '8.0':
mdb8 = ['8.0','8.1','8.2','8.3','8.4']
if mw.inArray(mdb8,version):
db.query("stop slave user='{}' password='{}';".format(user, apass)) db.query("stop slave user='{}' password='{}';".format(user, apass))
else: else:
db.query("stop slave") db.query("stop slave")
time.sleep(2) time.sleep(1)
writeDbSyncStatus({'code': 2, 'msg': '远程导出数据...', 'progress': 20}) writeDbSyncStatus({'code': 2, 'msg': '远程导出数据...', 'progress': 20})
if not os.path.exists(bak_file): # --master-data=2表示在dump过程中记录主库的binlog和pos点,并在dump文件中注释掉这一行
dump_sql_data = getServerDir() + "/bin/mysqldump " + dmp_option + " --force --opt --default-character-set=utf8 --single-transaction -h" + ip + " -P" + \ # --master-data=1表示在dump过程中记录主库的binlog和pos点,并在dump文件中不注释掉这一行,即恢复时会执行
port + " -u" + user + " -p'" + apass + \
"' --ssl-mode=DISABLED " + sync_db + " > " + bak_file
print(dump_sql_data)
mw.execShell(dump_sql_data)
# --dump-slave=2表示在dump过程中,在从库dump,mysqldump进程也要在从库执行,记录当时主库的binlog和pos点,并在dump文件中注释掉这一行
# --dump-slave=1表示在dump过程中,在从库dump,mysqldump进程也要在从库执行,记录当时主库的binlog和pos点,并在dump文件中不注释掉这一行
# --force --opt --single-transaction
# --skip-opt --create-options
# --master-data=1
find_run_dump = mw.execShell('ps -ef | grep mysqldump | grep -v grep')
if find_run_dump[0] != "":
print("正在远程导出数据中,别着急...")
writeDbSyncStatus({'code': 3.1, 'msg': '正在远程导出数据中,别着急...', 'progress': 19})
return False
time_s = time.time()
if not os.path.exists(bak_file):
if isSimpleSyncCmd(cmd):
dmp_option += " --master-data=1 --apply-slave-statements --include-master-host-port "
else:
dmp_option += ' '
dump_sql_data = getServerDir() + "/bin/mysqldump --single-transaction --default-character-set=utf8mb4 --compress -q " + dmp_option + " -h" + ip + " -P" + \
port + " -u" + user + " -p'" + apass + "' --ssl-mode=DISABLED " + sync_db + " > " + bak_file
print(dump_sql_data)
time_s = time.time()
r = mw.execShell(dump_sql_data)
print(r)
time_e = time.time()
export_cos = time_e - time_s
print("export cos:", export_cos)
writeDbSyncStatus({'code': 3, 'msg': '正在到本地导入数据中...', 'progress': 40}) writeDbSyncStatus({'code': 3, 'msg': '导出耗时:'+str(int(export_cos))+'秒,正在到本地导入数据中...', 'progress': 40})
find_run_import = mw.execShell('ps -ef | grep mysql| grep '+ bak_file +' | grep -v grep')
if find_run_import[0] != "":
print("正在导入数据中,别着急...")
writeDbSyncStatus({'code': 4.1, 'msg': '正在导入数据中,别着急...', 'progress': 39})
return False
time_s = time.time()
if os.path.exists(bak_file): if os.path.exists(bak_file):
# 重置
db.execute('reset master')
# 加快导入 - 开始
# db.execute('set global innodb_flush_log_at_trx_commit = 2')
# db.execute('set global sync_binlog = 2000')
if channel_name != '':
doFullSyncUserImportContentForChannel(bak_file, channel_name)
pwd = pSqliteDb('config').where('id=?', (1,)).getField('mysql_root') pwd = pSqliteDb('config').where('id=?', (1,)).getField('mysql_root')
sock = getSocketFile() sock = getSocketFile()
my_import_cmd = getServerDir() + '/bin/mysql -S ' + sock + " -uroot -p'" + pwd + \
"' " + sync_db_import + ' < ' + bak_file
print(my_import_cmd)
mw.execShell(my_import_cmd)
if version == '8.0': if is_exist_pv:
my_import_cmd = getServerDir() + '/bin/mysql -S ' + sock + " -uroot -p'" + pwd + "' " + sync_db_import
my_import_cmd = "pv -t -p " + bak_file + '|' + my_import_cmd
print(my_import_cmd)
os.system(my_import_cmd)
else:
my_import_cmd = getServerDir() + '/bin/mysql -S ' + sock + " -uroot -p'" + pwd + "' " + sync_db_import + ' < ' + bak_file
print(my_import_cmd)
mw.execShell(my_import_cmd)
# 加快导入 - 结束
# db.execute('set global innodb_flush_log_at_trx_commit = 1')
# db.execute('set global sync_binlog = 1')
time_e = time.time()
import_cos = time_e - time_s
print("import cos:", import_cos)
writeDbSyncStatus({'code': 4, 'msg': '导入耗时:'+str(int(import_cos))+'秒', 'progress': 60})
time.sleep(3)
# print(cmd)
# r = db.query(cmd)
# print(r)
mdb8 = ['8.0','8.1','8.2','8.3','8.4']
if mw.inArray(mdb8,version):
db.query("start slave user='{}' password='{}';".format(user, apass)) db.query("start slave user='{}' password='{}';".format(user, apass))
else: else:
db.query("start slave") db.query("start slave")
writeDbSyncStatus({'code': 6, 'msg': '从库重启完成...', 'progress': 100}) db.query("start all slaves")
time_all_e = time.time()
cos = time_all_e - time_all_s
writeDbSyncStatus({'code': 6, 'msg': '总耗时:'+str(int(cos))+'秒,从库重启完成...', 'progress': 100})
if os.path.exists(bak_file): if os.path.exists(bak_file):
os.system("rm -rf " + bak_file) os.system("rm -rf " + bak_file)
return True return True
@@ -3252,10 +3681,13 @@ def doFullSyncSSH(version=''):
print(import_data[0]) print(import_data[0])
writeDbSyncStatus({'code': 5, 'msg': '导入数据失败...', 'progress': 100}) writeDbSyncStatus({'code': 5, 'msg': '导入数据失败...', 'progress': 100})
return 'fail' return 'fail'
# "start slave user='{}' password='{}';".format(uinfo['username'], uinfo['password']) mdb8 = ['8.0','8.1','8.2','8.3','8.4']
if mw.inArray(mdb8,version):
db.query("start slave") db.query("start slave user='{}' password='{}';".format(uinfo['username'], uinfo['password']))
else:
db.query("start slave")
writeDbSyncStatus({'code': 6, 'msg': '从库重启完成...', 'progress': 100}) writeDbSyncStatus({'code': 6, 'msg': '从库重启完成...', 'progress': 100})
os.system("rm -rf " + SSH_PRIVATE_KEY) os.system("rm -rf " + SSH_PRIVATE_KEY)
@@ -3300,9 +3732,16 @@ def fullSync(version=''):
def installPreInspection(version): def installPreInspection(version):
import psutil
mem = psutil.virtual_memory()
memTotal = mem.total
memG = memTotal/1024/1024/1024
if memG > 2:
return 'ok'
swap_path = mw.getServerDir() + "/swap" swap_path = mw.getServerDir() + "/swap"
if not os.path.exists(swap_path): if not os.path.exists(swap_path):
return "为了稳定安装MySQL,先安装swap插件!" return "内存小,为了稳定安装MySQL,先安装swap插件!"
return 'ok' return 'ok'
@@ -3365,7 +3804,7 @@ if __name__ == "__main__":
elif func == 'my_db_pos': elif func == 'my_db_pos':
print(getMyDbPos()) print(getMyDbPos())
elif func == 'set_db_pos': elif func == 'set_db_pos':
print(setMyDbPos()) print(setMyDbPos(version))
elif func == 'my_port': elif func == 'my_port':
print(getMyPort()) print(getMyPort())
elif func == 'set_my_port': elif func == 'set_my_port':
@@ -3494,7 +3933,13 @@ if __name__ == "__main__":
print(fullSync(version)) print(fullSync(version))
elif func == 'do_full_sync': elif func == 'do_full_sync':
print(doFullSync(version)) print(doFullSync(version))
elif func == 'full_sync_cmd':
print(fullSyncCmd())
elif func == 'dump_mysql_data': elif func == 'dump_mysql_data':
print(dumpMysqlData(version)) print(dumpMysqlData(version))
elif func == 'sync_database_repair':
print(syncDatabaseRepair())
elif func == 'sync_database_repair_log':
print(syncDatabaseRepairLog())
else: else:
print('error') print('error')
+5 -2
View File
@@ -6,8 +6,11 @@ export PATH
# https://www.cnblogs.com/whiteY/p/17331882.html # https://www.cnblogs.com/whiteY/p/17331882.html
# cd /www/server/mdserver-web/plugins/mysql && bash install.sh install 8.2 # cd /www/server/mdserver-web/plugins/mysql && bash install.sh install 8.2
# cd /www/server/mdserver-web && source bin/activate && python3 /www/server/mdserver-web/plugins/mysql/index.py try_slave_sync_bugfix {} # cd /www/server/mdserver-web && source bin/activate && python3 plugins/mysql/index.py try_slave_sync_bugfix {}
# cd /www/server/mdserver-web && source bin/activate && python3 plugins/mysql/index.py do_full_sync {"db":"xxx","sign":"","begin":1}
# cd /www/server/mdserver-web && source bin/activate && python3 plugins/mysql/index.py sync_database_repair {"db":"xxx","sign":""}
# cd /www/server/mdserver-web && source bin/activate && python3 plugins/mysql/index.py init_slave_status
# cd /www/server/mdserver-web && source bin/activate && python3 plugins/mysql/index.py install_pre_inspection
curPath=`pwd` curPath=`pwd`
rootPath=$(dirname "$curPath") rootPath=$(dirname "$curPath")
rootPath=$(dirname "$rootPath") rootPath=$(dirname "$rootPath")
+124 -12
View File
@@ -871,7 +871,7 @@ function openPhpmyadmin(name,username,password){
} }
if (rdata.data['cfg']['choose'] != 'mysql'){ if (rdata.data['cfg']['choose'] != 'mysql'){
layer.msg('当前为['+rdata.choose+']模式,若要使用请修改phpMyAdmin访问切换.',{icon:2,shade: [0.3, '#000']}); layer.msg('当前为['+rdata.data['cfg']['choose'] + ']模式,若要使用请修改phpMyAdmin访问切换.',{icon:2,shade: [0.3, '#000']});
return; return;
} }
var home_page = rdata.data['home_page']; var home_page = rdata.data['home_page'];
@@ -1985,6 +1985,7 @@ function getFullSyncStatus(db){
</div>\ </div>\
<div class='table_toolbar' style='left:0px;'>\ <div class='table_toolbar' style='left:0px;'>\
<span data-status='init' class='sync btn btn-default btn-sm' id='begin_full_sync'>开始</span>\ <span data-status='init' class='sync btn btn-default btn-sm' id='begin_full_sync'>开始</span>\
<span data-status='init' class='btn btn-default btn-sm' id='full_sync_cmd'>手动命令</span>\
</div>\ </div>\
</div>", </div>",
cancel: function(){ cancel: function(){
@@ -2003,10 +2004,36 @@ function getFullSyncStatus(db){
fullSync(db,sign,0); fullSync(db,sign,0);
}, 1000); }, 1000);
$(this).data('status','starting'); $(this).data('status','starting');
$('#begin_full_sync').text("同步中");
} else { } else {
layer.msg("正在同步中..",{icon:0}); layer.msg("正在同步中..",{icon:0});
} }
}); });
$('#full_sync_cmd').click(function(){
myPostN('full_sync_cmd', {'db':db,'sign':''}, function(rdata){
var rdata = $.parseJSON(rdata.data);
layer.open({
title: "手动执行命令CMD",
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);
}
});
});
});
} }
}); });
}); });
@@ -2022,12 +2049,91 @@ function getFullSyncStatus(db){
if (rdata['code']==6 ||rdata['code']<0){ if (rdata['code']==6 ||rdata['code']<0){
layer.msg(rdata['msg']); layer.msg(rdata['msg']);
clearInterval(timeId); clearInterval(timeId);
$('#begin_full_sync').text("同步结束,再次同步?");
$("#begin_full_sync").attr('data-status','init'); $("#begin_full_sync").attr('data-status','init');
} }
}); });
} }
} }
function dataSyncVerify(db){
var reqTimer = null;
function requestLogs(layerIndex){
myPostN('sync_database_repair_log', {db:db, sign:'',op:'get'}, function(rdata){
var rdata = $.parseJSON(rdata.data);
if(!rdata.status) {
layer.close(layerIndex);
layer.msg(rdata.msg,{icon:2, time:2000});
clearInterval(reqTimer);
return;
};
if (rdata.msg == ''){
rdata.msg = '暂无数据!';
}
$("#data_verify_log").html(rdata.msg);
//滚动到最低
var ob = document.getElementById('data_verify_log');
ob.scrollTop = ob.scrollHeight;
});
}
layer.open({
type: 1,
title: '同步数据库['+db+']数据校验',
area: '500px',
btn:[ "开始","取消","手动"],
content:"<div class='bt-form'>\
"+'<pre id="data_verify_log" style="overflow: auto; border: 0px none; line-height:23px;padding: 5px; margin: 0px; white-space: pre-wrap; height: 395px; background-color: rgb(51,51,51);color:#f1f1f1;border-radius:0px;font-family:"></pre>'+"\
</div>",
cancel: function(){
if (reqTimer){
clearInterval(reqTimer);
}
},
yes:function(index,layer_index){
myPostN('sync_database_repair_log', {db:db, sign:'',op:'do'}, function(data){});
layer.msg("执行成功");
requestLogs(layer_index);
reqTimer = setInterval(function(){
requestLogs(layer_index);
},3000);
},
success:function(){
},
btn3: function(){
myPostN('sync_database_repair_log', {db:db, sign:'',op:'cmd'}, function(rdata){
var rdata = $.parseJSON(rdata.data);
layer.open({
title: "手动执行命令CMD",
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);
}
});
});
return false;
}
});
}
function addSlaveSSH(ip=''){ function addSlaveSSH(ip=''){
myPost('get_slave_ssh_by_ip', {ip:ip}, function(rdata){ myPost('get_slave_ssh_by_ip', {ip:ip}, function(rdata){
@@ -2183,7 +2289,7 @@ function addSlaveSyncUser(ip=''){
var index = layer.open({ var index = layer.open({
type: 1, type: 1,
area: ['500px','470px'], area: ['500px','510px'],
title: '同步账户', title: '同步账户',
closeBtn: 1, closeBtn: 1,
shift: 5, shift: 5,
@@ -2194,6 +2300,15 @@ function addSlaveSyncUser(ip=''){
<div class='line'><span class='tname'>端口</span><div class='info-r'><input name='port' class='bt-input-text mr5' type='number' style='width:330px;' value='"+port+"'></div></div>\ <div class='line'><span class='tname'>端口</span><div class='info-r'><input name='port' class='bt-input-text mr5' type='number' style='width:330px;' value='"+port+"'></div></div>\
<div class='line'><span class='tname'>同步账户</span><div class='info-r'><input name='user' class='bt-input-text mr5' type='text' style='width:330px;' value='"+user+"'></div></div>\ <div class='line'><span class='tname'>同步账户</span><div class='info-r'><input name='user' class='bt-input-text mr5' type='text' style='width:330px;' value='"+user+"'></div></div>\
<div class='line'><span class='tname'>同步密码</span><div class='info-r'><input name='pass' class='bt-input-text mr5' type='text' style='width:330px;' value='"+pass+"'></div></div>\ <div class='line'><span class='tname'>同步密码</span><div class='info-r'><input name='pass' class='bt-input-text mr5' type='text' style='width:330px;' value='"+pass+"'></div></div>\
<div class='line'>\
<span class='tname'>同步模式</span>\
<div class='info-r'>\
<select class='bt-input-text mr5' name='mode'>\
<option value='0' "+( mode == '0' ? 'selected="selected"' : '')+">经典</option>\
<option value='1' "+( mode == '1' ? 'selected="selected"' : '')+">GTID</option>\
</select>\
</div>\
</div>\
<div class='line'>\ <div class='line'>\
<span class='tname'>CMD[必须填写]</span>\ <span class='tname'>CMD[必须填写]</span>\
<div class='info-r'><textarea class='bt-input-text mr5' row='20' cols='30' name='cmd' style='width:330px;height:150px;'></textarea></div>\ <div class='info-r'><textarea class='bt-input-text mr5' row='20' cols='30' name='cmd' style='width:330px;height:150px;'></textarea></div>\
@@ -2204,6 +2319,7 @@ function addSlaveSyncUser(ip=''){
$('textarea[name="cmd"]').html(cmd); $('textarea[name="cmd"]').html(cmd);
$('textarea[name="cmd"]').change(function(){ $('textarea[name="cmd"]').change(function(){
var val = $(this).val(); var val = $(this).val();
val = val.replace(';','');
var a = {}; var a = {};
if (val.toLowerCase().indexOf('for')>0){ if (val.toLowerCase().indexOf('for')>0){
cmd_tmp = val.split('for'); cmd_tmp = val.split('for');
@@ -2230,11 +2346,6 @@ function addSlaveSyncUser(ip=''){
$('input[name="port"]').val(a['MASTER_PORT']); $('input[name="port"]').val(a['MASTER_PORT']);
$('input[name="user"]').val(a['MASTER_USER']); $('input[name="user"]').val(a['MASTER_USER']);
$('input[name="pass"]').val(a['MASTER_PASSWORD']); $('input[name="pass"]').val(a['MASTER_PASSWORD']);
console.log(a['MASTER_AUTO_POSITION'],typeof(a['MASTER_AUTO_POSITION']));
if (typeof(a['MASTER_AUTO_POSITION']) != 'undefined' ){
$('input[name="mode"]').val('1');
}
}); });
}, },
yes:function(index){ yes:function(index){
@@ -2243,7 +2354,7 @@ function addSlaveSyncUser(ip=''){
var user = $('input[name="user"]').val(); var user = $('input[name="user"]').val();
var pass = $('input[name="pass"]').val(); var pass = $('input[name="pass"]').val();
var cmd = $('textarea[name="cmd"]').val(); var cmd = $('textarea[name="cmd"]').val();
var mode = $('input[name="mode"]').val(); var mode = $('select[name="mode"]').val();
var data = {ip:ip,port:port,cmd:cmd,user:user,pass:pass,mode:mode}; var data = {ip:ip,port:port,cmd:cmd,user:user,pass:pass,mode:mode};
myPost('add_slave_sync_user', data, function(ret_data){ myPost('add_slave_sync_user', data, function(ret_data){
@@ -2521,7 +2632,7 @@ function masterOrSlaveConf(version=''){
for(i in rdata.data){ for(i in rdata.data){
var v = rdata.data[i]; var v = rdata.data[i];
if ('Channel_Name' in v){ if ('Channel_Name' in v && v['Channel_Name'] !=''){
isHasSign = true; isHasSign = true;
} }
@@ -2692,7 +2803,8 @@ function masterOrSlaveConf(version=''){
list += '<td>' + rdata.data[i]['name'] +'</td>'; list += '<td>' + rdata.data[i]['name'] +'</td>';
list += '<td style="text-align:right">' + list += '<td style="text-align:right">' +
'<a href="javascript:;" class="btlink" onclick="setDbSlave(\''+rdata.data[i]['name']+'\')" title="加入|退出">'+(rdata.data[i]['slave']?'退出':'加入')+'</a> | ' + '<a href="javascript:;" class="btlink" onclick="setDbSlave(\''+rdata.data[i]['name']+'\')" title="加入|退出">'+(rdata.data[i]['slave']?'退出':'加入')+'</a> | ' +
'<a href="javascript:;" class="btlink" onclick="getFullSyncStatus(\''+rdata.data[i]['name']+'\')" title="同步">同步</a>' + '<a href="javascript:;" class="btlink" onclick="getFullSyncStatus(\''+rdata.data[i]['name']+'\')" title="同步">同步</a> | ' +
'<a href="javascript:;" class="btlink" onclick="dataSyncVerify(\''+rdata.data[i]['name']+'\')" title="数据校验">数据校验</a>' +
'</td>'; '</td>';
list += '</tr>'; list += '</tr>';
} }
@@ -2824,10 +2936,10 @@ function masterOrSlaveConf(version=''){
getMasterDbList(); getMasterDbList();
} }
// if (rdata.slave_status){ if (rdata.slave_status){
getAsyncMasterDbList(); getAsyncMasterDbList();
getAsyncDataList() getAsyncDataList()
// } }
}); });
} }
getMasterStatus(); getMasterStatus();
+1 -1
View File
@@ -51,7 +51,7 @@ http
gzip_min_length 1k; gzip_min_length 1k;
gzip_buffers 4 16k; gzip_buffers 4 16k;
gzip_http_version 1.1; gzip_http_version 1.1;
gzip_comp_level 2; gzip_comp_level 9;
gzip_types text/plain application/javascript application/x-javascript text/javascript text/css application/xml; gzip_types text/plain application/javascript application/x-javascript text/javascript text/css application/xml;
gzip_vary on; gzip_vary on;
gzip_proxied expired no-cache no-store private auth; gzip_proxied expired no-cache no-store private auth;
+4
View File
@@ -17,6 +17,10 @@ sysName=`uname`
actionType=$1 actionType=$1
version=$2 version=$2
if [ "$version" -ge '74' ];then
LIBV=1.19.0
fi
if [ "$version" == '71' ];then if [ "$version" == '71' ];then
LIBV=1.11.1 LIBV=1.11.1
fi fi
+6 -2
View File
@@ -210,13 +210,17 @@ def __delete_port(port):
def openPort(): def openPort():
for i in ["888"]: conf = getCfg()
port = conf['port']
for i in [port]:
__release_port(i) __release_port(i)
return True return True
def delPort(): def delPort():
for i in ["888"]: conf = getCfg()
port = conf['port']
for i in [port]:
__delete_port(i) __delete_port(i)
return True return True
+9 -4
View File
@@ -1,5 +1,5 @@
daemonize yes daemonize yes
pidfile {$SERVER_PATH}/redis/redis_6379.pid pidfile {$SERVER_PATH}/redis/redis.pid
bind 127.0.0.1 bind 127.0.0.1
port 6379 port 6379
@@ -40,15 +40,20 @@ maxclients 10000
#maxmemory-samples 3 #maxmemory-samples 3
maxmemory 218mb maxmemory 218mb
maxmemory-policy volatile-ttl maxmemory-policy volatile-ttl
#maxmemory-policy allkeys-lru
############################## APPEND ONLY MODE ############################### ############################## APPEND ONLY MODE ###############################
#appendonly no # appendonly no
# appendfsync always # appendfsync always
#appendfsync everysec # appendfsync everysec
# appendfsync no # appendfsync no
#no-appendfsync-on-rewrite no
# appendfilename "appendonly.aof"
# no-appendfsync-on-rewrite no
auto-aof-rewrite-percentage 100 auto-aof-rewrite-percentage 100
auto-aof-rewrite-min-size 64mb auto-aof-rewrite-min-size 64mb
+1 -1
View File
@@ -23,7 +23,7 @@ if [ "$REDISPASS" != "" ];then
fi fi
EXEC={$SERVER_PATH}/redis/bin/redis-server EXEC={$SERVER_PATH}/redis/bin/redis-server
CLIEXEC="{$SERVER_PATH}/redis/bin/redis-cli -p $REDISPORT$REDISPASS" CLIEXEC="{$SERVER_PATH}/redis/bin/redis-cli -p $REDISPORT$REDISPASS"
PIDFILE={$SERVER_PATH}/redis/redis_6379.pid PIDFILE={$SERVER_PATH}/redis/redis.pid
echo $REDISPASS echo $REDISPASS
echo $REDISPORT echo $REDISPORT
+14 -9
View File
@@ -25,14 +25,16 @@ Install_App()
{ {
echo '正在安装脚本文件...' > $install_tmp echo '正在安装脚本文件...' > $install_tmp
mkdir -p $serverPath/source mkdir -p $serverPath/source
mkdir -p $serverPath/source/redis
if [ ! -f $serverPath/source/redis-${VERSION}.tar.gz ];then FILE_TGZ=redis-${VERSION}.tar.gz
wget -O $serverPath/source/redis-${VERSION}.tar.gz http://download.redis.io/releases/redis-${VERSION}.tar.gz REDIS_DIR=$serverPath/source/redis
if [ ! -f $REDIS_DIR/${FILE_TGZ} ];then
wget -O $REDIS_DIR/${FILE_TGZ} http://download.redis.io/releases/${FILE_TGZ}
fi fi
cd $serverPath/source && tar -zxvf redis-${VERSION}.tar.gz cd $REDIS_DIR && tar -zxvf ${FILE_TGZ}
CMD_MAKE=`which gmake` CMD_MAKE=`which gmake`
if [ "$?" == "0" ];then if [ "$?" == "0" ];then
@@ -55,8 +57,8 @@ Install_App()
echo '安装失败!' echo '安装失败!'
fi fi
if [ -d $serverPath/source/redis-${VERSION} ];then if [ -d ${REDIS_DIR}/redis-${VERSION} ];then
rm -rf $serverPath/source/redis-${VERSION} rm -rf ${REDIS_DIR}/redis-${VERSION}
fi fi
} }
@@ -80,8 +82,11 @@ Uninstall_App()
$serverPath/redis/initd/redis stop $serverPath/redis/initd/redis stop
fi fi
rm -rf $serverPath/redis if [ -d $serverPath/redis ];then
echo "Uninstall_redis" > $install_tmp rm -rf $serverPath/redis
fi
echo "卸载redis成功"
} }
action=$1 action=$1
+1 -1
View File
@@ -1,5 +1,5 @@
daemonize yes daemonize yes
pidfile {$SERVER_PATH}/redis/redis_6379.pid pidfile {$SERVER_PATH}/redis/redis.pid
loglevel notice loglevel notice
logfile {$SERVER_PATH}/redis/data/redis.log logfile {$SERVER_PATH}/redis/data/redis.log
+1 -1
View File
@@ -1,5 +1,5 @@
daemonize yes daemonize yes
pidfile {$SERVER_PATH}/redis/redis_6379.pid pidfile {$SERVER_PATH}/redis/redis.pid
bind 127.0.0.1 bind 127.0.0.1
port 6379 port 6379
+11 -10
View File
@@ -1,17 +1,18 @@
daemonize yes daemonize yes
pidfile {$SERVER_PATH}/redis/redis_6379.pid pidfile {$SERVER_PATH}/redis/redis.pid
loglevel notice
logfile {$SERVER_PATH}/redis/data/redis.log
databases 16
timeout 0
tcp-keepalive 0
bind 127.0.0.1 bind 127.0.0.1
port 6379 port 6379
requirepass {$REDIS_PASS} requirepass {$REDIS_PASS}
timeout 0
tcp-keepalive 0
loglevel notice
logfile {$SERVER_PATH}/redis/data/redis.log
databases 16
################################ SNAPSHOTTING ################################# ################################ SNAPSHOTTING #################################
save 900 1 save 900 1
@@ -33,7 +34,7 @@ slave-priority 100
# 填写主库信息 # 填写主库信息
#slaveof 127.0.0.1 6379 #slaveof 127.0.0.1 6379
#masterauth master_pwd #masterauth 123123
################################## SECURITY ################################### ################################## SECURITY ###################################
@@ -41,7 +42,7 @@ slave-priority 100
################################### LIMITS #################################### ################################### LIMITS ####################################
maxclients 10000 maxclients 10000
#maxmemory-samples 3 #maxmemory-samples 3
maxmemory 218mb maxmemory 0mb
maxmemory-policy volatile-ttl maxmemory-policy volatile-ttl
############################## APPEND ONLY MODE ############################### ############################## APPEND ONLY MODE ###############################
+75
View File
@@ -0,0 +1,75 @@
daemonize yes
pidfile {$SERVER_PATH}/redis/redis.pid
loglevel notice
logfile {$SERVER_PATH}/redis/data/redis.log
databases 16
timeout 0
tcp-keepalive 0
bind 127.0.0.1
port 6379
requirepass {$REDIS_PASS}
################################ SNAPSHOTTING #################################
save ""
stop-writes-on-bgsave-error no
################################# REPLICATION #################################
slave-serve-stale-data yes
slave-read-only yes
repl-disable-tcp-nodelay no
slave-priority 100
# 填写主库信息
#slaveof 127.0.0.1 6379
#masterauth 123123
################################## SECURITY ###################################
################################### LIMITS ####################################
maxclients 10000
#maxmemory-samples 3
maxmemory 218mb
maxmemory-policy allkeys-lru
############################## APPEND ONLY MODE ###############################
################################ LUA SCRIPTING ###############################
lua-time-limit 5000
################################## SLOW LOG ###################################
slowlog-log-slower-than 10000
slowlog-max-len 128
############################### ADVANCED CONFIG ###############################
hash-max-ziplist-entries 512
hash-max-ziplist-value 64
list-max-ziplist-entries 512
list-max-ziplist-value 64
set-max-intset-entries 512
zset-max-ziplist-entries 128
zset-max-ziplist-value 64
activerehashing no
client-output-buffer-limit normal 0 0 0
client-output-buffer-limit slave 256mb 64mb 60
client-output-buffer-limit pubsub 32mb 8mb 60
hz 10
aof-rewrite-incremental-fsync yes
+486
View File
@@ -0,0 +1,486 @@
# coding:utf-8
import sys
import io
import os
import time
import subprocess
import re
import json
sys.path.append(os.getcwd() + "/class/core")
import mw
def getServerDir():
return mw.getServerDir() + '/mysql'
def getPluginDir():
return mw.getPluginDir() + '/mysql'
def getConf():
path = getServerDir() + '/etc/my.cnf'
return path
def getDbPort():
file = getConf()
content = mw.readFile(file)
rep = 'port\s*=\s*(.*)'
tmp = re.search(rep, content)
return tmp.groups()[0].strip()
def getSocketFile():
file = getConf()
content = mw.readFile(file)
rep = 'socket\s*=\s*(.*)'
tmp = re.search(rep, content)
return tmp.groups()[0].strip()
def pSqliteDb(dbname='databases'):
file = getServerDir() + '/mysql.db'
name = 'mysql'
conn = mw.M(dbname).dbPos(getServerDir(), name)
return conn
def pMysqlDb():
# pymysql
db = mw.getMyORM()
db.setPort(getDbPort())
db.setSocket(getSocketFile())
# db.setCharset("utf8")
db.setPwd(pSqliteDb('config').where('id=?', (1,)).getField('mysql_root'))
return db
class sphinxMake():
pdb = None
psdb = None
pkey_name_cache = {}
delta = 'sph_counter'
ver = ''
def __init__(self):
self.pdb = pMysqlDb()
def setDeltaName(self, name):
self.delta = name
return True
def setVersion(self, ver):
self.ver = ver
def createSql(self, db):
conf = '''
CREATE TABLE IF NOT EXISTS `{$DB_NAME}`.`{$TABLE_NAME}` (
`id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
`table` varchar(200) NOT NULL,
`max_id` bigint(20) unsigned NOT NULL DEFAULT '0',
PRIMARY KEY (`id`),
UNIQUE KEY `table_uniq` (`table`),
KEY `table` (`table`)
) ENGINE=InnoDB AUTO_INCREMENT=1 CHARSET=utf8mb4;
'''
conf = conf.replace("{$TABLE_NAME}", self.delta)
conf = conf.replace("{$DB_NAME}", db)
return conf
def eqVerField(self, field):
ver = self.ver.replace(".1",'')
if float(ver) >= 3.6:
if field == 'sql_attr_timestamp':
return 'attr_bigint'
if field == 'sql_attr_bigint':
return 'attr_bigint'
if field == 'sql_attr_float':
return 'attr_float'
if field == 'sql_field_string':
return 'field_string'
if float(ver) >= 3.3:
if field == 'sql_attr_timestamp':
return 'sql_attr_bigint'
return field
def pathVerName(self):
ver = self.ver.replace(".1",'')
# if float(ver) >= 3.6:
# return 'datadir'
return 'path'
def getTablePk(self, db, table):
key = db+'_'+table
if key in self.pkey_name_cache:
return self.pkey_name_cache[key]
# SHOW INDEX FROM bbs.bbs_ucenter_vars WHERE Key_name = 'PRIMARY'
pkey_sql = "SHOW INDEX FROM {}.{} WHERE Key_name = 'PRIMARY';".format(db,table,);
pkey_data = self.pdb.query(pkey_sql)
# print(db, table)
# print(pkey_data)
key = ''
if len(pkey_data) == 1:
pkey_name = pkey_data[0]['Column_name']
sql = "select COLUMN_NAME,DATA_TYPE from information_schema.COLUMNS where `TABLE_SCHEMA`='{}' and `TABLE_NAME` = '{}' and `COLUMN_NAME`='{}';"
sql = sql.format(db,table,pkey_name,)
# print(sql)
fields = self.pdb.query(sql)
if len(fields) == 1:
# print(fields[0]['DATA_TYPE'])
if mw.inArray(['bigint','smallint','tinyint','int','mediumint'], fields[0]['DATA_TYPE']):
key = pkey_name
return key
def getTableFieldStr(self, db, table):
sql = "select COLUMN_NAME,DATA_TYPE from information_schema.COLUMNS where `TABLE_SCHEMA`='{}' and `TABLE_NAME` = '{}';"
sql = sql.format(db,table,)
fields = self.pdb.query(sql)
field_str = ''
for x in range(len(fields)):
field_str += '`'+fields[x]['COLUMN_NAME']+'`,'
field_str = field_str.strip(',')
return field_str
def makeSphinxHeader(self):
conf = '''
indexer
{
mem_limit = 128M
}
searchd
{
listen = 9312
listen = 9306:mysql41
log = {$server_dir}/sphinx/index/searchd.log
query_log = {$server_dir}/sphinx/index/query.log
read_timeout = 5
max_children = 0
pid_file = {$server_dir}/sphinx/index/searchd.pid
seamless_rotate = 1
preopen_indexes = 1
unlink_old = 1
#workers = threads # for RT to work
binlog_path = {$server_dir}/sphinx/index/binlog
}
'''
conf = conf.replace("{$server_dir}", mw.getServerDir())
return conf
def makeSphinxDbSourceRangeSql(self, db, table):
pkey_name = self.getTablePk(db,table)
sql = "SELECT min("+pkey_name+"), max("+pkey_name+") FROM "+table
return sql
def makeSphinxDbSourceQuerySql(self, db, table):
pkey_name = self.getTablePk(db,table)
field_str = self.getTableFieldStr(db,table)
# print(field_str)
if pkey_name == 'id':
sql = "SELECT " + field_str + " FROM " + table + " where id >= $start AND id <= $end"
else:
sql = "SELECT `"+pkey_name+'` as `id`,' + field_str + " FROM " + table + " where "+pkey_name+" >= $start AND "+pkey_name+" <= $end"
return sql
def makeSphinxDbSourceDeltaRange(self, db, table):
pkey_name = self.getTablePk(db,table)
conf = "SELECT (SELECT max_id FROM `{$SPH_TABLE}` where `table`='{$TABLE_NAME}') as min, (SELECT max({$PK_NAME}) FROM {$TABLE_NAME}) as max"
conf = conf.replace("{$DB_NAME}", db)
conf = conf.replace("{$TABLE_NAME}", table)
conf = conf.replace("{$SPH_TABLE}", self.delta)
conf = conf.replace("{$PK_NAME}", pkey_name)
return conf
def makeSphinxDbSourcePost(self, db, table):
pkey_name = self.getTablePk(db,table)
conf = "sql_query_post = UPDATE {$SPH_TABLE} SET max_id=(SELECT MAX({$PK_NAME}) FROM {$TABLE_NAME}) where `table`='{$TABLE_NAME}'"
# conf = "REPLACE INTO {$SPH_TABLE} (`table`,`max_id`) VALUES ('{$TABLE_NAME}',(SELECT MAX({$PK_NAME}) FROM {$TABLE_NAME}))"
conf = conf.replace("{$DB_NAME}", db)
conf = conf.replace("{$TABLE_NAME}", table)
conf = conf.replace("{$SPH_TABLE}", self.delta)
conf = conf.replace("{$PK_NAME}", pkey_name)
return conf
def makeSphinxDbSourceDelta(self, db, table):
conf = '''
source {$DB_NAME}_{$TABLE_NAME}_delta:{$DB_NAME}_{$TABLE_NAME}
{
sql_query_pre = SET NAMES utf8
sql_query_range = {$DELTA_RANGE}
sql_query = {$DELTA_QUERY}
{$DELTA_UPDATE}
{$SPH_FIELD}
}
index {$DB_NAME}_{$TABLE_NAME}_delta:{$DB_NAME}_{$TABLE_NAME}
{
source = {$DB_NAME}_{$TABLE_NAME}_delta
{$PATH_NAME} = {$server_dir}/sphinx/index/db/{$DB_NAME}.{$TABLE_NAME}/delta
html_strip = 1
ngram_len = 1
ngram_chars = U+3000..U+2FA1F
{$SPH_FIELD_INDEX}
}
''';
conf = conf.replace("{$server_dir}", mw.getServerDir())
conf = conf.replace("{$PATH_NAME}", self.pathVerName())
conf = conf.replace("{$DB_NAME}", db)
conf = conf.replace("{$TABLE_NAME}", table)
delta_range = self.makeSphinxDbSourceDeltaRange(db, table)
conf = conf.replace("{$DELTA_RANGE}", delta_range)
delta_query = self.makeSphinxDbSourceQuerySql(db, table)
conf = conf.replace("{$DELTA_QUERY}", delta_query)
delta_update = self.makeSphinxDbSourcePost(db, table)
conf = conf.replace("{$DELTA_UPDATE}", delta_update)
sph_field = self.makeSqlToSphinxTable(db, table)
conf = self.makeSphinxDbFieldRepalce(conf, sph_field)
return conf;
def makeSphinxDbSource(self, db, table, create_sphinx_table = False):
db_info = pSqliteDb('databases').field('username,password').where('name=?', (db,)).find()
port = getDbPort()
conf = '''
source {$DB_NAME}_{$TABLE_NAME}
{
type = mysql
sql_host = 127.0.0.1
sql_user = {$DB_USER}
sql_pass = {$DB_PASS}
sql_db = {$DB_NAME}
sql_port = {$DB_PORT}
sql_query_pre = SET NAMES utf8
{$UPDATE}
sql_query_range = {$DB_RANGE_SQL}
sql_range_step = 1000
sql_query = {$DB_QUERY_SQL}
{$SPH_FIELD}
}
index {$DB_NAME}_{$TABLE_NAME}
{
source = {$DB_NAME}_{$TABLE_NAME}
{$PATH_NAME} = {$server_dir}/sphinx/index/db/{$DB_NAME}.{$TABLE_NAME}/index
ngram_len = 1
ngram_chars = U+3000..U+2FA1F
{$SPH_FIELD_INDEX}
}
'''
conf = conf.replace("{$server_dir}", mw.getServerDir())
conf = conf.replace("{$PATH_NAME}", self.pathVerName())
conf = conf.replace("{$DB_NAME}", db)
conf = conf.replace("{$TABLE_NAME}", table)
conf = conf.replace("{$DB_USER}", db_info['username'])
conf = conf.replace("{$DB_PASS}", db_info['password'])
conf = conf.replace("{$DB_PORT}", port)
range_sql = self.makeSphinxDbSourceRangeSql(db, table)
conf = conf.replace("{$DB_RANGE_SQL}", range_sql)
query_sql = self.makeSphinxDbSourceQuerySql(db, table)
conf = conf.replace("{$DB_QUERY_SQL}", query_sql)
sph_field = self.makeSqlToSphinxTable(db, table)
# conf = conf.replace("{$SPH_FIELD}", sph_field)
conf = self.makeSphinxDbFieldRepalce(conf, sph_field)
if create_sphinx_table:
update = self.makeSphinxDbSourcePost(db, table)
conf = conf.replace("{$UPDATE}", update)
else:
conf = conf.replace("{$UPDATE}", '')
if create_sphinx_table:
sph_sql = self.createSql(db)
self.pdb.query(sph_sql)
sql_find = "select * from {}.{} where `table`='{}'".format(db,self.delta,table)
find_data = self.pdb.query(sql_find)
if len(find_data) == 0:
insert_sql = "insert into `{}`.`{}`(`table`,`max_id`) values ('{}',{}) ".format(db,self.delta,table,0)
# print(insert_sql)
self.pdb.execute(insert_sql)
conf += self.makeSphinxDbSourceDelta(db,table)
# print(ver)
# print(conf)
return conf
def makeSphinxDbFieldRepalce(self, content, sph_field):
ver = self.ver.replace(".1",'')
ver = float(ver)
if ver >= 3.6:
content = content.replace("{$SPH_FIELD}", '')
content = content.replace("{$SPH_FIELD_INDEX}", '')
else:
content = content.replace("{$SPH_FIELD}", sph_field)
content = content.replace("{$SPH_FIELD_INDEX}", '')
return content
def makeSqlToSphinxDb(self, db, table = [], is_delta = False):
conf = ''
for tn in table:
pkey_name = self.getTablePk(db,tn)
if pkey_name == '':
continue
conf += self.makeSphinxDbSource(db, tn,is_delta)
if len(table) == 0:
tables = self.pdb.query("show tables in "+ db)
for x in range(len(tables)):
key = 'Tables_in_'+db
table_name = tables[x][key]
pkey_name = self.getTablePk(db, table_name, is_delta)
if pkey_name == '':
continue
if self.makeSqlToSphinxTableIsHaveFulltext(db, table_name):
conf += self.makeSphinxDbSource(db, table_name)
return conf
def makeSqlToSphinxTableIsHaveFulltext(self, db, table):
sql = "select COLUMN_NAME,DATA_TYPE from information_schema.COLUMNS where `TABLE_SCHEMA`='{}' and `TABLE_NAME` = '{}';"
sql = sql.format(db,table,)
cols = self.pdb.query(sql)
cols_len = len(cols)
for x in range(cols_len):
data_type = cols[x]['DATA_TYPE']
column_name = cols[x]['COLUMN_NAME']
if mw.inArray(['varchar'], data_type):
return True
if mw.inArray(['text','mediumtext','tinytext','longtext'], data_type):
return True
return False
def makeSqlToSphinxTable(self,db,table):
pkey_name = self.getTablePk(db,table)
sql = "select COLUMN_NAME,DATA_TYPE from information_schema.COLUMNS where `TABLE_SCHEMA`='{}' and `TABLE_NAME` = '{}';"
sql = sql.format(db,table,)
cols = self.pdb.query(sql)
cols_len = len(cols)
conf = ''
run_pos = 0
for x in range(cols_len):
data_type = cols[x]['DATA_TYPE']
column_name = cols[x]['COLUMN_NAME']
# print(column_name+":"+data_type)
# if mw.inArray(['tinyint'], data_type):
# conf += 'sql_attr_bool = '+ column_name + "\n"
if pkey_name == column_name:
# run_pos += 1
# conf += '\tsql_attr_bigint = '+column_name+"\n"
continue
if mw.inArray(['enum'], data_type):
run_pos += 1
conf += '\t'+self.eqVerField('sql_attr_string')+' = '+ column_name + "\n"
continue
if mw.inArray(['decimal'], data_type):
run_pos += 1
conf += '\t'+self.eqVerField('sql_attr_float')+' = '+ column_name + "\n"
continue
if mw.inArray(['bigint','smallint','tinyint','int','mediumint'], data_type):
run_pos += 1
conf += '\t'+self.eqVerField('sql_attr_bigint')+' = '+ column_name + "\n"
continue
if mw.inArray(['float'], data_type):
run_pos += 1
conf += '\t'+self.eqVerField('sql_attr_float')+' = '+ column_name + "\n"
continue
if mw.inArray(['char'], data_type):
conf += '\t'+self.eqVerField('sql_attr_string')+' = '+ column_name + "\n"
continue
if mw.inArray(['varchar'], data_type):
run_pos += 1
conf += '\t'+self.eqVerField('sql_field_string')+' = '+ column_name + "\n"
continue
if mw.inArray(['text','mediumtext','tinytext','longtext'], data_type):
run_pos += 1
conf += '\t'+self.eqVerField('sql_field_string')+' = '+ column_name + "\n"
continue
if mw.inArray(['datetime','date'], data_type):
run_pos += 1
conf += '\t'+self.eqVerField('sql_attr_timestamp')+' = '+ column_name + "\n"
continue
return conf
def checkDbName(self, db):
filter_db = ['information_schema','performance_schema','sys','mysql']
if db in filter_db:
return False
return True
def makeSqlToSphinx(self, db, tables = [], is_delta = False):
conf = ''
conf += self.makeSphinxHeader()
conf += self.makeSqlToSphinxDb(db, tables, is_delta)
return conf
def makeSqlToSphinxAll(self):
filter_db = ['information_schema','performance_schema','sys','mysql']
dblist = self.pdb.query('show databases')
conf = ''
conf += self.makeSphinxHeader()
# conf += makeSqlToSphinxDb(pdb, 'bbs')
for x in range(len(dblist)):
dbname = dblist[x]['Database']
if mw.inArray(filter_db, dbname):
continue
conf += self.makeSqlToSphinxDb(dbname)
return conf
+17 -3
View File
@@ -1,21 +1,35 @@
<style>
#table xm-select{
min-height: 30px;
line-height: 30px;
}
#table xm-select *{
font-size: 12px;
}
</style>
<div class="bt-form"> <div class="bt-form">
<div class="bt-w-main"> <div class="bt-w-main">
<div class="bt-w-menu"> <div class="bt-w-menu">
<p class="bgw" onclick="pluginService('sphinx');">服务</p> <p class="bgw" onclick="pluginService('sphinx');">服务</p>
<p onclick="pluginInitD('sphinx');">自启动</p> <p onclick="pluginInitD('sphinx');">自启动</p>
<p onclick="pluginConfigTpl('sphinx');">配置修改</p> <p onclick="pluginConfigTpl('sphinx','', 'conf','config_tpl','read_config_tpl',tryRebuildIndex);">配置修改</p>
<p onclick="pluginLogs('sphinx','','run_log',30);">运行日志</p> <p onclick="pluginLogs('sphinx','','run_log',30);">运行日志</p>
<p onclick="pluginLogs('sphinx','','query_log', 30);">查询日志</p> <p onclick="pluginLogs('sphinx','','query_log', 30);">查询日志</p>
<p onclick="runStatus();">运行状态</p> <p onclick="runStatus();">运行状态</p>
<p onclick="rebuild();">重建索引</p> <p onclick="commonFunc();">常用功能</p>
<p onclick="readme();">说明</p> <p onclick="readme();">说明</p>
</div> </div>
<div class="bt-w-con pd15"> <div class="bt-w-con pd15">
<div class="soft-man-con"></div> <div class="soft-man-con" style="height:425px;overflow: auto;"></div>
</div> </div>
</div> </div>
</div> </div>
<script type="text/javascript"> <script type="text/javascript">
resetPluginWinHeight(500);
$.getScript( "/plugins/file?name=sphinx&f=js/sphinx.js", function(){ $.getScript( "/plugins/file?name=sphinx&f=js/sphinx.js", function(){
pluginService('sphinx'); pluginService('sphinx');
}); });
+84 -16
View File
@@ -15,14 +15,13 @@ app_debug = False
if mw.isAppleSystem(): if mw.isAppleSystem():
app_debug = True app_debug = True
def getPluginName(): def getPluginName():
return 'sphinx' return 'sphinx'
def getPluginDir(): def getPluginDir():
return mw.getPluginDir() + '/' + getPluginName() return mw.getPluginDir() + '/' + getPluginName()
sys.path.append(getPluginDir() +"/class")
def getServerDir(): def getServerDir():
return mw.getServerDir() + '/' + getPluginName() return mw.getServerDir() + '/' + getPluginName()
@@ -210,9 +209,10 @@ def reload():
def rebuild(): def rebuild():
file = initDreplace() file = initDreplace()
subprocess.Popen(file + ' rebuild &', cmd = file + ' rebuild'
stdout=subprocess.PIPE, shell=True) data = mw.execShell(cmd)
# data = mw.execShell(file + ' rebuild') if data[0].find('successfully')<0:
return data[0].replace("\n","<br/>")
return 'ok' return 'ok'
@@ -295,18 +295,35 @@ def sphinxConfParse():
sindex = re.findall(rep, content) sindex = re.findall(rep, content)
indexlen = len(sindex) indexlen = len(sindex)
cmd = {} cmd = {}
if indexlen > 0: cmd['cmd'] = bin_dir + '/bin/bin/indexer -c ' + bin_dir + '/sphinx.conf'
cmd_index = []
cmd_delta = []
for x in range(indexlen):
if sindex[x].find(':') != -1:
cmd_delta.append(sindex[x])
else:
cmd_index.append(sindex[x])
cmd['index'] = cmd_index cmd['index'] = []
cmd['delta'] = cmd_delta cmd_index = []
cmd['cmd'] = bin_dir + '/bin/bin/indexer -c ' + bin_dir + '/sphinx.conf' cmd_delta = []
if indexlen > 0:
for x in range(indexlen):
name = sindex[x].strip()
if name == '':
continue
if name.find(':') != -1:
cmd_delta.append(name.strip())
else:
cmd_index.append(name.strip())
# print(cmd_index)
# print(cmd_delta)
for ci in cmd_index:
val = {}
val['index'] = ci
for cd in cmd_delta:
cd = cd.replace(" ", '')
if cd.find(":"+ci) > -1:
val['delta'] = cd.split(":")[0].strip()
break
cmd['index'].append(val)
return cmd return cmd
@@ -317,6 +334,55 @@ def sphinxCmd():
else: else:
return mw.returnJson(False, 'no index') return mw.returnJson(False, 'no index')
def makeDbToSphinxTest():
conf_file = getConf()
import sphinx_make
sph_make = sphinx_make.sphinxMake()
conf = sph_make.makeSqlToSphinxAll()
mw.writeFile(conf_file,conf)
print(conf)
# makeSqlToSphinxTable()
return True
def makeDbToSphinx():
args = getArgs()
check = checkArgs(args, ['db','tables','is_delta','is_cover'])
if not check[0]:
return check[1]
db = args['db']
tables = args['tables']
is_delta = args['is_delta']
is_cover = args['is_cover']
if is_cover != 'yes':
return mw.returnJson(False,'暂时仅支持覆盖!')
sph_file = getConf()
import sphinx_make
sph_make = sphinx_make.sphinxMake()
version_pl = getServerDir() + "/version.pl"
if os.path.exists(version_pl):
version = mw.readFile(version_pl).strip()
sph_make.setVersion(version)
if not sph_make.checkDbName(db):
return mw.returnJson(False,'保留数据库名称,不可用!')
is_delta_bool = False
if is_delta == 'yes':
is_delta_bool = True
if is_cover == 'yes':
tables = tables.split(',')
content = sph_make.makeSqlToSphinx(db, tables, is_delta_bool)
mw.writeFile(sph_file,content)
return mw.returnJson(True,'设置成功!')
return mw.returnJson(True,'测试中')
if __name__ == "__main__": if __name__ == "__main__":
func = sys.argv[1] func = sys.argv[1]
@@ -352,5 +418,7 @@ if __name__ == "__main__":
print(runStatus()) print(runStatus())
elif func == 'sphinx_cmd': elif func == 'sphinx_cmd':
print(sphinxCmd()) print(sphinxCmd())
elif func == 'db_to_sphinx':
print(makeDbToSphinx())
else: else:
print('error') print('error')
+1 -2
View File
@@ -4,8 +4,7 @@
"name": "sphinx", "name": "sphinx",
"title": "sphinx", "title": "sphinx",
"shell": "install.sh", "shell": "install.sh",
"versions":["3.1.1"], "versions":["3.1.1","3.2.1","3.3.1","3.4.1","3.5.1","3.6.1","3.7.1"],
"updates":["3.1.1"],
"tip": "soft", "tip": "soft",
"checks": "server/sphinx", "checks": "server/sphinx",
"path": "server/sphinx", "path": "server/sphinx",
+1 -1
View File
@@ -33,7 +33,7 @@ start () {
} }
rebuild () { rebuild () {
${APP_PATH}/bin/bin/indexer -c ${APP_CONF} --all --rotate & ${APP_PATH}/bin/bin/indexer -c ${APP_CONF} --all --rotate
} }
+98 -22
View File
@@ -7,45 +7,108 @@ rootPath=$(dirname "$curPath")
rootPath=$(dirname "$rootPath") rootPath=$(dirname "$rootPath")
serverPath=$(dirname "$rootPath") serverPath=$(dirname "$rootPath")
sysName=`uname` sysName=`uname`
sysArch=`arch`
install_tmp=${rootPath}/tmp/mw_install.pl install_tmp=${rootPath}/tmp/mw_install.pl
# cd /www/server/mdserver-web && source bin/activate && python3 plugins/sphinx/index.py rebuild
# cd /www/server/mdserver-web/plugins/sphinx && bash install.sh install 3.6.1
# cd /www/server/mdserver-web && source bin/activate && python3 plugins/sphinx/index.py db_to_sphinx && /www/server/sphinx/bin/bin/indexer -c /www/server/sphinx/sphinx.conf --all --rotate
# /Users/midoks/Desktop/mwdev/server/sphinx/bin/bin/indexer /Users/midoks/Desktop/mwdev/server/sphinx/sphinx.conf --all --rotate
# cd /www/server/mdserver-web && source bin/activate && python3 plugins/sphinx/index.py sphinx_cmd
# /Users/midoks/Desktop/mwdev/server/sphinx/bin/bin/indexer /Users/midoks/Desktop/mwdev/server/sphinx/sphinx.conf --all --rotate
# cd /www/server/mdserver-web && source bin/activate && python3 plugins/sphinx/index.py start
bash ${rootPath}/scripts/getos.sh bash ${rootPath}/scripts/getos.sh
OSNAME=`cat ${rootPath}/data/osname.pl` # echo "bash ${rootPath}/scripts/getos.sh"
OSNAME_ID=`cat /etc/*-release | grep VERSION_ID | awk -F = '{print $2}' | awk -F "\"" '{print $2}'` OSNAME="macos"
if [ -f ${rootPath}/data/osname.pl ];then
OSNAME=`cat ${rootPath}/data/osname.pl`
fi
if [ ${OSNAME} == "centos" ] || if [ "${OSNAME}" == "centos" ] ||
[ ${OSNAME} == "fedora" ] || [ "${OSNAME}" == "fedora" ] ||
[ ${OSNAME} == "alma" ]; then [ "${OSNAME}" == "alma" ]; then
yum install -y postgresql-libs unixODBC yum install -y postgresql-libs unixODBC
fi fi
# http://sphinxsearch.com/files/sphinx-3.7.1-da9f8a4-linux-amd64.tar.gz
VERSION=$2
# echo $VERSION
if [ "$VERSION" == "3.1.1" ];then
VERSION_NUM=${VERSION}-612d99f
elif [ "$VERSION" == "3.2.1" ]; then
VERSION_NUM=${VERSION}-f152e0b
elif [ "$VERSION" == "3.3.1" ]; then
VERSION_NUM=${VERSION}-b72d67b
elif [ "$VERSION" == "3.4.1" ]; then
VERSION_NUM=${VERSION}-efbcc65
elif [ "$VERSION" == "3.5.1" ]; then
VERSION_NUM=${VERSION}-82c60cb
elif [ "$VERSION" == "3.6.1" ]; then
VERSION_NUM=${VERSION}-c9dbeda
elif [ "$VERSION" == "3.7.1" ]; then
VERSION_NUM=${VERSION}-da9f8a4
fi
# echo $VERSION_NUM
VERSION=3.1.1
Install_sphinx() Install_sphinx()
{ {
echo '正在安装Sphinx...' echo '正在安装Sphinx...'
mkdir -p $serverPath/sphinx mkdir -p $serverPath/sphinx
SPHINX_DIR=${serverPath}/source/sphinx SPHINX_DIR=${serverPath}/source/sphinx
mkdir -p $SPHINX_DIR mkdir -p $SPHINX_DIR
if [ ! -f ${SPHINX_DIR}/sphinx-${VERSION}.tar.gz ];then SPH_NAME=amd64
if [ $sysName == 'Darwin' ]; then if [ "$sysArch" == "arm64" ];then
wget -O ${SPHINX_DIR}/sphinx-${VERSION}.tar.gz http://sphinxsearch.com/files/sphinx-${VERSION}-612d99f-darwin-amd64.tar.gz SPH_NAME=amd64
else elif [ "$sysArch" == "x86_64" ]; then
curl -sSLo ${SPHINX_DIR}/sphinx-${VERSION}.tar.gz http://sphinxsearch.com/files/sphinx-${VERSION}-612d99f-linux-amd64.tar.gz SPH_NAME=amd64
elif [ "$sysArch" == "aarch64" ]; then
SPH_NAME=aarch64
fi
if [ "$sysName" == "Darwin" ] && [ "$VERSION" == "3.7.1" ];then
SPH_NAME=aarch64
fi
SPH_SYSNAME=linux
if [ $sysName == 'Darwin' ]; then
SPH_SYSNAME=darwin
elif [ "$sysName" == "aarch64" ]; then
SPH_NAME=aarch64
elif [ "$sysName" == "freebsd" ]; then
SPH_NAME=freebsd
fi
if [ "$SPH_SYSNAME" == "linux" ];then
glibc_ver=`ldd --version | grep libc | awk -F ')' '{print $2}'|awk '{gsub(/^\s+|\s+$/, "");print}'`
if [ "$VERSION" == "3.7.1" ] && [ `echo "2.29 > $glibc_ver " | bc` -eq 1 ];then
SPH_NAME=${SPH_NAME}-glibc2.17
fi
if [ "$VERSION" == "3.6.1" ] && [ `echo "2.29 > $glibc_ver " | bc` -eq 1 ];then
SPH_NAME=${SPH_NAME}-glibc2.17
fi fi
fi fi
if [ ! -f ${SPHINX_DIR}/sphinx-${VERSION}.tar.gz ];then FILE_NAME=sphinx-${VERSION_NUM}-${SPH_SYSNAME}-${SPH_NAME}
curl -sSLo ${SPHINX_DIR}/sphinx-${VERSION}.tar.gz https://github.com/midoks/mdserver-web/releases/download/init/sphinx-${VERSION}.tar.gz FILE_TGZ=${FILE_NAME}.tar.gz
echo $FILE_TGZ
# curl -sSLo ${SPHINX_DIR}/${FILE_TGZ} http://sphinxsearch.com/files/${FILE_TGZ}
if [ ! -f ${SPHINX_DIR}/${FILE_TGZ} ];then
wget --no-check-certificate -O ${SPHINX_DIR}/${FILE_TGZ} http://sphinxsearch.com/files/${FILE_TGZ}
fi fi
cd ${SPHINX_DIR} && tar -zxvf ${FILE_TGZ}
cd ${SPHINX_DIR} && tar -zxvf sphinx-${VERSION}.tar.gz
if [ "$?" == "0" ];then if [ "$?" == "0" ];then
mkdir -p $SPHINX_DIR mkdir -p $SPHINX_DIR
@@ -56,7 +119,10 @@ Install_sphinx()
echo "${VERSION}" > $serverPath/sphinx/version.pl echo "${VERSION}" > $serverPath/sphinx/version.pl
echo '安装Sphinx完成' echo '安装Sphinx完成'
cd ${rootPath} && python3 ${rootPath}/plugins/sphinx/index.py start cd ${rootPath} && python3 ${rootPath}/plugins/sphinx/index.py start
cd ${rootPath} && python3 ${rootPath}/plugins/sphinx/index.py initd_install
if [ $sysName != 'Darwin' ]; then
cd ${rootPath} && python3 ${rootPath}/plugins/sphinx/index.py initd_install
fi
fi fi
if [ -d ${SPHINX_DIR}/sphinx-${VERSION} ];then if [ -d ${SPHINX_DIR}/sphinx-${VERSION} ];then
@@ -69,8 +135,14 @@ Uninstall_sphinx()
if [ -f /usr/lib/systemd/system/sphinx.service ] || [ -f /lib/systemd/system/sphinx.service ];then if [ -f /usr/lib/systemd/system/sphinx.service ] || [ -f /lib/systemd/system/sphinx.service ];then
systemctl stop sphinx systemctl stop sphinx
systemctl disable sphinx systemctl disable sphinx
rm -rf /usr/lib/systemd/system/sphinx.service
rm -rf /lib/systemd/system/sphinx.service if [ -f /usr/lib/systemd/system/sphinx.service ];then
rm -rf /usr/lib/systemd/system/sphinx.service
fi
if [ -f /lib/systemd/system/sphinx.service ];then
rm -rf /lib/systemd/system/sphinx.service
fi
systemctl daemon-reload systemctl daemon-reload
fi fi
@@ -78,8 +150,12 @@ Uninstall_sphinx()
$serverPath/sphinx/initd/sphinx stop $serverPath/sphinx/initd/sphinx stop
fi fi
rm -rf $serverPath/sphinx if [ -d $serverPath/sphinx ];then
echo "Uninstall_sphinx" > $install_tmp echo "rm -rf $serverPath/sphinx"
rm -rf $serverPath/sphinx
fi
echo "卸载sphinx成功"
} }
action=$1 action=$1
+203 -27
View File
@@ -20,6 +20,32 @@ function spPostMin(method, args, callback){
},'json'); },'json');
} }
function myPost(method, args, callback, title){
var _args = null;
if (typeof(args) == 'string'){
_args = JSON.stringify(toArrayObject(args));
} else {
_args = JSON.stringify(args);
}
var _title = '正在获取...';
if (typeof(title) != 'undefined'){
_title = title;
}
$.post('/plugins/run', {name:'mysql', func:method, args:_args}, function(data) {
if (!data.status){
layer.msg(data.msg,{icon:0,time:2000,shade: [0.3, '#000']});
return;
}
if(typeof(callback) == 'function'){
callback(data);
}
},'json');
}
function spPost(method, args, callback){ function spPost(method, args, callback){
var loadT = layer.msg('正在获取...', { icon: 16, time: 0, shade: 0.3 }); var loadT = layer.msg('正在获取...', { icon: 16, time: 0, shade: 0.3 });
spPostMin(method,args,function(data){ spPostMin(method,args,function(data){
@@ -30,21 +56,168 @@ function spPost(method, args, callback){
}); });
} }
function rebuild(){ function commonFunc(){
var con = '<button class="btn btn-default btn-sm" onclick="rebuildIndex();">重建索引</button>'; var con = '<button class="btn btn-default btn-sm" onclick="confirmRebuildIndex();">重建索引</button>';
con += '&nbsp;&nbsp; <button class="btn btn-default btn-sm" onclick="autoMakeConf();">自动创建配置</button>';
$(".soft-man-con").html(con); $(".soft-man-con").html(con);
} }
function autoMakeConf(){
var xm_db_list;
var con = '<ul class="help-info-text c7">';
con += '<li style="color:red;">如果数据量比较大,第一次启动会失败!(可通过手动建立索引)</li>';
con += '<li style="color:red;">以下内容,需手动加入计划任务。</li>';
layer.open({
type: 1,
area: ['380px','350px'],
title: '自动创建配置',
closeBtn: 1,
shift: 5,
shadeClose: true,
btn:["提交","关闭"],
content: "<form class='bt-form pd20'>\
<div class='line'>\
<span class='tname'>选择数据库</span>\
<div class='info-r'>\
<select class='bt-input-text mr5' name='dbname' style='width:100%'>\
<option value=''>无</option>\
</select>\
</div>\
</div>\
<div class='line'>\
<span class='tname'>选择表</span>\
<div class='info-r'>\
<div id='table'></div>\
</div>\
</div>\
<div class='line'>\
<span class='tname'>是否增量</span>\
<div class='info-r'>\
<select class='bt-input-text mr5' name='is_delta' style='width:100px'>\
<option value='no'>否</option>\
<option value='yes'>是</option>\
</select>\
</div>\
</div>\
<div class='line'>\
<span class='tname'>是否覆盖配置</span>\
<div class='info-r'>\
<select class='bt-input-text mr5' name='is_cover' style='width:100px'>\
<option value='yes'>是</option>\
<option value='no'>否</option>\
</select>\
</div>\
</div>\
<ul class='help-info-text c7'>\
<li style='color:red;'>具体配置,仍须手动修改!!!</li>'\
<li style='color:red;'>增量索引,需要有更新权限,主从分离时,需要主库配置</li>'\
</ul>\
</form>\
",
success:function(l,i){
$(l).find('.layui-layer-content').css('overflow','visible');
xm_db_list = xmSelect.render({
el: '#table',
repeat: false,
toolbar: {show: true},
data: [],
});
myPost('get_db_list', {"page":1,"page_size":20}, function(data){
var rdata = $.parseJSON(data.data);
var dblist = rdata.data;
var db_html = '';
for (var i = 0; i < dblist.length; i++) {
db_html += "<option value='"+dblist[i]['name']+"'>"+dblist[i]['name']+"</option>";
}
if (dblist.length > 0){
initDbSelect(dblist[0]['name']);
}
$('select[name="dbname"]').html(db_html);
});
$('select[name="dbname"]').change(function(){
var db = $('select[name="dbname"]').val();
initDbSelect(db);
});
},
yes:function(index){
var args = {}
args['db'] = $('select[name="dbname"]').val();
args['is_delta'] = $('select[name="is_delta"]').val();
args['is_cover'] = $('select[name="is_cover"]').val();
args['tables'] = xm_db_list.getValue('value').join(',');
// console.log(args);
spPost('db_to_sphinx', args, function(rdata){
var rdata = $.parseJSON(rdata.data);
// console.log(rdata);
showMsg(rdata.msg,function(){
if (rdata.status){
layer.close(index);
confirmRebuildIndex();
}
},{icon: rdata.status ? 1 : 2}, 2000);
});
}
});
function initDbSelect(db){
if (db == ''){
return;
}
getDbInfo(db, function(rdata){
var rdata = $.parseJSON(rdata.data);
var tables = rdata.tables;
var idx_db = [];
for (var i = 0; i < tables.length; i++) {
var t = {};
t['name'] = tables[i]['table_name'];
t['value'] = tables[i]['table_name'];
idx_db.push(t);
}
xm_db_list = xmSelect.render({el: '#table', filterable: true,repeat: false,toolbar: {show: true},data: idx_db,});
});
}
function getDbInfo(db_name, callback){
myPost('get_db_info', {name:db_name}, function(data){
callback(data);
});
}
}
function rebuildIndex(){ function rebuildIndex(){
spPost('rebuild', '', function(data){ spPost('rebuild', '', function(data){
if (data.data == 'ok'){ if (data.data == 'ok'){
layer.msg('在重建中..',{icon:1,time:2000,shade: [0.3, '#000']}); layer.msg('重建成功!',{icon:1,time:2000,shade: [0.3, '#000']});
} else { } else {
layer.msg(data.data,{icon:2,time:2000,shade: [0.3, '#000']}); layer.msg(data.data,{icon:2,time:10000,shade: [0.3, '#000']});
} }
}); });
} }
function confirmRebuildIndex(){
layer.confirm("是否重建索引?", {icon:3,closeBtn: 1} , function(){
rebuildIndex();
});
}
function tryRebuildIndex(){
layer.confirm("修改配置后,是否尝试重建索引!", {icon:3,closeBtn: 1} , function(){
rebuildIndex();
});
}
function secToTime(s) { function secToTime(s) {
var t; var t;
if(s > -1){ if(s > -1){
@@ -74,7 +247,12 @@ function runStatus(){
return; return;
} }
var idata = rdata.data; var idata = rdata.data;
// console.log(idata);
var tbody = '';
for (var i in idata) {
tbody += '<tr><th>'+i+'</th><td>' + idata[i] + '</td><td colspan="2">'+i+'</td></tr>';
}
var con = '<div class="divtable"><table class="table table-hover table-bordered" style="margin-bottom:10px;background-color:#fafafa">\ var con = '<div class="divtable"><table class="table table-hover table-bordered" style="margin-bottom:10px;background-color:#fafafa">\
<tbody>\ <tbody>\
<tr><th>运行时间</th><td>' + secToTime(idata.uptime) + '</td><th>每秒查询</th><td>' + parseInt(parseInt(idata.queries) / parseInt(idata.uptime)) + '</td></tr>\ <tr><th>运行时间</th><td>' + secToTime(idata.uptime) + '</td><th>每秒查询</th><td>' + parseInt(parseInt(idata.queries) / parseInt(idata.uptime)) + '</td></tr>\
@@ -84,18 +262,12 @@ function runStatus(){
</tbody>\ </tbody>\
</table>\ </table>\
<table class="table table-hover table-bordered">\ <table class="table table-hover table-bordered">\
<thead style="display:none;"><th></th><th></th><th></th><th></th></thead>\ <thead style="display:none;"><th></th><th></th><th></th><th></th></thead>\
<tbody>\ <tbody>\
<tr><th>command_delete</th><td>' + idata.command_delete + '</td><td colspan="2">command_delete</td></tr>\ '+tbody+'\
<tr><th>command_excerpt</th><td>' + idata.command_excerpt + '</td><td colspan="2">command_excerpt</td></tr>\ <tbody>\
<tr><th>command_flushattrs</th><td>' + idata.command_flushattrs + '</td><td colspan="2">command_flushattrs</td></tr>\ </table>\
<tr><th>command_keywords</th><td>' + idata.command_keywords + '</td><td colspan="2">command_keywords</td></tr>\ </div>';
<tr><th>command_persist</th><td>' + idata.command_persist + '</td><td colspan="2">command_persist</td></tr>\
<tr><th>command_search</th><td>' + idata.command_search + '</td><td colspan="2">command_search</td></tr>\
<tr><th>command_status</th><td>' + idata.command_status + '</td><td colspan="2">command_status</td></tr>\
<tr><th>command_update</th><td>' + idata.command_update + '</td><td colspan="2">command_update</td></tr>\
<tbody>\
</table></div>';
$(".soft-man-con").html(con); $(".soft-man-con").html(con);
}); });
@@ -110,21 +282,25 @@ function readme(){
return; return;
} }
// console.log(rdata['data']);
var con = '<ul class="help-info-text c7">'; var con = '<ul class="help-info-text c7">';
con += '<li style="color:red;">如果数据量比较大,第一次启动会失败!(可通过手动建立索引)</li>'; con += '<li style="color:red;">如果数据量比较大,第一次启动会失败!(可通过手动建立索引)</li>';
con += '<li style="color:red;">以下内容,需手动加入计划任务。</li>';
con += '<li>全量:' + rdata['data']['cmd'] + ' --all --rotate</li>';
//主索引 //主索引
for (var i = 0; i < rdata['data']['index'].length; i++) { for (var i = 0; i < rdata['data']['index'].length; i++) {
var index_t = rdata['data']['index'][i]; var index_kv = rdata['data']['index'][i];
con += '<li>主索引:' + rdata['data']['cmd'] + ' '+ index_t +' --rotate</li>'; var index = index_kv['index'];
} // console.log(index);
con += '<li>主索引 :' + rdata['data']['cmd'] + ' '+ index +' --rotate</li>';
for (var i = 0; i < rdata['data']['delta'].length; i++) { if (typeof(index_kv['delta']) != 'undefined'){
var delta_t = rdata['data']['delta'][i]; var delta = index_kv['delta'];
var list = delta_t.split(':'); con += '<li>增量索引 :' + rdata['data']['cmd'] + ' '+ delta +' --rotate</li>';
// console.log(list); con += '<li>合并索引 :' + rdata['data']['cmd'] + ' --merge '+ index + ' ' + delta +' --rotate</li>';
con += '<li>增量索引:' + rdata['data']['cmd'] + ' '+ list[0] +' --rotate</li>'; }
con += '<li>合并索引:' + rdata['data']['cmd'] + ' --merge '+ list[1] + ' ' + list[0] +' --rotate</li>';
} }
con += '</ul>'; con += '</ul>';
+2 -2
View File
@@ -42,10 +42,10 @@ def send_msg(bot, tag='ad', trigger_time=300):
# 信号只在一个周期内执行一次|end # 信号只在一个周期内执行一次|end
# https://t.me/gjgzs2022 | 22/m | @GJ_gzs # https://t.me/gjgzs2022 | 22/m | @GJ_gzs
# https://zhaoziyuan1.cc | web | 15/m | 2m | next,5/15 | @baleite # https://zhaoziyuan1.cc | web | 15/m | 2m | next,7/15 | @baleite
# 综合包网/NG接口开户 | 28/m | 6m | next,10/28 | @aabbcx888 # 综合包网/NG接口开户 | 28/m | 6m | next,10/28 | @aabbcx888
# 实名认证/过人脸🕵️‍♀️各种账号处理✅ | 30/m| next,6/30 | @nngzs # 实名认证/过人脸🕵️‍♀️各种账号处理✅ | 30/m| next,6/30 | @nngzs
## 海外服务器 高防CDN 解决移动屏蔽 | 19/m | next,4/19 | @YYCDNFW # 海外服务器 高防CDN 解决移动屏蔽 | 群内置顶AD| 16/m | @YYCDNFW
keyboard = [ keyboard = [
[ [
types.InlineKeyboardButton( types.InlineKeyboardButton(
@@ -51,11 +51,9 @@ def send_msg(bot, tag='ad', trigger_time=300):
# 信号只在一个周期内执行一次|end # 信号只在一个周期内执行一次|end
# https://t.me/gjgzs2022 | 22/m | @GJ_gzs # https://t.me/gjgzs2022 | 22/m | @GJ_gzs
# https://zhaoziyuan1.cc | web | 15/m | 2m | next,5/15 | @baleite # https://zhaoziyuan1.cc | web | 15/m | 2m | next,7/15 | @baleite
# 综合包网/NG接口开户 | 28/m | 6m | next,10/28 | @aabbcx888 # 综合包网/NG接口开户 | 28/m | 6m | next,10/28 | @aabbcx888
# 实名认证/过人脸🕵️‍♀️各种账号处理✅ | 30/m| next,6/30 | @nngzs # 实名认证/过人脸🕵️‍♀️各种账号处理✅ | 30/m| next,6/30 | @nngzs
## 海外服务器 高防CDN 解决移动屏蔽 | 19/m | next,4/19 | @YYCDNFW
# 群内置顶AD| 16/m | @YYCDNFW
keyboard = [ keyboard = [
[ [
types.InlineKeyboardButton( types.InlineKeyboardButton(
+2
View File
@@ -31,6 +31,7 @@ CREATE INDEX method_idx ON web_logs (`method`);
CREATE INDEX status_code_idx ON web_logs (`status_code`); CREATE INDEX status_code_idx ON web_logs (`status_code`);
CREATE INDEX request_time_idx ON web_logs (`request_time`); CREATE INDEX request_time_idx ON web_logs (`request_time`);
CREATE INDEX is_spider_idx ON web_logs (`is_spider`); CREATE INDEX is_spider_idx ON web_logs (`is_spider`);
CREATE INDEX body_length_idx ON web_logs (`body_length`);
CREATE INDEX all_union_idx ON web_logs (`time`,`ip`,`method`,`status_code`,`request_time`,`is_spider`); CREATE INDEX all_union_idx ON web_logs (`time`,`ip`,`method`,`status_code`,`request_time`,`is_spider`);
@@ -84,6 +85,7 @@ CREATE TABLE `request_stat`(
`status_402` INTEGER DEFAULT 0, `status_402` INTEGER DEFAULT 0,
`status_403` INTEGER DEFAULT 0, `status_403` INTEGER DEFAULT 0,
`status_404` INTEGER DEFAULT 0, `status_404` INTEGER DEFAULT 0,
`status_499` INTEGER DEFAULT 0,
`http_get` INTEGER DEFAULT 0, `http_get` INTEGER DEFAULT 0,
`http_post` INTEGER DEFAULT 0, `http_post` INTEGER DEFAULT 0,
`http_put` INTEGER DEFAULT 0, `http_put` INTEGER DEFAULT 0,
+2 -2
View File
@@ -86,13 +86,13 @@
.indicators-label { .indicators-label {
display: inline-block; display: inline-block;
width: 95px; width: 88px;
height: 26px; height: 26px;
line-height: 26px; line-height: 26px;
cursor: pointer; cursor: pointer;
} }
.indicators-label input { .indicators-label input[type=radio] {
height: 13px; height: 13px;
margin: 0 5px 0 0; margin: 0 5px 0 0;
vertical-align: -2px; vertical-align: -2px;
+29 -2
View File
@@ -654,17 +654,19 @@ def getLogsRealtimeInfo():
''' '''
import datetime import datetime
args = getArgs() args = getArgs()
check = checkArgs(args, ['site', 'type']) check = checkArgs(args, ['site', 'type','second'])
if not check[0]: if not check[0]:
return check[1] return check[1]
domain = args['site'] domain = args['site']
dtype = args['type'] dtype = args['type']
second = int(args['second'])
conn = pSqliteDb('web_logs', domain) conn = pSqliteDb('web_logs', domain)
timeInt = time.mktime(datetime.datetime.now().timetuple()) timeInt = time.mktime(datetime.datetime.now().timetuple())
conn = conn.where("time>=?", (int(timeInt) - 10,)) conn = conn.where("time>=?", (int(timeInt) - second,))
field = 'time,body_length' field = 'time,body_length'
field_sum = toSumField(field.replace("time,", "")) field_sum = toSumField(field.replace("time,", ""))
@@ -714,6 +716,7 @@ def getLogsList():
method = args['method'] method = args['method']
status_code = args['status_code'] status_code = args['status_code']
request_time = args['request_time'] request_time = args['request_time']
request_size = args['request_size']
spider_type = args['spider_type'] spider_type = args['spider_type']
query_date = args['query_date'] query_date = args['query_date']
search_uri = args['search_uri'] search_uri = args['search_uri']
@@ -752,6 +755,14 @@ def getLogsList():
if len(request_time_s) == 1: if len(request_time_s) == 1:
conn = conn.andWhere("request_time>=?", (request_time,)) conn = conn.andWhere("request_time>=?", (request_time,))
if request_size != "all":
request_size_s = request_size.strip().split('-')
# print(int(request_size_s[0])*1024)
if len(request_size_s) == 2:
conn = conn.andWhere("body_length>=? and body_length<?", (int(request_size_s[0])*1024,int(request_size_s[1])*1024,))
if len(request_size_s) == 1:
conn = conn.andWhere("body_length>=?", (int(request_size_s[0])*1024,))
if spider_type == "normal": if spider_type == "normal":
pass pass
elif spider_type == "only_spider": elif spider_type == "only_spider":
@@ -780,7 +791,23 @@ def getLogsList():
attacHistoryLogHack(conn, domain, query_date) attacHistoryLogHack(conn, domain, query_date)
conn.changeTextFactoryToBytes()
clist = conn.limit(limit).order('time desc').inquiry() clist = conn.limit(limit).order('time desc').inquiry()
for x in range(len(clist)):
req_line = clist[x]
for cx in req_line:
v = req_line[cx]
if type(v) == bytes:
try:
clist[x][cx] = v.decode('utf-8')
except Exception as e:
v = str(v)
v = v.replace("b'",'').strip("'")
clist[x][cx] = v
else:
clist[x][cx] = v
# print(clist) # print(clist)
count_key = "count(*) as num" count_key = "count(*) as num"
count = conn.field(count_key).limit('').order('').inquiry() count = conn.field(count_key).limit('').order('').inquiry()
+95 -32
View File
@@ -192,7 +192,7 @@ function wsOverviewRequest(page){
var data = rdata.data.data; var data = rdata.data.data;
var statData = rdata.data.stat_list; var statData = rdata.data.stat_list;
console.log(statData, data); // console.log(statData, data);
var stat_pv = statData['pv'] == null?0:statData['pv']; var stat_pv = statData['pv'] == null?0:statData['pv'];
var stat_uv = statData['uv'] == null?0:statData['uv']; var stat_uv = statData['uv'] == null?0:statData['uv'];
@@ -383,11 +383,13 @@ function wsOverviewRequest(page){
var select_option = $('.indicators-container input:checked').parent().attr('data-name'); var select_option = $('.indicators-container input:checked').parent().attr('data-name');
if (select_option != "realtime_traffic" && select_option != 'realtime_request' ){ if (select_option != "realtime_traffic" && select_option != 'realtime_request' ){
clearInterval(ovTimer); clearInterval(ovTimer);
console.log("get_logs_realtime_info over:"+select_option); // console.log("get_logs_realtime_info over:"+select_option);
return; return;
} }
wsOriginPost("get_logs_realtime_info",'',{"site":args["site"], "type":select_option} , function(rdata){ var second = $('#check_realtime_second').val();
wsOriginPost("get_logs_realtime_info",'',{"site":args["site"], "type":select_option,'second':second} , function(rdata){
var rdata = $.parseJSON(rdata.data); var rdata = $.parseJSON(rdata.data);
@@ -488,11 +490,11 @@ var html = '<div>\
<p class="ov_num">0</p>\ <p class="ov_num">0</p>\
</div>\ </div>\
<div class="overview_box">\ <div class="overview_box">\
<p class="ov_title">实时流量<i class="tips" data-toggle="tooltip" data-placement="top" title="当前10秒内您网站的实时流量大小。包括已排除的请求。">?</i></p>\ <p class="ov_title">实时流量<i class="tips" data-toggle="tooltip" data-placement="top" title="当前X秒内您网站的实时流量大小。包括已排除的请求。">?</i></p>\
<p class="ov_num">0</p>\ <p class="ov_num">0</p>\
</div>\ </div>\
<div class="overview_box">\ <div class="overview_box">\
<p class="ov_title">每秒请求<i class="tips" data-toggle="tooltip" data-placement="top" title="当前10秒内您网站的实时请求数量。包括已排除的请求。">?</i></p>\ <p class="ov_title"><span id="ov_title_req_second">每秒请求<span><i class="tips" data-toggle="tooltip" data-placement="top" title="当前1-10秒内您网站的实时请求数量。包括已排除的请求。">?</i></p>\
<p class="ov_num">0</p>\ <p class="ov_num">0</p>\
</div>\ </div>\
</div>\ </div>\
@@ -525,7 +527,11 @@ var html = '<div>\
</div>\ </div>\
<div class="indicators-label" bt-event-click="indicatorsType" data-name="realtime_request">\ <div class="indicators-label" bt-event-click="indicatorsType" data-name="realtime_request">\
<input type="radio" id="check_realtime_request" name="check_realtime_request">\ <input type="radio" id="check_realtime_request" name="check_realtime_request">\
<span class="check_realtime_request" style="font-weight:normal">每秒请求</span>\ <span class="check_realtime_request" style="font-weight:normal">每X秒请求</span>\
</div>\
<div class="indicators-label" bt-event-click="indicatorsType">\
<input class="bt-input-text mr5" type="number" id="check_realtime_second" name="check_realtime_second" value="1" style="width:40px;outline:none;height:23px;border-radius:3px;">\
<span style="font-weight:normal">秒</span>\
</div>\ </div>\
</div>\ </div>\
</div>\ </div>\
@@ -592,8 +598,34 @@ $('#search_time button').click(function(){
}); });
$('.indicators-container input').click(function(){ function initRealtimeTraffic(){
$('.indicators-container input').each(function(){ var check_realtime_second = $('#check_realtime_second').val();
if (check_realtime_second<1){
check_realtime_second = 1;
$('#check_realtime_second').val(check_realtime_second);
}
if (check_realtime_second>10){
check_realtime_second = 10;
$('#check_realtime_second').val(check_realtime_second);
}
var title = "每秒请求";
if (check_realtime_second > 1){
title = '每'+check_realtime_second+'秒请求'
}
$('#ov_title_req_second').text(title)
$('.check_realtime_request').text(title);
}
initRealtimeTraffic();
$('#check_realtime_second').change(function(){
initRealtimeTraffic();
});
$('.indicators-container input[type=radio]').click(function(){
$('.indicators-container input[type=radio]').each(function(){
$(this).removeAttr('checked'); $(this).removeAttr('checked');
}); });
$(this).prop({'checked':true}); $(this).prop({'checked':true});
@@ -2081,6 +2113,7 @@ var html = '<div>\
<option value="503">503</option>\ <option value="503">503</option>\
<option value="403">403</option>\ <option value="403">403</option>\
<option value="404">404</option>\ <option value="404">404</option>\
<option value="499">499</option>\
</select>\ </select>\
<span style="margin-left:10px">时间: </span>\ <span style="margin-left:10px">时间: </span>\
<div class="input-group" style="margin-left:10px;width:350px;display: inline-table;vertical-align: top;">\ <div class="input-group" style="margin-left:10px;width:350px;display: inline-table;vertical-align: top;">\
@@ -2179,6 +2212,7 @@ function wsTableLogRequest(page){
args['method'] = $('select[name="method"]').val(); args['method'] = $('select[name="method"]').val();
args['status_code'] = $('select[name="status_code"]').val(); args['status_code'] = $('select[name="status_code"]').val();
args['request_time'] = $('select[name="request_time"]').val(); args['request_time'] = $('select[name="request_time"]').val();
args['request_size'] = $('select[name="request_size"]').val();
args['spider_type'] = $('select[name="spider_type"]').val(); args['spider_type'] = $('select[name="spider_type"]').val();
args['referer'] = $('select[name="referer"]').val(); args['referer'] = $('select[name="referer"]').val();
args['ip'] = $('input[name="ip"]').val(); args['ip'] = $('input[name="ip"]').val();
@@ -2213,11 +2247,23 @@ function wsTableLogRequest(page){
"12":"其他", "12":"其他",
} }
var req_status = $('#logs_search').attr('req');
// console.log(req_status);
if (typeof(req_status) != 'undefined'){
if (req_status == 'start'){
layer.msg("正在请求中,请稍候!");
return;
}
}
$('#logs_search').attr('req','start');
// wsPost('get_logs_list', '' ,args, function(rdata){
wsPostCallbak('get_logs_list', '' ,args, function(rdata){ wsPostCallbak('get_logs_list', '' ,args, function(rdata){
$('#logs_search').attr('req','end');
var rdata = $.parseJSON(rdata.data); var rdata = $.parseJSON(rdata.data);
var list = ''; var list = '';
var data = rdata.data.data; var data = rdata.data.data;
// console.log(data);
if (data.length > 0){ if (data.length > 0){
for(i in data){ for(i in data){
@@ -2267,22 +2313,6 @@ function wsTableLogRequest(page){
$('#ws_table').html(table); $('#ws_table').html(table);
$('#wsPage').html(rdata.data.page); $('#wsPage').html(rdata.data.page);
$('input[name="ip"]').bind('focus', function(e){
$(this).keyup(function(e){
if(e.keyCode == 13) {
wsTableLogRequest(1);
}
});
});
$('input[name="search_uri"]').bind('focus', function(e){
$(this).keyup(function(e){
if(e.keyCode == 13) {
wsTableLogRequest(1);
}
});
});
$(".tablescroll .details").click(function(){ $(".tablescroll .details").click(function(){
var index = $(this).attr('data-id'); var index = $(this).attr('data-id');
var res = data[index]; var res = data[index];
@@ -2318,20 +2348,23 @@ function wsTableLogRequest(page){
var index = $(this).attr('data-id'); var index = $(this).attr('data-id');
var res = data[index]; var res = data[index];
var request_headers = res.request_headers; var request_headers = res.request_headers;
var req_data = $.parseJSON(request_headers);
var req_data_html = res.method +' ' + res.uri + '<br/>'; var req_data_html = res.method +' ' + res.uri + '<br/>';
for (var d in req_data) {
if (d == 'payload'){ try {
req_data_html += '<b style="color:red;">'+d +"</b>:"+req_data[d]+"<br/>"; var req_data = $.parseJSON(request_headers);
} else{ for (var d in req_data) {
req_data_html += d+":"+req_data[d]+"<br/>"; if (d == 'payload'){
req_data_html += '<b style="color:red;">'+d +"</b>:"+req_data[d]+"<br/>";
} else{
req_data_html += d+":"+req_data[d]+"<br/>";
}
} }
} catch (error) {
req_data_html += request_headers;
} }
layer.open({ layer.open({
type: 1, type: 1,
title: "【"+res.domain + "】HTTP详情", title: "【"+res.domain + "】HTTP详情",
@@ -2388,6 +2421,7 @@ var html = '<div>\
<option value="500">500</option>\ <option value="500">500</option>\
<option value="502">502</option>\ <option value="502">502</option>\
<option value="503">503</option>\ <option value="503">503</option>\
<option value="499">499</option>\
<option value="404">404</option>\ <option value="404">404</option>\
<option value="301">301</option>\ <option value="301">301</option>\
<option value="302">302</option>\ <option value="302">302</option>\
@@ -2432,6 +2466,15 @@ var html = '<div>\
<option value="500-1000">500ms-1s</option>\ <option value="500-1000">500ms-1s</option>\
<option value="1000">大于1s</option>\ <option value="1000">大于1s</option>\
</select>\ </select>\
<span style="margin-left:10px;">大小: </span>\
<select class="bt-input-text" name="request_size" style="margin-left:5px;">\
<option value="all">所有</option>\
<option value="0-1">0-1(kb)</option>\
<option value="1-20">1-20(kb)</option>\
<option value="20-50">20-50(kb)</option>\
<option value="50-100">50-100(kb)</option>\
<option value="100">大于100kb</option>\
</select>\
<span style="margin-left:10px;">URL过滤: </span>\ <span style="margin-left:10px;">URL过滤: </span>\
<div class="input-group" style="width:210px;display:inline-flex;">\ <div class="input-group" style="width:210px;display:inline-flex;">\
<input type="text" name="search_uri" class="form-control btn-group-sm" autocomplete="off" placeholder="URI搜索" style="font-size: 12px;padding: 0 10px;height:30px;">\ <input type="text" name="search_uri" class="form-control btn-group-sm" autocomplete="off" placeholder="URI搜索" style="font-size: 12px;padding: 0 10px;height:30px;">\
@@ -2444,6 +2487,22 @@ var html = '<div>\
</div>'; </div>';
$(".soft-man-con").html(html); $(".soft-man-con").html(html);
$('input[name="ip"]').bind('focus', function(e){
$(this).keyup(function(e){
if(e.keyCode == 13) {
wsTableLogRequest(1);
}
});
});
$('input[name="search_uri"]').bind('focus', function(e){
$(this).keyup(function(e){
if(e.keyCode == 13) {
wsTableLogRequest(1);
}
});
});
//日期范围 //日期范围
laydate.render({ laydate.render({
elem: '#time_choose', elem: '#time_choose',
@@ -2507,6 +2566,10 @@ $('select[name="request_time"]').change(function(){
wsTableLogRequest(1); wsTableLogRequest(1);
}); });
$('select[name="request_size"]').change(function(){
wsTableLogRequest(1);
});
$('#logs_search').click(function(){ $('#logs_search').click(function(){
wsTableLogRequest(1); wsTableLogRequest(1);
}); });
+25
View File
@@ -616,7 +616,32 @@ function _M.statistics_request(self, ip, is_spider, body_length)
-- 计算pv uv -- 计算pv uv
local pvc = 0 local pvc = 0
local uvc = 0 local uvc = 0
local request_header = ngx.req.get_headers()
if not is_spider and ngx.status == 200 and body_length > 0 then
local ua = ''
if request_header['user-agent'] then
ua = string.lower(request_header['user-agent'])
end
pvc = 1
if ua then
local today = ngx.today()
local uv_token = ngx.md5(ip .. ua .. today)
if not cache:get(uv_token) then
uvc = 1
cache:set(uv_token,1, self:get_end_time())
end
end
end
return pvc, uvc
end
-- 仅计算GET/HTML
function _M.statistics_request_old(self, ip, is_spider, body_length)
-- 计算pv uv
local pvc = 0
local uvc = 0
local method = ngx.req.get_method()
if not is_spider and method == 'GET' and ngx.status == 200 and body_length > 512 then if not is_spider and method == 'GET' and ngx.status == 200 and body_length > 512 then
local ua = '' local ua = ''
if request_header['user-agent'] then if request_header['user-agent'] then
+2 -2
View File
@@ -260,7 +260,7 @@ log_by_lua_block {
end end
end end
if ngx.re.find("500,501,502,503,504,505,506,507,509,510,400,401,402,403,404,405,406,407,408,409,410,411,412,413,414,415,416,417,418,421,422,423,424,425,426,449,451", tostring(status_code), "jo") then if ngx.re.find("500,501,502,503,504,505,506,507,509,510,400,401,402,403,404,405,406,407,408,409,410,411,412,413,414,415,416,417,418,421,422,423,424,425,426,449,451,499", tostring(status_code), "jo") then
local field = "status_"..status_code local field = "status_"..status_code
request_stat_fields = request_stat_fields .. ","..field.."="..field.."+1" request_stat_fields = request_stat_fields .. ","..field.."="..field.."+1"
end end
@@ -392,7 +392,7 @@ log_by_lua_block {
end end
end end
if ngx.re.find("500,501,502,503,504,505,506,507,509,510,400,401,402,403,404,405,406,407,408,409,410,411,412,413,414,415,416,417,418,421,422,423,424,425,426,449,451", tostring(status_code), "jo") then if ngx.re.find("500,501,502,503,504,505,506,507,509,510,400,401,402,403,404,405,406,407,408,409,410,411,412,413,414,415,416,417,418,421,422,423,424,425,426,449,451,499", tostring(status_code), "jo") then
local field = "status_"..status_code local field = "status_"..status_code
request_stat_fields[field] = 1 request_stat_fields[field] = 1
end end
+27 -4
View File
@@ -705,7 +705,7 @@ def get_logs_list(args):
start_time = time.time() start_time = time.time()
check = checkArgs(args, ['page', 'page_size','site', 'method', check = checkArgs(args, ['page', 'page_size','site', 'method',
'status_code', 'spider_type', 'request_time', 'query_date', 'search_uri']) 'status_code', 'spider_type', 'request_time', 'request_size', 'query_date', 'search_uri'])
if not check[0]: if not check[0]:
return check[1] return check[1]
@@ -716,6 +716,7 @@ def get_logs_list(args):
method = args['method'] method = args['method']
status_code = args['status_code'] status_code = args['status_code']
request_time = args['request_time'] request_time = args['request_time']
request_size = args['request_size']
spider_type = args['spider_type'] spider_type = args['spider_type']
query_date = args['query_date'] query_date = args['query_date']
search_uri = args['search_uri'] search_uri = args['search_uri']
@@ -726,7 +727,7 @@ def get_logs_list(args):
limit = str(page_size) + ' offset ' + str(page_size * (page - 1)) limit = str(page_size) + ' offset ' + str(page_size * (page - 1))
conn = pSqliteDb('web_logs', domain) conn = pSqliteDb('web_logs', domain)
field = 'time,ip,domain,server_name,method,is_spider,protocol,status_code,request_headers,ip_list,client_port,body_length,user_agent,referer,request_time,uri,body_length' field = 'time,ip,domain,server_name,method,is_spider,protocol,status_code,request_headers,ip_list,client_port,body_length,user_agent,referer,request_time,uri'
condition = '' condition = ''
conn = conn.field(field) conn = conn.field(field)
conn = conn.where("1=1", ()) conn = conn.where("1=1", ())
@@ -762,6 +763,14 @@ def get_logs_list(args):
if len(request_time_s) == 1: if len(request_time_s) == 1:
conn = conn.andWhere("request_time>=?", (request_time,)) conn = conn.andWhere("request_time>=?", (request_time,))
if request_size != "all":
request_size_s = request_size.strip().split('-')
# print(int(request_size_s[0])*1024)
if len(request_size_s) == 2:
conn = conn.andWhere("body_length>=? and body_length<?", (int(request_size_s[0])*1024,int(request_size_s[1])*1024,))
if len(request_size_s) == 1:
conn = conn.andWhere("body_length>=?", (int(request_size_s[0])*1024,))
if spider_type == "normal": if spider_type == "normal":
pass pass
elif spider_type == "only_spider": elif spider_type == "only_spider":
@@ -782,8 +791,22 @@ def get_logs_list(args):
attacHistoryLogHack(conn, domain, query_date) attacHistoryLogHack(conn, domain, query_date)
conn.changeTextFactoryToBytes()
clist = conn.limit(limit).order('time desc').inquiry() clist = conn.limit(limit).order('time desc').inquiry()
# print(clist) for x in range(len(clist)):
req_line = clist[x]
for cx in req_line:
v = req_line[cx]
if type(v) == bytes:
try:
clist[x][cx] = v.decode('utf-8')
except Exception as e:
v = str(v)
v = v.replace("b'",'').strip("'")
clist[x][cx] = v
else:
clist[x][cx] = v
count_key = "count(*) as num" count_key = "count(*) as num"
count = conn.field(count_key).limit('').order('').inquiry() count = conn.field(count_key).limit('').order('').inquiry()
# print(count) # print(count)
@@ -824,7 +847,7 @@ def getLogsErrorList():
limit = str(page_size) + ' offset ' + str(page_size * (page - 1)) limit = str(page_size) + ' offset ' + str(page_size * (page - 1))
conn = pSqliteDb('web_logs', domain) conn = pSqliteDb('web_logs', domain)
field = 'time,ip,domain,server_name,method,protocol,status_code,ip_list,client_port,body_length,user_agent,referer,request_time,uri,body_length' field = 'time,ip,domain,server_name,method,protocol,status_code,ip_list,client_port,body_length,user_agent,referer,request_time,uri'
conn = conn.field(field) conn = conn.field(field)
conn = conn.where("1=1", ()) conn = conn.where("1=1", ())
+2 -2
View File
@@ -1,7 +1,7 @@
setuptools>=33.1.1 setuptools>=33.1.1
Werkzeug>=1.0.1,<3.0.0 Werkzeug>=1.0.1,<3.0.0
wheel>=0.37.1 wheel>=0.37.1
gunicorn==22.0.0 gunicorn>=21.2.0
requests>=2.27.1 requests>=2.27.1
urllib3>=1.21.1 urllib3>=1.21.1
flask>=2.0.3,<3.0.0 flask>=2.0.3,<3.0.0
@@ -29,7 +29,7 @@ pymemcache
redis redis
pillow pillow
Jinja2>=2.11.2 Jinja2>=2.11.2
PyMySQL==1.0.2 PyMySQL>=1.0.2
whitenoise==5.3.0 whitenoise==5.3.0
pyotp pyotp
pytz pytz
+1 -5
View File
@@ -60,8 +60,7 @@ try:
sdb.create_all() sdb.create_all()
except: except:
app.config['SESSION_TYPE'] = 'filesystem' app.config['SESSION_TYPE'] = 'filesystem'
app.config['SESSION_FILE_DIR'] = '/tmp/py_mw_session_' + \ app.config['SESSION_FILE_DIR'] = '/tmp/py_mw_session_' + str(sys.version_info[0])
str(sys.version_info[0])
app.config['SESSION_FILE_THRESHOLD'] = 1024 app.config['SESSION_FILE_THRESHOLD'] = 1024
app.config['SESSION_FILE_MODE'] = 384 app.config['SESSION_FILE_MODE'] = 384
@@ -296,9 +295,6 @@ def code():
out = io.BytesIO() out = io.BytesIO()
codeImage[0].save(out, "png") codeImage[0].save(out, "png")
# print(codeImage[1])
session['code'] = mw.md5(''.join(codeImage[1]).lower()) session['code'] = mw.md5(''.join(codeImage[1]).lower())
img = Response(out.getvalue(), headers={'Content-Type': 'image/png'}) img = Response(out.getvalue(), headers={'Content-Type': 'image/png'})
+222 -58
View File
@@ -75,16 +75,106 @@ $('input[name="bind_domain"]').change(function(){
$('.btn_bind_domain').removeAttr('disabled'); $('.btn_bind_domain').removeAttr('disabled');
$('.btn_bind_domain').unbind().click(function(){ $('.btn_bind_domain').unbind().click(function(){
$.post('/config/set_panel_domain','domain='+domain, function(rdata){ $.post('/config/set_panel_domain','domain='+domain, function(rdata){
showMsg(rdata.msg,function(){window.location.reload();},{icon:rdata.status?1:2},2000); showMsg(rdata.msg,function(){
window.location.href = rdata.data;
},{icon:rdata.status?1:2},5000);
},'json'); },'json');
}); });
}); });
$('input[name="bind_ssl"]').click(function(){ $('input[name="bind_ssl"]').click(function(){
var open_ssl = $(this).prop("checked"); var panel_ssl = $(this).prop("checked");
$.post('/config/set_panel_ssl',{}, function(rdata){ $(this).prop("checked",!panel_ssl);
showMsg(rdata.msg,function(){window.location.reload();},{icon:rdata.status?1:2},2000);
},'json'); //开启证书
if (panel_ssl){
// <option value="1">ACME</option>
layer.open({
type:1,
closeBtn: 1,
title:"开启SSL证书",
area: ['600px','440px'],
btn: ["开启SSL证书访问"],
maxmin:false,
shadeClose: true,
content: '<div class="bt-form" style="padding: 25px 40px;">\
<div style="text-align: center;">\
<h3 style="font-size: 20px;color: #333;margin-left: 5px;">【开启SSL证书】保护面板访问安全</h3>\
</div>\
<ul class="help-info-text c7 pd15" style="color: #333;font-size: 14px;background: #F5F7FA;margin-top: 24px;padding: 10px 20px 10px 20px;border-radius: 2px;">\
<li>自签证书访问步骤:</li>\
<li>1. 部署SSL证书</li>\
<li>2. 浏览器地址栏修改为https://访问</li>\
<li>3. 如提醒风险(正常现象)点击【高级】或【详情】</li>\
<li>4.【继续访问】或【接收风险并继续】</li>\
</ul>\
<div class="pt10" style="margin-top: 20px;">\
<div class="line" style="font-size: 14px;">\
<span class="tname" style="width: 78px;">类型</span>\
<div class="info-r" style="margin-left: 78px;">\
<select class="bt-input-text mr5" name="cert_type" style="width: 440px;">\
<option value="0">自签证书 (推荐,浏览器会提示不安全。可忽略,请放心开启)</option>\
</select>\
</div>\
</div>\
<ul class="help-info-text c7 sslSafeTips">\
<li><span>开启后导致面板不能访问,可以点击查看</span></li>\
<li>自签证书不被浏览器信任,显示不安全是正常现象</li>\
</ul>\
</div>\
</div>',
yes: function(){
var cert_type = $('select[name=cert_type]').val();
$.post('/config/set_panel_local_ssl',{'cert_type':cert_type}, function(rdata){
// console.log(rdata);
var to_https = window.location.href.replace('http','https');
showMsg(rdata.msg,function(){
if (rdata.status){
window.location.href = to_https;
}
},{icon:rdata.status?1:2},5000);
},'json');
}
});
} else {
//关闭SSL
layer.open({
type:1,
closeBtn: 1,
title:"关闭SSL证书",
area: ['480px','280px'],
btn: ["确定","取消"],
shadeClose: true,
content: '<div class="bt-form" style="padding: 25px 40px;">\
<div class="hint_title" style="font-size: 15px;color: #111;text-align:center;">\
<div class="hint_con">关闭SSL极易被抓包攻击导致账号密码泄露,请勿关闭</div>\
</div>\
<div class="confirm-info-box" style="background-color: #f0f0f0;clear: both;font-size: 14px;height: 105px;line-height: 26px;padding: 20px 20px;margin-top: 20px;">\
<div>请手动输入【<span style="color: #fc6d26;">我要关闭</span>】,完成验证</div>\
<input onpaste="return false;" id="prompt_input_box" style="height: 30px;line-height: 30px;margin-top: 5px;width: 360px;color: #444;outline: none;border: 1px solid #ccc;padding: 0 5px;" type="text" value="" autocomplete="off">\
</div>\
</div>',
yes: function(index){
var val = $('#prompt_input_box').val();
if (val != '我要关闭'){
layer.msg("关闭SSL失败!");
return;
}
$.post('/config/close_panel_ssl',{}, function(rdata){
var to_http = window.location.href.replace('https','http');
showMsg(rdata.msg,function(){
if (rdata.status){
window.location.href = to_http;
}
},{icon:rdata.status?1:2},5000);
},'json');
}
});
}
}); });
/** op **/ /** op **/
@@ -714,8 +804,30 @@ function getPanelSSL(){
$.post('/config/get_panel_ssl',{},function(cert){ $.post('/config/get_panel_ssl',{},function(cert){
layer.close(loadT); layer.close(loadT);
// console.log(cert);
var choose = cert['choose'];
var choose_local = '';
var choose_nginx = '';
if (choose == 'local'){
cert = cert['local'];
choose_local = 'selected="selected"';
} else if (choose == 'nginx') {
cert = cert['nginx'];
choose_nginx = 'selected="selected"';
} else {
cert = cert['local'];
}
var cert_data = ''; var cert_data = '';
// <div class='state_item'>\
// <span>强制HTTPS:</span>\
// <span class='switch'>\
// <input class='btswitch btswitch-ios' id='toHttps' type='checkbox' "+cert['is_https']+">\
// <label class='btswitch-btn set_panel_http_to_https' for='toHttps'></label>\
// </span>\
// </div>\
if (cert['info']){ if (cert['info']){
cert_data = "<div class='ssl_state_info'><div class='state_info_flex'>\ cert_data = "<div class='ssl_state_info'><div class='state_info_flex'>\
<div class='state_item'><span>证书品牌:</span>\ <div class='state_item'><span>证书品牌:</span>\
@@ -726,16 +838,11 @@ function getPanelSSL(){
<div class='state_info_flex'>\ <div class='state_info_flex'>\
<div class='state_item'><span>认证域名:</span>\ <div class='state_item'><span>认证域名:</span>\
<span class='ellipsis_text ssl_subject'>"+cert['info']['subject']+"</span></div>\ <span class='ellipsis_text ssl_subject'>"+cert['info']['subject']+"</span></div>\
<div class='state_item'>\
<span>强制HTTPS:</span>\
<span class='switch'>\
<input class='btswitch btswitch-ios' id='toHttps' type='checkbox' "+cert['is_https']+">\
<label class='btswitch-btn set_panel_http_to_https' for='toHttps'></label>\
</span>\
</div>\
</div></div>"; </div></div>";
} }
// <button class="btn btn-success btn-sm apply-lets-ssl">申请ACME证书</button>\
// <option value="nginx" '+choose_nginx+'>OpenResty</option>\
var certBody = '<div class="tab-con">\ var certBody = '<div class="tab-con">\
<div class="myKeyCon ptb15">\ <div class="myKeyCon ptb15">\
'+cert_data+'\ '+cert_data+'\
@@ -750,7 +857,9 @@ function getPanelSSL(){
<div class="ssl-btn pull-left mtb15" style="width:100%">\ <div class="ssl-btn pull-left mtb15" style="width:100%">\
<button class="btn btn-success btn-sm save-panel-ssl">保存</button>\ <button class="btn btn-success btn-sm save-panel-ssl">保存</button>\
<button class="btn btn-success btn-sm del-panel-ssl">删除</button>\ <button class="btn btn-success btn-sm del-panel-ssl">删除</button>\
<button class="btn btn-success btn-sm apply-lets-ssl">申请Lets证书</button>\ <select class="bt-input-text" name="choose" style="width:100px;">\
<option value="local" '+choose_local+'>本地</option>\
</select>\
</div>\ </div>\
</div>\ </div>\
<ul class="help-info-text c7 pull-left">\ <ul class="help-info-text c7 pull-left">\
@@ -768,65 +877,120 @@ function getPanelSSL(){
content:certBody, content:certBody,
success:function(layero, layer_id){ success:function(layero, layer_id){
//保存SSL //保存SSL
$('.save-panel-ssl').click(function(){ $('.save-panel-ssl').click(function(){
var data = { var data = {
privateKey:$("#key").val(), privateKey:$("#key").val(),
certPem:$("#csr").val() certPem:$("#csr").val()
} }
var loadT = layer.msg('正在安装并设置SSL组件,这需要几分钟时间...',{icon:16,time:0,shade: [0.3, '#000']});
$.post('/config/save_panel_ssl',data,function(rdata){ layer.confirm('选择保存面板SSL方式?',
layer.close(loadT); {
if(rdata.status){ title:'提示',
layer.closeAll(); shade:0.001,
} btn: ['本地SSL', '取消'],//'OpenResty'
layer.msg(rdata.msg,{icon:rdata.status?1:2}); btn3:function(){
},'json'); data['choose'] = 'nginx';
var loadT = layer.msg('正在安装并设置SSL组件,这需要几分钟时间...',{icon:16,time:0,shade: [0.3, '#000']});
$.post('/config/save_panel_ssl',data,function(rdata){
layer.close(loadT);
if(rdata.status){
layer.closeAll();
}
layer.msg(rdata.msg,{icon:rdata.status?1:2});
},'json');
},
},
function(index) {
data['choose'] = 'local';
var loadT = layer.msg('正在安装并设置SSL组件,这需要几分钟时间...',{icon:16,time:0,shade: [0.3, '#000']});
$.post('/config/save_panel_ssl',data,function(rdata){
layer.close(loadT);
if(rdata.status){
layer.closeAll();
}
layer.msg(rdata.msg,{icon:rdata.status?1:2});
},'json');
},
function(index) {
layer.close(index);
});
}); });
//删除SSL //删除SSL
$('.del-panel-ssl').click(function(){ $('.del-panel-ssl').click(function(){
var loadT = layer.msg('正在删除SSL...',{icon:16,time:0,shade: [0.3, '#000']});
$.post('/config/del_panel_ssl',data,function(rdata){
layer.close(loadT);
if(rdata.status){
layer.closeAll();
}
layer.msg(rdata.msg,{icon:rdata.status?1:2});
},'json');
});
// 设置面板SSL的Http layer.confirm('选择删除面板SSL方式?',
$('.set_panel_http_to_https').click(function(){ {
var https = $('#toHttps').prop('checked'); title:'提示',
$.post('/config/set_panel_http_to_https',{'https':https},function(rdata){ shade:0.001,
layer.close(loadT); btn: ['本地SSL', '取消'],//, 'OpenResty'
if(rdata.status){ btn3:function(){
layer.closeAll(); var data = {};
} data['choose'] = 'nginx';
layer.msg(rdata.msg,{icon:rdata.status?1:2}); var loadT = layer.msg('正在删除面板SSL【nginx】...',{icon:16,time:0,shade: [0.3, '#000']});
},'json'); $.post('/config/del_panel_ssl',data,function(rdata){
}); layer.close(loadT);
if(rdata.status){
//申请Lets证书 layer.closeAll();
$('.apply-lets-ssl').click(function(){ }
showSpeedWindow('正在申请...', 'site.get_let_logs', function(layers,index){ layer.msg(rdata.msg,{icon:rdata.status?1:2});
$.post('/config/apply_panel_let_ssl',{},function(rdata){ },'json');
},
},
function(index) {
var data = {};
data['choose'] = 'local';
var loadT = layer.msg('正在删除面板SSL【本地】...',{icon:16,time:0,shade: [0.3, '#000']});
$.post('/config/del_panel_ssl',data,function(rdata){
console.log(rdata);
layer.close(loadT); layer.close(loadT);
if(rdata.status){ showMsg(rdata.msg, function(){
layer.close(index); if(rdata.status){
var tdata = rdata['data']; location.href = rdata.data;
$('.ssl_issue').text(tdata['info']['issuer']); }
$('.ssl_endtime').text("剩余"+tdata['info']['endtime']+"天到期"); },{icon:rdata.status?1:2},3000);
$('.ssl_subject').text(tdata['info']['subject']);
$('textarea[name="key"]').val(tdata['info']['privateKey']);
$('textarea[name="csr"]').val(tdata['info']['certPem']);
}
layer.msg(rdata.msg,{icon:rdata.status?1:2});
},'json'); },'json');
}); },
function(index) {
layer.close(index);
});
}); });
// // 设置面板SSL的Http
// $('.set_panel_http_to_https').click(function(){
// var https = $('#toHttps').prop('checked');
// $.post('/config/set_panel_http_to_https',{'https':https},function(rdata){
// layer.close(loadT);
// if(rdata.status){
// layer.closeAll();
// }
// layer.msg(rdata.msg,{icon:rdata.status?1:2});
// },'json');
// });
// //申请Lets证书
// $('.apply-lets-ssl').click(function(){
// showSpeedWindow('正在申请...', 'site.get_let_logs', function(layers,index){
// $.post('/config/apply_panel_acme_ssl',{},function(rdata){
// layer.close(loadT);
// if(rdata.status){
// layer.close(index);
// var tdata = rdata['data'];
// $('.ssl_issue').text(tdata['info']['issuer']);
// $('.ssl_endtime').text("剩余"+tdata['info']['endtime']+"天到期");
// $('.ssl_subject').text(tdata['info']['subject']);
// $('textarea[name="key"]').val(tdata['info']['privateKey']);
// $('textarea[name="csr"]').val(tdata['info']['certPem']);
// }
// layer.msg(rdata.msg,{icon:rdata.status?1:2});
// },'json');
// });
// });
} }
}); });
},'json'); },'json');
+19 -5
View File
@@ -294,7 +294,7 @@ function openFilename(obj){
onlineEditFile(0, path); onlineEditFile(0, path);
} }
if (inArray(ext,['png','jpeg','gif','jpg','ico'])){ if (inArray(ext,['png','jpeg','jpg','gif','webp','bmp','ico'])){
getImage(path); getImage(path);
} }
@@ -1592,6 +1592,22 @@ function setChmod(action,fileName){
var toExec = fileName == lan.files.all?'batch(3,1)':'setChmod(1,\''+fileName+'\')'; var toExec = fileName == lan.files.all?'batch(3,1)':'setChmod(1,\''+fileName+'\')';
$.post('/files/file_access','filename='+encodeURIComponent(fileName),function(rdata){ $.post('/files/file_access','filename='+encodeURIComponent(fileName),function(rdata){
// console.log(rdata); // console.log(rdata);
var sys_users = rdata.sys_users;
var own_html = '';
var is_find_own_option = false;
for (var i = 0; i < sys_users.length; i++) {
var own = sys_users[i];
if (rdata.chown==own){
is_find_own_option = true;
own_html += '<option value="'+own+'" selected="selected">'+own+'</option>';
} else {
own_html += '<option value="'+own+'">'+own+'</option>';
}
}
if (!is_find_own_option){
own_html += '<option value="'+rdata.chown+'" selected="selected">'+rdata.chown+'</option>';
}
layer.open({ layer.open({
type:1, type:1,
closeBtn: 1, closeBtn: 1,
@@ -1619,10 +1635,8 @@ function setChmod(action,fileName){
</fieldset>\ </fieldset>\
<div class="setchmodnum"><input class="bt-input-text" type="text" id="access" maxlength="3" value="'+rdata.chmod+'">权限,\ <div class="setchmodnum"><input class="bt-input-text" type="text" id="access" maxlength="3" value="'+rdata.chmod+'">权限,\
<span>所有者\ <span>所有者\
<select id="chown" class="bt-input-text">\ <select id="chown" class="bt-input-text" style="width:100px;">\
<option value="www" '+(rdata.chown=='www'?'selected="selected"':'')+'>www</option>\ '+own_html+'\
<option value="mysql" '+(rdata.chown=='mysql'?'selected="selected"':'')+'>mysql</option>\
<option value="root" '+(rdata.chown=='root'?'selected="selected"':'')+'>root</option>\
</select></span></div>\ </select></span></div>\
<div class="bt-form-submit-btn">\ <div class="bt-form-submit-btn">\
<button type="button" class="btn btn-danger btn-sm btn-title" onclick="layer.closeAll()">关闭</button>\ <button type="button" class="btn btn-danger btn-sm btn-title" onclick="layer.closeAll()">关闭</button>\
+3 -3
View File
@@ -1408,8 +1408,8 @@ var index = {
$("#readBytes").html(toSize(iostat_select.read_bytes)); $("#readBytes").html(toSize(iostat_select.read_bytes));
$("#writeBytes").html(toSize(iostat_select.write_bytes)); $("#writeBytes").html(toSize(iostat_select.write_bytes));
$("#diskIops").html(iostat_select.read_count); $("#diskIops").html(iostat_select.read_count+":"+iostat_select.write_count+ " 次");
$("#diskTime").html(iostat_select.write_time); $("#diskTime").html(iostat_select.read_time+":"+iostat_select.write_time +" ms");
$("#core").html(net.cpu[1] + " " + lan.index.cpu_core); $("#core").html(net.cpu[1] + " " + lan.index.cpu_core);
@@ -1427,7 +1427,7 @@ var index = {
},'json'); },'json');
}, },
task:function(){ task:function(){
index.getData(); // index.getData();
setInterval(function() { setInterval(function() {
index.getData(); index.getData();
}, 3000); }, 3000);
+25 -19
View File
@@ -1000,7 +1000,7 @@ function setSelectChecked(c, d) {
function jump() { function jump() {
layer.closeAll(); layer.closeAll();
window.location.href = "/soft" window.location.href = "/soft";
} }
function installTips() { function installTips() {
@@ -1134,15 +1134,15 @@ function getPanelList(){
var user = $(this).attr("data-user"); var user = $(this).attr("data-user");
var pw = $(this).attr("data-pw"); var pw = $(this).attr("data-pw");
layer.open({ layer.open({
type: 2, type: 2,
title: false, title: false,
closeBtn: 0, //不显示关闭按钮 closeBtn: 0, //不显示关闭按钮
shade: [0], shade: [0],
area: ['340px', '215px'], area: ['340px', '215px'],
offset: 'rb', //右下角弹出 offset: 'rb', //右下角弹出
time: 5, //2秒后自动关闭 time: 5, //2秒后自动关闭
anim: 2, anim: 2,
content: [murl+'/login', 'no'] content: [murl+'/login', 'no']
}); });
var loginForm ='<div id="btpanelform" style="display:none"><form id="toBtpanel" action="'+murl+'/do_login" method="post" target="btpfrom">\ var loginForm ='<div id="btpanelform" style="display:none"><form id="toBtpanel" action="'+murl+'/do_login" method="post" target="btpfrom">\
<input name="username" id="btp_username" value="'+user+'" type="text">\ <input name="username" id="btp_username" value="'+user+'" type="text">\
@@ -1375,7 +1375,7 @@ function remind(a){
<td>\ <td>\
<div class="titlename c3">'+g.data[d].name+'</span>\ <div class="titlename c3">'+g.data[d].name+'</span>\
<span class="rs-status">【'+lan.bt.task_ok+'】<span>\ <span class="rs-status">【'+lan.bt.task_ok+'】<span>\
<span class="rs-time">耗时['+ getSFM(g.data[d].end - g.data[d].start) +']</span>\ <span class="rs-time">安装等待中...</span>\
</div>\ </div>\
</td>\ </td>\
<td class="text-right c3">'+g.data[d].addtime+'</td>\ <td class="text-right c3">'+g.data[d].addtime+'</td>\
@@ -2255,7 +2255,7 @@ function pluginConfig(_name, version, func){
//配置修改模版 --- start //配置修改模版 --- start
function pluginConfigTpl(_name, version, func, config_tpl_func, read_config_tpl_func){ function pluginConfigTpl(_name, version, func, config_tpl_func, read_config_tpl_func, save_callback_func){
if ( typeof(version) == 'undefined' ){ if ( typeof(version) == 'undefined' ){
version = ''; version = '';
} }
@@ -2281,7 +2281,7 @@ function pluginConfigTpl(_name, version, func, config_tpl_func, read_config_tpl_
<textarea class="bt-input-text" style="height: 320px; line-height:18px;" id="textBody"></textarea>\ <textarea class="bt-input-text" style="height: 320px; line-height:18px;" id="textBody"></textarea>\
<button id="onlineEditFileBtn" class="btn btn-success btn-sm" style="margin-top:10px;">保存</button>\ <button id="onlineEditFileBtn" class="btn btn-success btn-sm" style="margin-top:10px;">保存</button>\
<ul class="help-info-text c7 ptb15">\ <ul class="help-info-text c7 ptb15">\
<li>此处为'+ _name + version +'主配置文件,若您不了解配置规则,请勿随意修改。</li>\ <li>此处为【'+ _name + version +'】主配置文件,若您不了解配置规则,请勿随意修改。</li>\
</ul>'; </ul>';
$(".soft-man-con").html(con); $(".soft-man-con").html(con);
@@ -2321,7 +2321,7 @@ function pluginConfigTpl(_name, version, func, config_tpl_func, read_config_tpl_
"Ctrl-H": "replaceAll", "Ctrl-H": "replaceAll",
"Ctrl-S": function() { "Ctrl-S": function() {
$("#textBody").text(editor.getValue()); $("#textBody").text(editor.getValue());
pluginConfigSave(fileName); pluginConfigSave(fileName,save_callback_func);
} }
}, },
lineNumbers: true, lineNumbers: true,
@@ -2332,7 +2332,7 @@ function pluginConfigTpl(_name, version, func, config_tpl_func, read_config_tpl_
$("#onlineEditFileBtn").unbind('click'); $("#onlineEditFileBtn").unbind('click');
$("#onlineEditFileBtn").click(function(){ $("#onlineEditFileBtn").click(function(){
$("#textBody").text(editor.getValue()); $("#textBody").text(editor.getValue());
pluginConfigSave(fileName); pluginConfigSave(fileName, save_callback_func);
}); });
},'json'); },'json');
} }
@@ -2361,7 +2361,7 @@ function pluginConfigTpl(_name, version, func, config_tpl_func, read_config_tpl_
"Ctrl-H": "replaceAll", "Ctrl-H": "replaceAll",
"Ctrl-S": function() { "Ctrl-S": function() {
$("#textBody").text(editor.getValue()); $("#textBody").text(editor.getValue());
pluginConfigSave(fileName); pluginConfigSave(fileName,save_callback_func);
} }
}, },
lineNumbers: true, lineNumbers: true,
@@ -2371,7 +2371,7 @@ function pluginConfigTpl(_name, version, func, config_tpl_func, read_config_tpl_
$(".CodeMirror-scroll").css({"height":"300px","margin":0,"padding":0}); $(".CodeMirror-scroll").css({"height":"300px","margin":0,"padding":0});
$("#onlineEditFileBtn").click(function(){ $("#onlineEditFileBtn").click(function(){
$("#textBody").text(editor.getValue()); $("#textBody").text(editor.getValue());
pluginConfigSave(fileName); pluginConfigSave(fileName,save_callback_func);
}); });
},'json'); },'json');
},'json'); },'json');
@@ -2498,13 +2498,19 @@ function pluginConfigListTpl(_name, version, config_tpl_func, read_config_tpl_fu
//配置保存 //配置保存
function pluginConfigSave(fileName) { function pluginConfigSave(fileName, callback) {
var data = encodeURIComponent($("#textBody").val()); var data = encodeURIComponent($("#textBody").val());
var encoding = 'utf-8'; var encoding = 'utf-8';
var loadT = layer.msg('保存中...', {icon: 16,time: 0}); var loadT = layer.msg('保存中...', {icon: 16,time: 0});
$.post('/files/save_body', 'data=' + data + '&path=' + fileName + '&encoding=' + encoding, function(rdata) { $.post('/files/save_body', 'data=' + data + '&path=' + fileName + '&encoding=' + encoding, function(rdata) {
layer.close(loadT); layer.close(loadT);
layer.msg(rdata.msg, {icon: rdata.status ? 1 : 2});
showMsg(rdata.msg, function(){
if ( rdata.status && typeof(callback) == 'function'){
callback();
}
},{icon: rdata.status ? 1 : 2});
},'json'); },'json');
} }
+2 -1
View File
@@ -9,7 +9,8 @@ input,
textarea, textarea,
select { select {
font-size: 100%; font-size: 100%;
font-family: inherit font-family: inherit;
outline: none;
} }
body, body,
Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.8 KiB

+1 -1
View File
@@ -54,7 +54,7 @@
<p class="mtb15"> <p class="mtb15">
<span class="set-tit text-right">面板端口</span> <span class="set-tit text-right">面板端口</span>
<input data-port="{{data['port']}}" id="banport" name="port" class="inputtxt bt-input-text" type="numner" value="{{data['port']}}" maxlength="5"> <input data-port="{{data['port']}}" id="banport" name="port" class="inputtxt bt-input-text" type="number" value="{{data['port']}}" maxlength="5">
<button type="button" class="btn btn-success btn-sm ml5 btn_port" disabled>保存</button> <button type="button" class="btn btn-success btn-sm ml5 btn_port" disabled>保存</button>
<span class="set-info c7">建议端口范围7200 - 65535</span> <span class="set-info c7">建议端口范围7200 - 65535</span>
</p> </p>
+1 -1
View File
@@ -132,7 +132,7 @@
<div class="col-sm-6 col-md-3"><p class="c9"><span class="ico-read"></span>读取</p><a id="readBytes">0</a></div> <div class="col-sm-6 col-md-3"><p class="c9"><span class="ico-read"></span>读取</p><a id="readBytes">0</a></div>
<div class="col-sm-6 col-md-3"><p class="c9"><span class="ico-write"></span>写入</p><a id="writeBytes">0</a></div> <div class="col-sm-6 col-md-3"><p class="c9"><span class="ico-write"></span>写入</p><a id="writeBytes">0</a></div>
<div class="col-sm-6 col-md-3"><p class="c9">每秒读写</p><a id="diskIops">0</a></div> <div class="col-sm-6 col-md-3"><p class="c9">每秒读写</p><a id="diskIops">0</a></div>
<div class="col-sm-6 col-md-3"><p class="c9">IO延迟</p><a id="diskTime" style="color: rgb(32, 165, 58);">0</a></div> <div class="col-sm-6 col-md-3"><p class="c9">读写IO延迟</p><a id="diskTime" style="color: rgb(32, 165, 58);">0</a></div>
</div> </div>
<div id="ioStat" style="width:100%;height:330px;"></div> <div id="ioStat" style="width:100%;height:330px;"></div>
</div> </div>
+26 -9
View File
@@ -14,15 +14,6 @@
<!--[if lte IE 9]> <!--[if lte IE 9]>
<script src="/static/js/requestAnimationFrame.js"></script> <script src="/static/js/requestAnimationFrame.js"></script>
<![endif]--> <![endif]-->
<!-- 统计一下用户量,让我的开发更有激情 -->
<!-- Google tag (gtag.js) -->
<script async src="https://www.googletagmanager.com/gtag/js?id=G-FC74BB2RGD"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', 'G-FC74BB2RGD');
</script>
{% for menu in data['hook_menu'] %} {% for menu in data['hook_menu'] %}
@@ -188,4 +179,30 @@ if (thisPath.indexOf('?')>-1){
{% endif %} {% endif %}
{% endfor %} {% endfor %}
<!-- 统计一下用户量,让我的开发更有激情 -->
<script type="text/javascript">
$(document).ready(function(){
setTimeout(function(){
$.getScript('https://www.googletagmanager.com/gtag/js?id=G-FC74BB2RGD', function(){
console.log("load google ad!");
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', 'G-FC74BB2RGD');
});
},3000);
});
</script>
<!-- Google tag (gtag.js) -->
<!-- <script async src="https://www.googletagmanager.com/gtag/js?id=G-FC74BB2RGD"></script>
<script>
window.dataLayer = window.dataLayer || [];
function gtag(){dataLayer.push(arguments);}
gtag('js', new Date());
gtag('config', 'G-FC74BB2RGD');
</script> -->
</html> </html>
+23 -4
View File
@@ -87,6 +87,18 @@ class backupTools:
path = mw.getServerDir() + '/' + mtype + '/etc/my.cnf' path = mw.getServerDir() + '/' + mtype + '/etc/my.cnf'
return path return path
def recognizeDbMode(self, mtype='mysql'):
conf = self.getConf(mtype)
con = mw.readFile(conf)
rep = r"!include %s/(.*)?\.cnf" % (mw.getServerDir() +'/'+ mtype +"/etc/mode",)
mode = 'none'
try:
data = re.findall(rep, con, re.M)
mode = data[0]
except Exception as e:
pass
return mode
# 数据库密码处理 # 数据库密码处理
def mypass(self, act, root): def mypass(self, act, root):
conf_file = self.getConf('mysql') conf_file = self.getConf('mysql')
@@ -134,13 +146,20 @@ class backupTools:
# mw.execShell(db_path + "/bin/mysqldump --opt --default-character-set=utf8 " + # mw.execShell(db_path + "/bin/mysqldump --opt --default-character-set=utf8 " +
# name + " | gzip > " + filename) # name + " | gzip > " + filename)
# mw.execShell(db_path + "/bin/mysqldump --skip-lock-tables --default-character-set=utf8 " +
# name + " | gzip > " + filename)
# mw.execShell(db_path + "/bin/mysqldump --single-transaction --quick --default-character-set=utf8 " + # mw.execShell(db_path + "/bin/mysqldump --single-transaction --quick --default-character-set=utf8 " +
# name + " | gzip > " + filename) # name + " | gzip > " + filename)
cmd = db_path + "/bin/mysqldump --defaults-file=" + my_cnf + " --force --opt --default-character-set=utf8 " + \ # 开启一致性事务 会lock表
# cmd = db_path + "/bin/mysqldump --defaults-file=" + my_cnf + " --force --opt --default-character-set=utf8 " + \
# name + " | gzip > " + filename
option = ''
mode = self.recognizeDbMode('mysql')
if mode == 'gtid':
option = ' --set-gtid-purged=off '
# skip-opt 不会lock表
# --skip-opt --create-options
cmd = db_path + "/bin/mysqldump --defaults-file=" + my_cnf +" " + option +" --single-transaction -q --default-character-set=utf8mb4 " + \
name + " | gzip > " + filename name + " | gzip > " + filename
# print(cmd) # print(cmd)
mw.execShell(cmd) mw.execShell(cmd)
+56 -14
View File
@@ -209,6 +209,21 @@ mw_unbind_domain()
fi fi
} }
mw_unbind_ssl()
{
if [ -f $mw_path/local ];then
rm -rf $mw_path/local
fi
if [ -f $mw_path/nginx ];then
rm -rf $mw_path/nginx
fi
if [ -f $mw_path/ssl/choose.pl ];then
rm -rf $mw_path/ssl/choose.pl
fi
}
error_logs() error_logs()
{ {
tail -n 100 $mw_path/logs/error.log tail -n 100 $mw_path/logs/error.log
@@ -258,7 +273,7 @@ mw_mirror()
else else
bash <(curl --insecure -sSL https://gitee.com/SuperManito/LinuxMirrors/raw/main/ChangeMirrors.sh) bash <(curl --insecure -sSL https://gitee.com/SuperManito/LinuxMirrors/raw/main/ChangeMirrors.sh)
fi fi
cd /www/server/mdserver-web cd ${ROOT_PATH}/mdserver-web
} }
mw_install_app() mw_install_app()
@@ -297,8 +312,8 @@ mw_debug(){
port=$(cat $mw_path/data/port.pl) port=$(cat $mw_path/data/port.pl)
fi fi
if [ -d /www/server/mdserver-web ];then if [ -d ${ROOT_PATH}/mdserver-web ];then
cd /www/server/mdserver-web cd ${ROOT_PATH}/mdserver-web
fi fi
gunicorn -b :$port -k geventwebsocket.gunicorn.workers.GeventWebSocketWorker -w 1 app:app gunicorn -b :$port -k geventwebsocket.gunicorn.workers.GeventWebSocketWorker -w 1 app:app
} }
@@ -373,7 +388,8 @@ mw_connect_mysql(){
INPUT_KEY=${SOURCE_LIST_KEY[$INPUT]} INPUT_KEY=${SOURCE_LIST_KEY[$INPUT]}
CHOICE_DB=${DB_TYPE[$INPUT_KEY]} CHOICE_DB=${DB_TYPE[$INPUT_KEY]}
echo "login to ${CHOICE_DB}:" echo "login to ${CHOICE_DB}:"
pwd=$(cd /www/server/mdserver-web && python3 /www/server/mdserver-web/plugins/${CHOICE_DB}/index.py root_pwd)
pwd=$(cd ${ROOT_PATH}/mdserver-web && python3 ${ROOT_PATH}/mdserver-web/plugins/${CHOICE_DB}/index.py root_pwd)
if [ "$CHOICE_DB" == "mysql" ];then if [ "$CHOICE_DB" == "mysql" ];then
${ROOT_PATH}/mysql/bin/mysql -uroot -p"${pwd}" ${ROOT_PATH}/mysql/bin/mysql -uroot -p"${pwd}"
fi fi
@@ -392,15 +408,34 @@ mw_connect_mysql(){
} }
mw_redis(){
CONF="${ROOT_PATH}/redis/redis.conf"
if [ ! -f "$CONF" ]; then
echo -e "not install redis!"
exit 1
fi
REDISPORT=$(cat $CONF |grep port|grep -v '#'|awk '{print $2}')
REDISPASS=$(cat $CONF |grep requirepass|grep -v '#'|awk '{print $2}')
if [ "$REDISPASS" != "" ];then
REDISPASS=" -a $REDISPASS"
fi
CLIEXEC="${ROOT_PATH}/redis/bin/redis-cli -p $REDISPORT$REDISPASS"
echo $CLIEXEC
${CLIEXEC}
}
mw_venv(){ mw_venv(){
cd /www/server/mdserver-web && source bin/activate cd ${ROOT_PATH}/mdserver-web && source bin/activate
} }
mw_clean_lib(){ mw_clean_lib(){
cd /www/server/mdserver-web && rm -rf lib cd ${ROOT_PATH}/mdserver-web && rm -rf lib
cd /www/server/mdserver-web && rm -rf lib64 cd ${ROOT_PATH}/mdserver-web && rm -rf lib64
cd /www/server/mdserver-web && rm -rf bin cd ${ROOT_PATH}/mdserver-web && rm -rf bin
cd /www/server/mdserver-web && rm -rf include cd ${ROOT_PATH}/mdserver-web && rm -rf include
} }
case "$1" in case "$1" in
@@ -425,14 +460,21 @@ case "$1" in
'install_app') mw_install_app;; 'install_app') mw_install_app;;
'close_admin_path') mw_close_admin_path;; 'close_admin_path') mw_close_admin_path;;
'unbind_domain') mw_unbind_domain;; 'unbind_domain') mw_unbind_domain;;
'unbind_ssl') mw_unbind_domain;;
'debug') mw_debug;; 'debug') mw_debug;;
'mirror') mw_mirror;; 'mirror') mw_mirror;;
'db') mw_connect_mysql;; 'db') mw_connect_mysql;;
'redis') mw_redis;;
'venv') mw_venv;; 'venv') mw_venv;;
'clean_lib') mw_clean_lib;; 'clean_lib') mw_clean_lib;;
'default') 'default')
cd $mw_path cd $mw_path
port=7200 port=7200
scheme=http
if [ -f $mw_path/ssl/choose.pl ];then
scheme=https
fi
if [ -f $mw_path/data/port.pl ];then if [ -f $mw_path/data/port.pl ];then
port=$(cat $mw_path/data/port.pl) port=$(cat $mw_path/data/port.pl)
@@ -463,9 +505,9 @@ case "$1" in
mw_start mw_start
fi fi
address="MW-Panel-Url-Ipv4: http://$v4:$port$auth_path \nMW-Panel-Url-Ipv6: http://[$v6]:$port$auth_path" address="MW-Panel-Url-Ipv4: ${scheme}://$v4:$port$auth_path \nMW-Panel-Url-Ipv6: ${scheme}://[$v6]:$port$auth_path"
elif [ "$v4" != "" ]; then elif [ "$v4" != "" ]; then
address="MW-Panel-Url: http://$v4:$port$auth_path" address="MW-Panel-Url: ${scheme}://$v4:$port$auth_path"
elif [ "$v6" != "" ]; then elif [ "$v6" != "" ]; then
if [ ! -f $mw_path/data/ipv6.pl ];then if [ ! -f $mw_path/data/ipv6.pl ];then
@@ -474,12 +516,12 @@ case "$1" in
mw_stop mw_stop
mw_start mw_start
fi fi
address="MW-Panel-Url: http://[$v6]:$port$auth_path" address="MW-Panel-Url: ${scheme}://[$v6]:$port$auth_path"
else else
address="MW-Panel-Url: http://you-network-ip:$port$auth_path" address="MW-Panel-Url: ${scheme}://you-network-ip:$port$auth_path"
fi fi
else else
address="MW-Panel-Url: http://$address:$port$auth_path" address="MW-Panel-Url: ${scheme}://$address:$port$auth_path"
fi fi
show_panel_ip="$port|" show_panel_ip="$port|"
+1
View File
@@ -95,6 +95,7 @@ yum install -y bison re2c cmake
yum install -y cmake3 yum install -y cmake3
yum install -y autoconf yum install -y autoconf
yum install -y expect yum install -y expect
yum install -y bc
yum install -y curl curl-devel yum install -y curl curl-devel
yum install -y zlib zlib-devel yum install -y zlib zlib-devel
+1
View File
@@ -22,6 +22,7 @@ yum install -y curl-devel libmcrypt libmcrypt-devel
yum install -y mysql-devel yum install -y mysql-devel
yum install -y expect yum install -y expect
yum install -y pv yum install -y pv
yum install -y bc
SSH_PORT=`netstat -ntpl|grep sshd|grep -v grep | sed -n "1,1p" | awk '{print $4}' | awk -F : '{print $2}'` SSH_PORT=`netstat -ntpl|grep sshd|grep -v grep | sed -n "1,1p" | awk '{print $4}' | awk -F : '{print $2}'`
if [ "$SSH_PORT" == "" ];then if [ "$SSH_PORT" == "" ];then
+1
View File
@@ -41,6 +41,7 @@ echo y | pacman -Sy lemon
echo y | pacman -Sy which echo y | pacman -Sy which
echo y | pacman -Sy expect echo y | pacman -Sy expect
echo y | pacman -Sy pv echo y | pacman -Sy pv
echo y | pacman -Sy bc
## gd start ## gd start
+1
View File
@@ -32,6 +32,7 @@ yum install -y libmcrypt-devel
yum install -y mysql-devel yum install -y mysql-devel
yum install -y expect yum install -y expect
yum install -y pv yum install -y pv
yum install -y bc
# if [ -f /usr/sbin/iptables ];then # if [ -f /usr/sbin/iptables ];then
+1
View File
@@ -57,6 +57,7 @@ apt install -y wget curl lsof unzip tar cron expect locate lrzsz
apt install -y rar apt install -y rar
apt install -y unrar apt install -y unrar
apt install -y pv apt install -y pv
apt install -y bc
apt install -y python3-pip python3-dev python3-venv apt install -y python3-pip python3-dev python3-venv
+1
View File
@@ -32,6 +32,7 @@ yum install -y libmcrypt-devel
yum install -y mysql-devel yum install -y mysql-devel
yum install -y expect yum install -y expect
yum install -y pv yum install -y pv
yum install -y bc
# if [ -f /usr/sbin/iptables ];then # if [ -f /usr/sbin/iptables ];then
+1
View File
@@ -74,6 +74,7 @@ yum install -y epel-release
yum install -y libevent libevent-devel zip libmcrypt libmcrypt-devel yum install -y libevent libevent-devel zip libmcrypt libmcrypt-devel
yum install -y rar unrar yum install -y rar unrar
yum install -y pv yum install -y pv
yum install -y bc
yum install -y gcc libffi-devel python-devel openssl-devel yum install -y gcc libffi-devel python-devel openssl-devel
yum install -y libmcrypt libmcrypt-devel python3-devel yum install -y libmcrypt libmcrypt-devel python3-devel
+1
View File
@@ -61,6 +61,7 @@ pkg install -y libevent
pkg install -y pidof pkg install -y pidof
pkg install -y pstree pkg install -y pstree
pkg install -y pv pkg install -y pv
pkg install -y bc
# curl https://sh.rustup.rs -sSf | sh # curl https://sh.rustup.rs -sSf | sh
pkg install -y rust pkg install -y rust
+1
View File
@@ -29,6 +29,7 @@ zypper install -y libtirpc-devel
zypper install -y rpcgen zypper install -y rpcgen
zypper install -y expect zypper install -y expect
zypper install -y pv zypper install -y pv
zypper install -y bc
zypper install -y libzip libzip-devel zypper install -y libzip libzip-devel
zypper install -y unrar rar zypper install -y unrar rar
+2 -2
View File
@@ -184,7 +184,7 @@ if [ "$VERSION_ID" -ge "8" ];then
oniguruma oniguruma-devel patch pcre pcre-devel perl perl-Data-Dumper perl-devel procps psmisc python3-devel \ oniguruma oniguruma-devel patch pcre pcre-devel perl perl-Data-Dumper perl-devel procps psmisc python3-devel \
openssl openssl-devel patchelf libargon2-devel\ openssl openssl-devel patchelf libargon2-devel\
ImageMagick ImageMagick-devel libyaml-devel \ ImageMagick ImageMagick-devel libyaml-devel \
pv \ pv bc\
readline-devel rpcgen sqlite-devel rar unrar tar unzip vim-minimal wget zip zlib zlib-devel; readline-devel rpcgen sqlite-devel rar unrar tar unzip vim-minimal wget zip zlib zlib-devel;
do do
# dnf --enablerepo=remi,appstream,baseos,epel,extras,powertools install -y oniguruma5php-devel # dnf --enablerepo=remi,appstream,baseos,epel,extras,powertools install -y oniguruma5php-devel
@@ -204,7 +204,7 @@ else
libwebp libwebp-devel libxml2 libxml2-devel libxslt libxslt-devel libzip libzip-devel libzstd-devel \ libwebp libwebp-devel libxml2 libxml2-devel libxslt libxslt-devel libzip libzip-devel libzstd-devel \
make mysql-devel ncurses ncurses-devel net-tools oniguruma oniguruma-devel openldap openldap-devel \ make mysql-devel ncurses ncurses-devel net-tools oniguruma oniguruma-devel openldap openldap-devel \
openssl openssl-devel patch pcre pcre-devel perl perl-Data-Dumper perl-devel psmisc python-devel \ openssl openssl-devel patch pcre pcre-devel perl perl-Data-Dumper perl-devel psmisc python-devel \
pv \ pv bc\
python3-devel python3-pip re2c readline-devel rpcgen sqlite-devel tar unzip rar unrar vim-minimal vixie-cron \ python3-devel python3-pip re2c readline-devel rpcgen sqlite-devel tar unzip rar unrar vim-minimal vixie-cron \
wget zip zlib zlib-devel ImageMagick ImageMagick-devel libyaml-devel patchelf libargon2-devel; wget zip zlib zlib-devel ImageMagick ImageMagick-devel libyaml-devel patchelf libargon2-devel;
do do
+1
View File
@@ -16,6 +16,7 @@ sed -i 's#SELINUX=enforcing#SELINUX=disabled#g' /etc/selinux/config
yum install -y wget lsof yum install -y wget lsof
yum install -y unrar rar yum install -y unrar rar
yum install -y pv yum install -y pv
yum install -y bc
yum install -y python3-devel yum install -y python3-devel
yum install -y crontabs yum install -y crontabs
yum install -y expect yum install -y expect
+1
View File
@@ -19,6 +19,7 @@ apt install -y python3-venv
apt install -y python3-dev apt install -y python3-dev
apt install -y expect apt install -y expect
apt install -y pv apt install -y pv
apt install -y bc
apt install -y cron apt install -y cron
+11 -5
View File
@@ -59,11 +59,17 @@ if os.path.exists('data/ipv6.pl'):
else: else:
bind.append('0.0.0.0:%s' % mw_port) bind.append('0.0.0.0:%s' % mw_port)
# if os.path.exists('data/ssl.pl'): ssl_choose_file = 'ssl/choose.pl'
# certfile = 'ssl/certificate.pem' if os.path.exists(ssl_choose_file):
# keyfile = 'ssl/privateKey.pem' ssl_choose = mw.readFile(ssl_choose_file).strip()
# ciphers = 'TLSv1 TLSv1.1 TLSv1.2 TLSv1.3' if mw.inArray(['local','nginx'],ssl_choose):
# ssl_version = 2 tmp_cert = 'ssl/'+ssl_choose+'/cert.pem'
tmp_private = 'ssl/'+ssl_choose+'/private.pem'
if os.path.exists(tmp_cert) and os.path.exists(tmp_private):
certfile = tmp_cert
keyfile = tmp_private
ciphers = 'TLSv1 TLSv1.1 TLSv1.2 TLSv1.3'
ssl_version = 2
# 初始安装时,自动生成安全路径 # 初始安装时,自动生成安全路径
if not os.path.exists('data/admin_path.pl'): if not os.path.exists('data/admin_path.pl'):
+43 -7
View File
@@ -64,6 +64,29 @@ def service_cmd(method):
return return
def openresty_cmd(method = 'reload'):
# 检查是否安装
odir = mw.getServerDir() + '/openresty'
if not os.path.exists(odir):
return False
# systemd
systemd = mw.systemdCfgDir()+'/openresty.service'
if os.path.exists(systemd):
execShell('systemctl ' + method + ' openresty')
return True
sys_initd = '/etc/init.d/openresty'
if os.path.exists(install_initd):
os.system(sys_initd + ' ' + method)
return True
install_initd = mw.getServerDir()+'/openresty/init.d/openresty'
if os.path.exists(install_initd):
os.system(install_initd + ' ' + method)
return True
return False
def mw_async(f): def mw_async(f):
def wrapper(*args, **kwargs): def wrapper(*args, **kwargs):
thr = threading.Thread(target=f, args=args, kwargs=kwargs) thr = threading.Thread(target=f, args=args, kwargs=kwargs)
@@ -539,13 +562,7 @@ def openrestyAutoRestart():
time.sleep(86400) time.sleep(86400)
continue continue
# systemd openresty_cmd('reload')
systemd = mw.systemdCfgDir()+'/openresty.service'
initd = '/etc/init.d/openresty'
if os.path.exists(systemd):
execShell('systemctl reload openresty')
elif os.path.exists(initd):
os.system(initd + ' reload')
time.sleep(86400) time.sleep(86400)
except Exception as e: except Exception as e:
print(str(e)) print(str(e))
@@ -553,6 +570,18 @@ def openrestyAutoRestart():
# --------------------------------------OpenResty Auto Restart End --------------------------------------------- # # --------------------------------------OpenResty Auto Restart End --------------------------------------------- #
# ------------------------------------ OpenResty Restart At Once Start ------------------------------------------ #
def openrestyRestartAtOnce():
restart_nginx_tip = 'data/restart_nginx.pl'
while True:
if os.path.exists(restart_nginx_tip):
os.remove(restart_nginx_tip)
openresty_cmd('reload')
time.sleep(1)
# ----------------------------------- OpenResty Restart At Once End ------------------------------------------ #
# --------------------------------------Panel Restart Start --------------------------------------------- # # --------------------------------------Panel Restart Start --------------------------------------------- #
def restartPanelService(): def restartPanelService():
@@ -584,11 +613,18 @@ if __name__ == "__main__":
php502 = setDaemon(php502) php502 = setDaemon(php502)
php502.start() php502.start()
# OpenResty Restart At Once Start
oraos = threading.Thread(target=openrestyRestartAtOnce)
oraos = setDaemon(oraos)
oraos.start()
# OpenResty Auto Restart Start # OpenResty Auto Restart Start
oar = threading.Thread(target=openrestyAutoRestart) oar = threading.Thread(target=openrestyAutoRestart)
oar = setDaemon(oar) oar = setDaemon(oar)
oar.start() oar.start()
# Panel Restart Start # Panel Restart Start
rps = threading.Thread(target=restartPanelService) rps = threading.Thread(target=restartPanelService)
rps = setDaemon(rps) rps = setDaemon(rps)
+36 -8
View File
@@ -58,10 +58,12 @@ def mwcli(mw_input=0):
print("(13) 显示面板错误日志") print("(13) 显示面板错误日志")
print("(20) 关闭BasicAuth认证") print("(20) 关闭BasicAuth认证")
print("(21) 解除域名绑定") print("(21) 解除域名绑定")
print("(22) 开启IPV6支持") print("(22) 解除面板SSL绑定")
print("(23) 关闭IPV6支持") print("(23) 开启IPV6支持")
print("(24) 开启防火墙SSH端口") print("(24) 关闭IPV6支持")
print("(25) 关闭二次验证") print("(25) 开启防火墙SSH端口")
print("(26) 关闭二次验证")
print("(27) 查看防火墙信息")
print("(100) 开启PHP52显示") print("(100) 开启PHP52显示")
print("(101) 关闭PHP52显示") print("(101) 关闭PHP52显示")
print("(200) 切换Linux系统软件源") print("(200) 切换Linux系统软件源")
@@ -75,7 +77,12 @@ def mwcli(mw_input=0):
except: except:
mw_input = 0 mw_input = 0
nums = [1, 2, 3, 4, 5, 10, 11, 12, 13, 20, 21, 22, 23, 24, 25, 100, 101, 200, 201] nums = [
1, 2, 3, 4, 5, 10, 11, 12, 13,
20, 21, 22, 23, 24, 25, 26, 27,
100, 101,
200, 201
]
if not mw_input in nums: if not mw_input in nums:
print(raw_tip) print(raw_tip)
print("已取消!") print("已取消!")
@@ -128,6 +135,12 @@ def mwcli(mw_input=0):
os.system(INIT_CMD + " unbind_domain") os.system(INIT_CMD + " unbind_domain")
print("|-解除域名绑定成功") print("|-解除域名绑定成功")
elif mw_input == 22: elif mw_input == 22:
ssl_choose = 'ssl/choose.pl'
if os.path.exists(ssl_choose):
os.remove(ssl_choose)
os.system(INIT_CMD + " unbind_ssl")
print("|-解除面板SSL绑定成功")
elif mw_input == 23:
listen_ipv6 = 'data/ipv6.pl' listen_ipv6 = 'data/ipv6.pl'
if not os.path.exists(listen_ipv6): if not os.path.exists(listen_ipv6):
mw.writeFile(listen_ipv6,'True') mw.writeFile(listen_ipv6,'True')
@@ -135,7 +148,7 @@ def mwcli(mw_input=0):
print("|-开启IPv6支持了") print("|-开启IPv6支持了")
else: else:
print("|-已开启IPv6支持!") print("|-已开启IPv6支持!")
elif mw_input == 23: elif mw_input == 24:
listen_ipv6 = 'data/ipv6.pl' listen_ipv6 = 'data/ipv6.pl'
if not os.path.exists(listen_ipv6): if not os.path.exists(listen_ipv6):
print("|-已关闭IPv6支持!") print("|-已关闭IPv6支持!")
@@ -143,16 +156,31 @@ def mwcli(mw_input=0):
os.remove(listen_ipv6) os.remove(listen_ipv6)
os.system(INIT_CMD + " restart") os.system(INIT_CMD + " restart")
print("|-关闭IPv6支持了") print("|-关闭IPv6支持了")
elif mw_input == 24: elif mw_input == 25:
open_ssh_port() open_ssh_port()
print("|-已开启!") print("|-已开启!")
elif mw_input == 25: elif mw_input == 26:
auth_secret = 'data/auth_secret.pl' auth_secret = 'data/auth_secret.pl'
if os.path.exists(auth_secret): if os.path.exists(auth_secret):
os.remove(auth_secret) os.remove(auth_secret)
print("|-关闭二次验证成功!") print("|-关闭二次验证成功!")
else: else:
print("|-二次验证已关闭!") print("|-二次验证已关闭!")
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 == 100: elif mw_input == 100:
php_conf = 'plugins/php/info.json' php_conf = 'plugins/php/info.json'
if os.path.exists(php_conf): if os.path.exists(php_conf):
+2 -1
View File
@@ -35,4 +35,5 @@ whitenoise==5.3.0
pyotp pyotp
pytz pytz
pyTelegramBotAPI pyTelegramBotAPI
telebot telebot
pyyaml
+2 -1
View File
@@ -1,3 +1,4 @@
urllib3==1.21.1 urllib3==1.21.1
gevent==22.10.2 gevent==22.10.2
gunicorn==21.2.0 gunicorn==21.2.0
pyyaml