Compare commits

...
264 Commits
Author SHA1 Message Date
Mr Chen 29a3e606e1 Merge pull request #586 from midoks/dev
0.17.1
2024-06-05 19:00:44 +08:00
Mr Chen d2b837f52c Update mw.py 2024-06-05 18:58:12 +08:00
Mr Chen 8984381ea2 0.17.1 2024-06-05 18:57:35 +08:00
Mr Chen ff015b19f7 Update install.sh 2024-06-05 18:14:40 +08:00
Mr Chen 1e71eedc19 Update install.sh 2024-06-05 18:13:40 +08:00
Mr Chen fbce4a1abc Update install.sh 2024-06-05 18:12:51 +08:00
Mr Chen d0cdab81d4 Update install.sh 2024-06-05 18:08:57 +08:00
Mr Chen a69d5367d7 Update task_manager_index.py 2024-06-05 17:48:26 +08:00
Mr Chen 8d0ee48aa6 Update task_manager_index.py 2024-06-05 17:47:13 +08:00
Mr Chen c24510e1e7 Update task_manager_index.py 2024-06-05 17:44:27 +08:00
Mr Chen 2e1c2e21b7 Update task_manager_index.py 2024-06-05 17:18:19 +08:00
Mr Chen fee8c8c3db Update task_manager_index.py 2024-06-05 17:13:16 +08:00
Mr Chen d94290019c Update task_manager_index.py 2024-06-05 17:08:42 +08:00
Mr Chen 27b914a905 Update task_manager_index.py 2024-06-05 17:01:45 +08:00
Mr Chen ded25a7c87 Update task_manager_index.py 2024-06-05 16:41:29 +08:00
Mr Chen 5ba5da4cd1 Update task_manager_index.py 2024-06-05 16:39:48 +08:00
Mr Chen cbb2a076e3 Update task_manager.js 2024-06-05 16:30:10 +08:00
Mr Chen 62df45da6a Update task_manager_index.py 2024-06-05 16:22:45 +08:00
Mr Chen e441d8dc87 Update task_manager_index.py 2024-06-05 16:22:12 +08:00
Mr Chen 6d77e923b7 Update task_manager_index.py 2024-06-05 16:22:04 +08:00
Mr Chen 17a40802ae Update task_manager_index.py 2024-06-05 16:16:10 +08:00
Mr Chen 13bb828a95 Update task_manager.js 2024-06-05 16:12:37 +08:00
Mr Chen eaa1a74dd6 Update task_manager_index.py 2024-06-05 16:05:46 +08:00
Mr Chen 80b615810c Update process_network_total.py 2024-06-05 15:56:27 +08:00
Mr Chen 23fe4928e0 Update task_manager_index.py 2024-06-05 15:31:09 +08:00
Mr Chen 5a37c742e2 Update info.json 2024-06-05 15:21:09 +08:00
Mr Chen b94af58524 Update task_manager_index.py 2024-06-05 15:14:48 +08:00
Mr Chen 082c90b769 update 2024-06-05 15:14:20 +08:00
Mr Chen 5012dc3a7b Update task_manager_index.py 2024-06-05 14:02:41 +08:00
Mr Chen a6619b6e9d Update task_manager_index.py 2024-06-05 13:56:34 +08:00
Mr Chen fe4ab5a013 Update task_manager_index.py 2024-06-05 13:40:27 +08:00
Mr Chen ade3a5621f Update process_network_total.py 2024-06-05 13:33:23 +08:00
Mr Chen 1cadeb3f59 Update task_manager_index.py 2024-06-05 13:19:44 +08:00
Mr Chen 05a2ba7f21 Update process_network_total.py 2024-06-05 13:18:06 +08:00
Mr Chen 1759b6bcb1 update 2024-06-05 13:16:56 +08:00
Mr Chen a9e4c1b793 Update task_manager_index.py 2024-06-05 12:59:31 +08:00
Mr Chen 650b565fdf Update task_manager_index.py 2024-06-05 12:58:19 +08:00
Mr Chen 7ab974e565 Update task_manager_index.py 2024-06-05 12:56:47 +08:00
Mr Chen 95f8faf30f Update task_manager.js 2024-06-05 12:14:29 +08:00
Mr Chen 3d69e3723e Update task_manager.js 2024-06-05 12:13:56 +08:00
Mr Chen 4420bbc36c Update task_manager.js 2024-06-05 12:08:33 +08:00
Mr Chen d5d96f6323 Update config_api.py 2024-06-05 12:01:38 +08:00
Mr Chen 0bc9ed9d49 Update task_manager.js 2024-06-05 12:01:13 +08:00
Mr Chen 95e29d6266 Update task_manager.js 2024-06-05 12:00:27 +08:00
Mr Chen 207cef200f update 2024-06-05 11:58:07 +08:00
Mr Chen 0082bcd159 Update site.css 2024-06-05 11:39:21 +08:00
Mr Chen f2a1674247 Update task_manager_index.py 2024-06-05 11:29:04 +08:00
Mr Chen cd1a3de04a Update task_manager_index.py 2024-06-05 11:27:26 +08:00
Mr Chen 60a0bd7a65 Update task_manager_index.py 2024-06-05 11:25:54 +08:00
Mr Chen a0696fc974 Update task_manager_index.py 2024-06-05 11:25:32 +08:00
Mr Chen 159660815b Update task_manager.js 2024-06-05 11:20:28 +08:00
Mr Chen 1c5a3f3c13 Update task_manager_index.py 2024-06-05 11:17:30 +08:00
Mr Chen b36fc58839 Update task_manager_index.py 2024-06-05 11:09:53 +08:00
Mr Chen 0cd7a7dc9e Update task_manager_index.py 2024-06-05 11:05:10 +08:00
Mr Chen 52724dd5b2 Update task_manager_index.py 2024-06-05 11:04:42 +08:00
Mr Chen dccea0e283 Update task_manager_index.py 2024-06-05 11:04:05 +08:00
Mr Chen 0f488b26d1 Update task_manager_index.py 2024-06-05 11:03:41 +08:00
Mr Chen c3b60695c7 Update task_manager_index.py 2024-06-05 10:54:21 +08:00
Mr Chen 6fe205dbfb Update task_manager_index.py 2024-06-05 10:51:36 +08:00
Mr Chen d8efd3f987 Update task_manager.js 2024-06-05 03:19:48 +08:00
Mr Chen 4c50e44e2c Update task_manager.js 2024-06-05 03:15:32 +08:00
Mr Chen 60717c3363 Update task_manager.js 2024-06-05 03:09:10 +08:00
Mr Chen 48388edcfc Update task_manager_index.py 2024-06-05 03:07:24 +08:00
Mr Chen 3c90a0fbe6 Update task_manager_index.py 2024-06-05 03:03:43 +08:00
Mr Chen 6eadb548a1 Update task_manager_index.py 2024-06-05 02:54:10 +08:00
Mr Chen 204be55c81 Update task_manager_index.py 2024-06-05 02:48:19 +08:00
Mr Chen a44ac7606a Update task_manager_index.py 2024-06-05 02:43:31 +08:00
Mr Chen 67505f7aef Update task_manager_index.py 2024-06-05 02:40:57 +08:00
Mr Chen 369dc69364 Update task_manager.js 2024-06-05 02:23:47 +08:00
Mr Chen 1ffe197d69 Update task_manager_index.py 2024-06-05 02:16:53 +08:00
Mr Chen 6a584ccf7b Update task_manager_index.py 2024-06-05 02:13:02 +08:00
Mr Chen 43aba019c0 Update task_manager_index.py 2024-06-05 02:09:19 +08:00
Mr Chen a0a61d3bbc Update task_manager.js 2024-06-05 01:15:34 +08:00
Mr Chen 9b008fcff9 Update task_manager_index.py 2024-06-05 01:09:51 +08:00
Mr Chen e80162cfa9 Update task_manager.js 2024-06-05 01:07:14 +08:00
Mr Chen 30b02aea33 update 2024-06-05 01:05:37 +08:00
Mr Chen 1cf49c24ef Update task_manager_index.py 2024-06-05 01:03:38 +08:00
Mr Chen 3bb0552ba6 Update task_manager_index.py 2024-06-05 00:52:03 +08:00
Mr Chen b33be2ab84 update 2024-06-05 00:50:42 +08:00
Mr Chen 8a4a63c85b Update task_manager.js 2024-06-05 00:39:40 +08:00
Mr Chen 640e8bbfa0 Update task_manager_index.py 2024-06-05 00:37:13 +08:00
Mr Chen e6463e0286 Update task_manager_index.py 2024-06-05 00:35:08 +08:00
Mr Chen c6cf6fef0b Update task_manager_index.py 2024-06-05 00:32:50 +08:00
Mr Chen a428636e1c Update task_manager_index.py 2024-06-05 00:31:17 +08:00
Mr Chen 6fd145fa52 Update index.html 2024-06-05 00:13:42 +08:00
Mr Chen 6164d462f3 Update task_manager.js 2024-06-05 00:08:28 +08:00
Mr Chen d05e72e1d7 Update task_manager_index.py 2024-06-04 23:42:37 +08:00
Mr Chen d9fbfcb184 Update task_manager_index.py 2024-06-04 22:28:35 +08:00
Mr Chen 660d95616d Update task_manager_index.py 2024-06-04 22:26:13 +08:00
Mr Chen 6160819920 Update task_manager.js 2024-06-04 22:17:14 +08:00
Mr Chen c83b2d5bc5 Update task_manager_index.py 2024-06-04 22:09:59 +08:00
Mr Chen 44d529d32f Update public.js 2024-06-04 20:36:31 +08:00
Mr Chen b81e51c0e8 Update task_manager.js 2024-06-04 20:35:43 +08:00
Mr Chen beccfe8519 update 2024-06-04 18:19:17 +08:00
Mr Chen 9e774fb629 update 2024-06-04 17:57:58 +08:00
Mr Chen 10c7ec8718 Update task_manager_index.py 2024-06-04 03:32:34 +08:00
Mr Chen 4bb9d0f002 Update task_manager.js 2024-06-04 01:51:09 +08:00
Mr Chen 39676d1a37 Update task_manager_index.py 2024-06-04 01:49:24 +08:00
Mr Chen 50f44afd82 update 2024-06-04 01:48:47 +08:00
Mr Chen 9884bbd0e3 Update task_manager.js 2024-06-04 01:38:53 +08:00
Mr Chen 39853e39c7 Update task_manager.js 2024-06-04 01:38:37 +08:00
Mr Chen 894656741d Update task_manager.js 2024-06-04 01:35:09 +08:00
Mr Chen 93ef3659f2 Update task_manager_index.py 2024-06-04 01:34:17 +08:00
Mr Chen 3ccf084a53 Update task_manager.js 2024-06-04 01:27:43 +08:00
Mr Chen aaa2b766a3 Update task_manager.js 2024-06-04 01:07:52 +08:00
Mr Chen dc1f03b7ae update 2024-06-04 01:04:16 +08:00
Mr Chen 36b50d3bf2 update 2024-06-04 01:01:15 +08:00
Mr Chen b9b7081eb4 Update task_manager_index.py 2024-06-04 00:38:18 +08:00
Mr Chen 808e65091d Update install.sh 2024-06-04 00:34:19 +08:00
Mr Chen 9b0c30e554 Update task_manager_index.py 2024-06-04 00:33:02 +08:00
Mr Chen ffec30883f Update task_manager_index.py 2024-06-04 00:30:29 +08:00
Mr Chen 900dfc6ca7 Update task_manager_index.py 2024-06-04 00:28:42 +08:00
Mr Chen 203b4d11a8 Update task_manager_index.py 2024-06-04 00:26:48 +08:00
Mr Chen 2ba57ab05f Update task_manager_index.py 2024-06-04 00:25:04 +08:00
Mr Chen 4b11415f36 Update task_manager_index.py 2024-06-04 00:23:52 +08:00
Mr Chen 1ef0c4b8f0 Update task_manager_index.py 2024-06-04 00:23:06 +08:00
Mr Chen 4d85ed4e88 Update task_manager_index.py 2024-06-04 00:22:42 +08:00
Mr Chen 6c40e57ff6 update 2024-06-04 00:19:15 +08:00
Mr Chen 19a4974537 Update stats.js 2024-06-03 23:09:30 +08:00
Mr Chen 6783e1507d Update mysql.js 2024-06-03 12:10:59 +08:00
Mr Chen 8f3e722a0f Update .gitignore 2024-06-03 00:43:38 +08:00
Mr Chen 3437d2e484 Update php.js 2024-06-01 12:17:10 +08:00
Mr Chen 06e654ae2e Merge pull request #585 from midoks/dev
ssh终端提高稳定性。
2024-06-01 02:08:55 +08:00
Mr Chen c3103a23c6 Update __init__.py 2024-06-01 01:57:36 +08:00
Mr Chen 55b38c2741 Update ssh_local.py 2024-06-01 01:55:14 +08:00
Mr Chen 9f30c1968d Update ssh_local.py 2024-06-01 01:53:53 +08:00
Mr Chen d2455768ff Update ssh_local.py 2024-06-01 01:53:39 +08:00
Mr Chen 6331155dc7 Update ssh_local.py 2024-06-01 01:51:27 +08:00
Mr Chen 06d9695d33 update 2024-06-01 01:50:29 +08:00
Mr Chen 80b35284ed Update ssh_terminal.py 2024-06-01 01:33:29 +08:00
Mr Chen 27c38153b8 Update ssh_terminal.py 2024-06-01 01:20:26 +08:00
Mr Chen db62524dab Update __init__.py 2024-06-01 01:19:04 +08:00
Mr Chen 190b858e46 update 2024-06-01 01:16:40 +08:00
Mr Chen be69e5838e Merge pull request #584 from midoks/dev
mysql插件 pv命令判断修复
2024-05-30 12:15:26 +08:00
Mr Chen b12122f53b update 2024-05-30 12:14:54 +08:00
Mr Chen 33be35eb60 Update config_api.py 2024-05-27 19:06:20 +08:00
Mr Chen 39b3f96ab5 Update index.js 2024-05-27 19:05:44 +08:00
Mr Chen 7d214f74fd Update public.js 2024-05-27 18:59:47 +08:00
Mr Chen 10aa140fe9 Update public.js 2024-05-27 18:59:39 +08:00
Mr Chen c68eaa9c61 Update public.js 2024-05-27 18:59:23 +08:00
Mr Chen 3cc6021540 Update public.js 2024-05-27 18:53:50 +08:00
Mr Chen bfe7d49e07 Update public.js 2024-05-27 18:53:44 +08:00
Mr Chen c02aacb0d5 Update public.js 2024-05-27 18:53:19 +08:00
Mr Chen 6d0f6fd171 Update index.js 2024-05-27 18:51:48 +08:00
Mr Chen 712061e814 Update index.js 2024-05-27 18:49:48 +08:00
Mr Chen 197dfa1b7e Update index.js 2024-05-27 18:48:41 +08:00
Mr Chen f21b66ddc0 Update index.js 2024-05-27 18:48:02 +08:00
Mr Chen 5e086537d6 Update index.js 2024-05-27 18:46:53 +08:00
Mr Chen 23532cb7e6 Update index.js 2024-05-27 18:43:40 +08:00
Mr Chen 864ee5caec Update index.js 2024-05-27 18:43:07 +08:00
Mr Chen 5db6d0332b Update config_api.py 2024-05-27 18:36:01 +08:00
Mr Chen eb9504658a update 2024-05-27 18:35:30 +08:00
Mr Chen a67389d611 Update stats.js 2024-05-27 18:07:12 +08:00
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
50 changed files with 5161 additions and 459 deletions
+23 -22
View File
@@ -87,6 +87,7 @@ venv/
ENV/
env.bak/
venv.bak/
lib/python*
# Spyder project settings
.spyderproject
@@ -98,6 +99,12 @@ venv.bak/
# mkdocs documentation
/site
bin
pyvenv.cfg
include
share
pip-selfcheck.json
# mypy
.mypy_cache/
@@ -108,7 +115,22 @@ tmp/*
*.swp
*.pem
*.zip
debug.out
mdioks.session-journal
*.session
logs/*
ssl/local
ssl/nginx
ssl/choose.pl
scripts/init.d/mw
scripts/mdserver-web
data/api_login.txt
data/sessions
data/*.db
data/iplist.txt
data/json/index.json
@@ -122,27 +144,11 @@ data/default.pl
data/backup.pl
data/debug.pl
data/default_site.pl
ssl/input.pl
lib/python*
bin
pyvenv.cfg
include
share
pip-selfcheck.json
scripts/init.d/mw
scripts/mdserver-web
data/api_login.txt
data/sessions
data/ssl.pl
data/port.pl
data/ipv6.pl
data/restart.pl
data/ssl.pl
data/edate.pl
data/osname.pl
data/only_netio_counters.pl
@@ -174,8 +180,3 @@ plugins/frp
plugins/file_search
plugins/proxysql
plugins/tidb
debug.out
mdioks.session-journal
*.session
+2 -5
View File
@@ -110,12 +110,9 @@ docker run -itd --name mw-server --privileged=true -p 7200:7200 -p 80:80 -p 443:
```
### 版本更新 0.16.9
### 版本更新 0.17.1
- mysql同步优化,享受丝滑般感觉。
- 网站统计 - 实时-可调节1-10s。
- 网站统计 - 加入大小条件。
- Sphinx优化。
- 任务管理器-插件。
### JSDelivr安装地址
+318 -222
View File
@@ -28,7 +28,7 @@ from flask import request
class config_api:
__version = '0.16.9'
__version = '0.17.1'
__api_addr = 'data/api.json'
# 统一默认配置文件
@@ -42,7 +42,7 @@ class config_api:
'bind_domain' : 'data/bind_domain.pl', # 面板域名绑定
'unauth_status' : 'data/unauthorized_status.pl', # URL路径未成功显示状态
'auth_secret': 'data/auth_secret.pl', # 二次验证密钥
'ssl':'data/ssl.pl', # ssl设置
'ssl':'ssl/choose.pl', # ssl设置
'hook_database' : 'data/hook_database.json', # 数据库钩子
'hook_menu' : 'data/hook_menu.json', # 菜单钩子
'hook_global_static' : 'data/hook_global_static.json', # 静态文件钩子
@@ -362,19 +362,23 @@ class config_api:
return mw.getJson(cert)
def getPanelSslData(self):
cert = {}
keyPath = 'ssl/private.pem'
certPath = 'ssl/cert.pem'
rdata = {}
choose_file = self.__file['ssl']
rdata['choose'] = mw.readFile(choose_file)
keyPath = 'ssl/local/private.pem'
certPath = 'ssl/local/cert.pem'
if not os.path.exists(certPath):
# 不再自动生成证书
# mw.createSSL()
cert['privateKey'] = ''
cert['is_https'] = ''
cert['certPem'] = ''
cert['rep'] = os.path.exists('ssl/input.pl')
cert['info'] = {'endtime': 0, 'subject': '无',
'notAfter': '无', 'notBefore': '无', 'issuer': '无'}
return cert
mw.createLocalSSL()
cert = {}
cert['privateKey'] = mw.readFile(keyPath)
cert['is_https'] = ''
cert['certPem'] = mw.readFile(certPath)
cert['info'] = mw.getCertName(certPath)
rdata['local'] = cert
panel_ssl = mw.getServerDir() + "/web_conf/nginx/vhost/panel.conf"
if not os.path.exists(panel_ssl):
@@ -384,21 +388,68 @@ class config_api:
if ssl_data.find('$server_port !~ 443') != -1:
cert['is_https'] = 'checked'
keyPath = 'ssl/nginx/private.pem'
certPath = 'ssl/nginx/cert.pem'
cert = {}
cert['privateKey'] = mw.readFile(keyPath)
cert['certPem'] = mw.readFile(certPath)
cert['rep'] = os.path.exists('ssl/input.pl')
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):
keyPath = 'ssl/private.pem'
certPath = 'ssl/cert.pem'
checkCert = '/tmp/cert.pl'
choose = request.form.get('choose', '').strip()
certPem = request.form.get('certPem', '').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):
return mw.returnJson(False, '秘钥错误,请检查!')
if(certPem.find('CERTIFICATE') == -1):
@@ -411,258 +462,303 @@ class config_api:
mw.writeFile(certPath, certPem)
if not mw.checkCert(checkCert):
return mw.returnJson(False, '证书错误,请检查!')
mw.writeFile('ssl/input.pl', 'True')
return mw.returnJson(True, '证书已保存!')
# 设置面板SSL证书设置
def setPanelHttpToHttpsApi(self):
# def setPanelHttpToHttpsApi(self):
bind_domain = self.__file['bind_domain']
if not os.path.exists(bind_domain):
return mw.returnJson(False, '先要绑定域名!')
# bind_domain = self.__file['bind_domain']
# if not os.path.exists(bind_domain):
# return mw.returnJson(False, '先要绑定域名!')
keyPath = 'ssl/private.pem'
if not os.path.exists(keyPath):
return mw.returnJson(False, '未申请SSL证书!')
# choose_file = self.__file['ssl']
# choose = mw.readFile(choose_file)
# 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"
if not os.path.exists(panel_ssl):
return mw.returnJson(False, '未开启面板SSL!')
# is_https = request.form.get('https', '').strip()
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)
# panel_ssl = mw.getServerDir() + "/web_conf/nginx/vhost/panel.conf"
# if not os.path.exists(panel_ssl):
# return mw.returnJson(False, '未开启面板SSL!')
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 = '开启'
if is_https == 'true':
action = '关闭'
return mw.returnJson(True, action + 'HTTPS跳转成功!')
# mw.restartNginx()
# action = '开启'
# if is_https == 'true':
# action = '关闭'
# return mw.returnJson(True, action + 'HTTPS跳转成功!')
# 删除面板证书
def delPanelSslApi(self):
bind_domain = self.__file['bind_domain']
if not os.path.exists(bind_domain):
return mw.returnJson(False, '未绑定域名!')
ip = mw.getLocalIp()
if mw.isAppleSystem():
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'
dst_csrpath = dst_letpath + '/cert.pem'
dst_keypath = dst_letpath + '/private.pem'
if not mw.inArray(['local','nginx'], choose):
return mw.returnJson(True, '删除错误面板SSL类型!')
if os.path.exists(src_letpath) or os.path.exists(dst_csrpath):
if os.path.exists(src_letpath):
mw.execShell('rm -rf ' + src_letpath)
if os.path.exists(dst_csrpath):
mw.execShell('rm -rf ' + dst_csrpath)
if os.path.exists(dst_keypath):
mw.execShell('rm -rf ' + dst_keypath)
# mw.restartWeb()
return mw.returnJson(True, '已经删除SSL!')
to_panel_url = 'http://'+ip+":"+port+'/config'
# mw.restartWeb()
return mw.returnJson(False, '已经不存在SSL!')
if choose == 'local':
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证书
def applyPanelLetSslApi(self):
# def applyPanelAcmeSslApi(self):
# check domain is bind?
bind_domain = self.__file['bind_domain']
if not os.path.exists(bind_domain):
return mw.returnJson(False, '先要绑定域名!')
# bind_domain = self.__file['bind_domain']
# if not os.path.exists(bind_domain):
# return mw.returnJson(False, '先要绑定域名!')
siteName = mw.readFile(bind_domain).strip()
auth_to = mw.getRunDir() + "/tmp"
to_args = {
'domains': [siteName],
'auth_type': 'http',
'auth_to': auth_to,
}
# # 生成nginx配置
# domain = mw.readFile(bind_domain)
# panel_tpl = mw.getRunDir() + "/data/tpl/nginx_panel.conf"
# dst_panel_path = mw.getServerDir() + "/web_conf/nginx/vhost/panel.conf"
# if not os.path.exists(dst_panel_path):
# 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
src_csrpath = src_letpath + "/fullchain.pem" # 生成证书路径
src_keypath = src_letpath + "/privkey.pem" # 密钥文件路径
# op_dir = mw.getServerDir() + "/openresty"
# if not os.path.exists(op_dir):
# return mw.returnJson(False, '依赖OpenResty,先安装启动它!')
dst_letpath = mw.getRunDir() + '/ssl'
dst_csrpath = dst_letpath + '/cert.pem'
dst_keypath = dst_letpath + '/private.pem'
# 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.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):
import cert_api
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
# src_path = mw.getServerDir() + '/web_conf/letsencrypt/' + siteName
# src_csrpath = src_path + "/fullchain.pem" # 生成证书路径
# src_keypath = src_path + "/privkey.pem" # 密钥文件路径
mw.buildSoftLink(src_csrpath, dst_csrpath, True)
mw.buildSoftLink(src_keypath, dst_keypath, True)
mw.execShell('echo "lets" > "' + dst_letpath + '/README"')
# dst_path = mw.getRunDir() + '/ssl/nginx'
# dst_csrpath = dst_path + '/cert.pem'
# dst_keypath = dst_path + '/private.pem'
data = self.getPanelSslData()
# is_already_apply = False
tmp_well_know = auth_to + '/.well-known'
if os.path.exists(tmp_well_know):
mw.execShell('rm -rf ' + tmp_well_know)
# if not os.path.exists(src_path):
# import cert_api
# 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:
return mw.returnJson(True, '重复申请!', data)
# mw.buildSoftLink(src_csrpath, dst_csrpath, True)
# 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):
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']
port = mw.readFile('data/port.pl').strip()
ip = mw.getLocalIp()
if domain == '':
os.remove(cfg_domain)
os.remove(dst_panel_path)
mw.restartWeb()
return mw.returnJson(True, '清空域名成功!')
if os.path.exists(cfg_domain):
os.remove(cfg_domain)
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, '主域名格式不正确')
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()
to_panel_url = 'http://'+ip+":"+port+'/config'
mw.restartMw()
return mw.returnJson(True, '清空域名成功!', to_panel_url)
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
def setPanelSslApi(self):
sslConf = mw.getRunDir() + '/' + self.__file['ssl']
# def setPanelSslApi(self):
# sslConf = mw.getRunDir() + '/' + self.__file['ssl']
panel_tpl = mw.getRunDir() + "/data/tpl/nginx_panel.conf"
dst_panel_path = mw.getServerDir() + "/web_conf/nginx/vhost/panel.conf"
if os.path.exists(sslConf):
os.system('rm -f ' + sslConf)
# panel_tpl = mw.getRunDir() + "/data/tpl/nginx_panel.conf"
# dst_panel_path = mw.getServerDir() + "/web_conf/nginx/vhost/panel.conf"
# if os.path.exists(sslConf):
# os.system('rm -f ' + sslConf)
conf = mw.readFile(dst_panel_path)
if conf:
rep = "\\s+ssl_certificate\\s+.+;\\s+ssl_certificate_key\\s+.+;"
conf = re.sub(rep, '', conf)
rep = "\\s+ssl_protocols\\s+.+;\n"
conf = re.sub(rep, '', conf)
rep = "\\s+ssl_ciphers\\s+.+;\n"
conf = re.sub(rep, '', conf)
rep = "\\s+ssl_prefer_server_ciphers\\s+.+;\n"
conf = re.sub(rep, '', conf)
rep = "\\s+ssl_session_cache\\s+.+;\n"
conf = re.sub(rep, '', conf)
rep = "\\s+ssl_session_timeout\\s+.+;\n"
conf = re.sub(rep, '', conf)
rep = "\\s+ssl_ecdh_curve\\s+.+;\n"
conf = re.sub(rep, '', conf)
rep = "\\s+ssl_session_tickets\\s+.+;\n"
conf = re.sub(rep, '', conf)
rep = "\\s+ssl_stapling\\s+.+;\n"
conf = re.sub(rep, '', conf)
rep = "\\s+ssl_stapling_verify\\s+.+;\n"
conf = re.sub(rep, '', conf)
rep = "\\s+ssl\\s+on;"
conf = re.sub(rep, '', conf)
rep = "\\s+error_page\\s497.+;"
conf = re.sub(rep, '', conf)
rep = "\\s+if.+server_port.+\n.+\n\\s+\\s*}"
conf = re.sub(rep, '', conf)
rep = "\\s+listen\\s+443.*;"
conf = re.sub(rep, '', conf)
rep = "\\s+listen\\s+\\[\\:\\:\\]\\:443.*;"
conf = re.sub(rep, '', conf)
mw.writeFile(dst_panel_path, conf)
# conf = mw.readFile(dst_panel_path)
# if conf:
# rep = "\\s+ssl_certificate\\s+.+;\\s+ssl_certificate_key\\s+.+;"
# conf = re.sub(rep, '', conf)
# rep = "\\s+ssl_protocols\\s+.+;\n"
# conf = re.sub(rep, '', conf)
# rep = "\\s+ssl_ciphers\\s+.+;\n"
# conf = re.sub(rep, '', conf)
# rep = "\\s+ssl_prefer_server_ciphers\\s+.+;\n"
# conf = re.sub(rep, '', conf)
# rep = "\\s+ssl_session_cache\\s+.+;\n"
# conf = re.sub(rep, '', conf)
# rep = "\\s+ssl_session_timeout\\s+.+;\n"
# conf = re.sub(rep, '', conf)
# rep = "\\s+ssl_ecdh_curve\\s+.+;\n"
# conf = re.sub(rep, '', conf)
# rep = "\\s+ssl_session_tickets\\s+.+;\n"
# conf = re.sub(rep, '', conf)
# rep = "\\s+ssl_stapling\\s+.+;\n"
# conf = re.sub(rep, '', conf)
# rep = "\\s+ssl_stapling_verify\\s+.+;\n"
# conf = re.sub(rep, '', conf)
# rep = "\\s+ssl\\s+on;"
# conf = re.sub(rep, '', conf)
# rep = "\\s+error_page\\s497.+;"
# conf = re.sub(rep, '', conf)
# rep = "\\s+if.+server_port.+\n.+\n\\s+\\s*}"
# conf = re.sub(rep, '', conf)
# rep = "\\s+listen\\s+443.*;"
# conf = re.sub(rep, '', conf)
# rep = "\\s+listen\\s+\\[\\:\\:\\]\\:443.*;"
# conf = re.sub(rep, '', conf)
# mw.writeFile(dst_panel_path, conf)
mw.writeLog('面板配置', '面板SSL关闭成功!')
mw.restartWeb()
return mw.returnJson(True, 'SSL已关闭,请使用http协议访问面板!')
else:
try:
mw.writeFile(sslConf, 'True')
# mw.writeLog('面板配置', '面板SSL关闭成功!')
# mw.restartWeb()
# return mw.returnJson(True, 'SSL已关闭,请使用http协议访问面板!')
# else:
# try:
# mw.writeFile(sslConf, 'True')
keyPath = mw.getRunDir() + '/ssl/private.pem'
certPath = mw.getRunDir() + '/ssl/cert.pem'
# keyPath = mw.getRunDir() + '/ssl/private.pem'
# certPath = mw.getRunDir() + '/ssl/cert.pem'
conf = mw.readFile(dst_panel_path)
if conf:
if conf.find('ssl_certificate') == -1:
sslStr = """#error_page 404/404.html;
ssl_certificate %s;
ssl_certificate_key %s;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:HIGH:!aNULL:!MD5:!RC4:!DHE;
ssl_prefer_server_ciphers on;
ssl_session_cache shared:SSL:10m;
ssl_session_timeout 10m;
error_page 497 https://$host$request_uri;""" % (certPath, keyPath)
if(conf.find('ssl_certificate') != -1):
return mw.returnJson(True, 'SSL开启成功!')
# conf = mw.readFile(dst_panel_path)
# if conf:
# if conf.find('ssl_certificate') == -1:
# sslStr = """#error_page 404/404.html;
# ssl_certificate %s;
# ssl_certificate_key %s;
# ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
# ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:HIGH:!aNULL:!MD5:!RC4:!DHE;
# ssl_prefer_server_ciphers on;
# ssl_session_cache shared:SSL:10m;
# ssl_session_timeout 10m;
# error_page 497 https://$host$request_uri;""" % (certPath, keyPath)
# if(conf.find('ssl_certificate') != -1):
# 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]*;"
tmp = re.findall(rep, conf)
if not mw.inArray(tmp, '443'):
listen = re.search(rep, conf).group()
http_ssl = "\n\tlisten 443 ssl http2;"
http_ssl = http_ssl + "\n\tlisten [::]:443 ssl http2;"
conf = conf.replace(listen, listen + http_ssl)
# rep = "listen\\s+([0-9]+)\\s*[default_server]*;"
# tmp = re.findall(rep, conf)
# if not mw.inArray(tmp, '443'):
# listen = re.search(rep, conf).group()
# http_ssl = "\n\tlisten 443 ssl http2;"
# http_ssl = http_ssl + "\n\tlisten [::]:443 ssl http2;"
# conf = conf.replace(listen, listen + http_ssl)
mw.backFile(dst_panel_path)
mw.writeFile(dst_panel_path, conf)
isError = mw.checkWebConfig()
if(isError != True):
mw.restoreFile(dst_panel_path)
return mw.returnJson(False, '证书错误: <br><a style="color:red;">' + isError.replace("\n", '<br>') + '</a>')
except Exception as ex:
return mw.returnJson(False, '开启失败:' + str(ex))
mw.restartWeb()
return mw.returnJson(True, '开启成功,请使用https协议访问面板!')
# mw.backFile(dst_panel_path)
# mw.writeFile(dst_panel_path, conf)
# isError = mw.checkWebConfig()
# if(isError != True):
# mw.restoreFile(dst_panel_path)
# return mw.returnJson(False, '证书错误: <br><a style="color:red;">' + isError.replace("\n", '<br>') + '</a>')
# except Exception as ex:
# return mw.returnJson(False, '开启失败:' + str(ex))
# mw.restartWeb()
# return mw.returnJson(True, '开启成功,请使用https协议访问面板!')
def getApi(self):
data = {}
@@ -958,7 +1054,7 @@ class config_api:
mw.writeFile(auth, crypt_data)
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['secret'] = sec
+5
View File
@@ -42,8 +42,13 @@ class Sql():
self.__DB_CONN = sqlite3.connect(self.__DB_FILE)
self.__DB_CONN.text_factory = str
except Exception as ex:
# print(mw.getTracebackInfo())
return "error: " + str(ex)
def changeTextFactoryToBytes(self):
self.__DB_CONN.text_factory = bytes
return True
def autoTextFactory(self):
if sys.version_info[0] == 3:
self.__DB_CONN.text_factory = lambda x: str(
+66 -23
View File
@@ -347,6 +347,9 @@ def restartMw():
import system_api
system_api.system_api().restartMw()
def restartNginx(self):
writeFile('data/restart_nginx.pl', 'True')
return True
def checkWebConfig():
op_dir = getServerDir() + '/openresty/nginx'
@@ -1288,25 +1291,46 @@ def checkDomainPanel():
domain = readFile('data/bind_domain.pl')
port = readFile('data/port.pl').strip()
npid = getServerDir() + "/openresty/nginx/logs/nginx.pid"
if not os.path.exists(npid):
scheme = 'http'
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
nconf = getServerDir() + "/web_conf/nginx/vhost/panel.conf"
if os.path.exists(nconf):
port = "80"
local_ssl = getRunDir()+'/ssl/local'
if choose == 'local':
scheme = 'https'
if domain:
client_ip = getClientIp()
if client_ip in ['127.0.0.1', 'localhost', '::1']:
if choose == 'nginx':
# print(port)
npid = getServerDir() + "/openresty/nginx/logs/nginx.pid"
if not os.path.exists(npid):
return False
if tmp.strip().lower() != domain.strip().lower():
from flask import Flask, redirect, request, url_for
to = "http://" + domain + ":" + str(port)
return redirect(to, code=302)
nconf = getServerDir() + "/web_conf/nginx/vhost/panel.conf"
if os.path.exists(nconf):
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
def createLinuxUser(user, group):
execShell("groupadd {}".format(group))
execShell('useradd -s /sbin/nologin -g {} {}'.format(user, group))
@@ -1442,7 +1466,7 @@ def makeConf():
file = getRunDir() + '/data/json/config.json'
if not os.path.exists(file):
c = {}
c['title'] = '老子面板'
c['title'] = '后羿面板'
c['home'] = 'http://github/midoks/mdserver-web'
c['recycle_bin'] = True
c['template'] = 'default'
@@ -1811,28 +1835,38 @@ def getCertName(certPath):
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'):
return True
# if os.path.exists('ssl/local/input.pl'):
# return True
client_ip = getClientIp()
import OpenSSL
key = OpenSSL.crypto.PKey()
key.generate_key(OpenSSL.crypto.TYPE_RSA, 2048)
cert = OpenSSL.crypto.X509()
cert.set_serial_number(0)
cert.get_subject().CN = 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.gmtime_adj_notBefore(0)
cert.gmtime_adj_notAfter(86400 * 3650)
cert.set_pubkey(key)
cert.sign(key, 'md5')
cert_ca = OpenSSL.crypto.dump_certificate(
OpenSSL.crypto.FILETYPE_PEM, cert)
private_key = OpenSSL.crypto.dump_privatekey(
OpenSSL.crypto.FILETYPE_PEM, key)
cert_ca = OpenSSL.crypto.dump_certificate(OpenSSL.crypto.FILETYPE_PEM, cert)
private_key = OpenSSL.crypto.dump_privatekey(OpenSSL.crypto.FILETYPE_PEM, key)
if len(cert_ca) > 100 and len(private_key) > 100:
writeFile('ssl/cert.pem', cert_ca, 'wb+')
writeFile('ssl/private.pem', private_key, 'wb+')
writeFile('ssl/local/cert.pem', cert_ca, 'wb+')
writeFile('ssl/local/private.pem', private_key, 'wb+')
return True
return False
@@ -2076,6 +2110,15 @@ def notifyMessage(msg, stype='common', trigger_time=300, is_write_log=True):
##################### notify end #########################################
def getGlibcVersion():
try:
cmd_result = execShell("ldd --version")[0]
if not cmd_result: return ''
glibc_version = cmd_result.split("\n")[0].split()[-1]
except:
return ''
return glibc_version
##################### ssh start #########################################
def getSshDir():
if isAppleSystem():
+120
View File
@@ -0,0 +1,120 @@
# coding: utf-8
# ---------------------------------------------------------------------------------
# MW-Linux面板
# ---------------------------------------------------------------------------------
# copyright (c) 2018-∞(https://github.com/midoks/mdserver-web) All rights reserved.
# ---------------------------------------------------------------------------------
# Author: midoks <midoks@163.com>
# ---------------------------------------------------------------------------------
# ---------------------------------------------------------------------------------
# SSH终端操作
# ---------------------------------------------------------------------------------
import json
import time
import os
import sys
import socket
import threading
import re
from io import BytesIO, StringIO
import mw
import paramiko
from flask_socketio import SocketIO, emit, send
class ssh_local(object):
__debug_file = 'logs/ssh_local.log'
__log_type = 'SSH终端'
__ssh = None
# lock
_instance_lock = threading.Lock()
def __init__(self):
pass
@classmethod
def instance(cls, *args, **kwargs):
if not hasattr(ssh_local, "_instance"):
with ssh_local._instance_lock:
if not hasattr(ssh_local, "_instance"):
ssh_local._instance = ssh_local(*args, **kwargs)
return ssh_local._instance
def debug(self, msg):
msg = "{} - {}:{} => {} \n".format(mw.formatDate(),
self.__host, self.__port, msg)
if not mw.isDebugMode():
return
mw.writeFile(self.__debug_file, msg, 'a+')
def returnMsg(self, status, msg):
return {'status': status, 'msg': msg}
def connectSsh(self):
import paramiko
ssh = paramiko.SSHClient()
mw.createSshInfo()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
port = mw.getSSHPort()
try:
ssh.connect('127.0.0.1', port, timeout=5)
except Exception as e:
ssh.connect('localhost', port, timeout=5)
except Exception as e:
ssh.connect(mw.getHostAddr(), port, timeout=30)
except Exception as e:
return False
shell = ssh.invoke_shell(term='xterm', width=83, height=21)
shell.setblocking(0)
return shell
def send(self):
pass
def close(self):
try:
if self.__ssh:
self.__ssh.close()
except:
pass
def wsSend(self, recv):
try:
t = recv.decode("utf-8")
return emit('server_response', {'data': t})
except Exception as e:
return emit('server_response', {'data': recv})
def wsSendConnect(self):
return emit('connect', {'data': 'ok'})
def wsSendReConnect(self):
return emit('reconnect', {'data': 'ok'})
def run(self, info):
if not self.__ssh:
self.__ssh = self.connectSsh()
if self.__ssh.exit_status_ready():
self.__ssh = self.connectSsh()
self.__ssh.send(info)
try:
time.sleep(0.005)
recv = self.__ssh.recv(8192)
return self.wsSend(recv)
except Exception as ex:
return self.wsSend('')
+13 -2
View File
@@ -28,9 +28,9 @@ import paramiko
from flask_socketio import SocketIO, emit, send
class ssh_terminal:
class ssh_terminal(object):
__debug_file = 'logs/terminal.log'
__debug_file = 'logs/ssh_terminal.log'
__log_type = 'SSH终端'
# websocketio 唯一标识
@@ -55,10 +55,21 @@ class ssh_terminal:
__ssh_list = {}
__ssh_last_request_time = {}
# lock
_instance_lock = threading.Lock()
def __init__(self):
ht = threading.Thread(target=self.heartbeat)
ht.start()
@classmethod
def instance(cls, *args, **kwargs):
if not hasattr(ssh_terminal, "_instance"):
with ssh_terminal._instance_lock:
if not hasattr(ssh_terminal, "_instance"):
ssh_terminal._instance = ssh_terminal(*args, **kwargs)
return ssh_terminal._instance
def debug(self, msg):
msg = "{} - {}:{} => {} \n".format(mw.formatDate(),
self.__host, self.__port, msg)
+9
View File
@@ -64,6 +64,11 @@ mw_start_debug2(){
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()
{
@@ -96,4 +101,8 @@ case "$1" in
mw_stop
mw_start_debug2
;;
'debug3')
mw_stop
mw_start_debug3
;;
esac
+349 -7
View File
@@ -1948,7 +1948,7 @@ def addMasterRepSlaveUser(version=''):
if isError != None:
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('FLUSH PRIVILEGES;')
@@ -2583,6 +2583,293 @@ def dumpMysqlData(version=''):
return 'ok'
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=''):
which_pv = mw.execShell('which pv')
is_exist_pv = False
if os.path.exists(which_pv[0]):
is_exist_pv = True
args = getArgs()
data = checkArgs(args, ['db', 'sign'])
if not data[0]:
return data[1]
time_all_s = time.time()
sync_db = args['db']
sync_sign = args['sign']
@@ -2675,23 +2969,67 @@ def doFullSyncUser(version=''):
os.system("rm -rf " + bak_file)
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})
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):
dmp_option += " --master-data=1 --apply-slave-statements --include-master-host-port "
# 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
print(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):
pwd = pSqliteDb('config').where('id=?', (1,)).getField('mysql_root')
sock = getSocketFile()
my_import_cmd = getServerDir() + '/bin/mariadb -S ' + sock + " -uroot -p'" + pwd + \
"' " + sync_db + '<' + bak_file
print(my_import_cmd)
mw.execShell(my_import_cmd)
if is_exist_pv:
my_import_cmd = getServerDir() + '/bin/mariadb -S ' + sock + " -uroot -p'" + pwd + "' " + sync_db
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'])
# print(pinfo)
@@ -3026,5 +3364,9 @@ if __name__ == "__main__":
print(doFullSync(version))
elif func == 'dump_mysql_data':
print(dumpMysqlData(version))
elif func == 'sync_database_repair':
print(syncDatabaseRepair())
elif func == 'sync_database_repair_log':
print(syncDatabaseRepairLog())
else:
print('error')
+6
View File
@@ -7,6 +7,12 @@ rootPath=$(dirname "$curPath")
rootPath=$(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
+81 -2
View File
@@ -874,7 +874,7 @@ function openPhpmyadmin(name,username,password){
}
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;
}
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=''){
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 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="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>';
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 " +
# 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
mw.execShell(cmd)
+2 -4
View File
@@ -3075,9 +3075,7 @@ def syncDatabaseRepairLog(version=''):
if op == 'do':
os.system(' echo "开始执行" > '+ tmp_log)
subprocess.Popen(cmd +' >> '+ tmp_log +' &')
# time.sleep(10)
# mw.execShell('rm -rf '+tmp_log)
os.system(cmd +' >> '+ tmp_log +' &')
return mw.returnJson(True, 'ok')
return mw.returnJson(False, '无效请求!')
@@ -3392,7 +3390,7 @@ def doFullSyncUserImportContentForChannel(file, channel_name):
def doFullSyncUser(version=''):
which_pv = mw.execShell('which pv')
is_exist_pv = False
if not os.path.exists(which_pv[0]):
if os.path.exists(which_pv[0]):
is_exist_pv = True
time_all_s = time.time()
+3 -3
View File
@@ -871,7 +871,7 @@ function openPhpmyadmin(name,username,password){
}
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;
}
var home_page = rdata.data['home_page'];
@@ -2936,10 +2936,10 @@ function masterOrSlaveConf(version=''){
getMasterDbList();
}
if (rdata.slave_status){
// if (rdata.slave_status){
getAsyncMasterDbList();
getAsyncDataList()
}
// }
});
}
getMasterStatus();
+3 -3
View File
@@ -26,7 +26,7 @@ function phpPost(method, version, args,callback){
},'json');
}
function phpPostCallbak(method, version, args,callback){
function phpPostCallback(method, version, args,callback){
var loadT = layer.msg('正在获取...', { icon: 16, time: 0, shade: 0.3 });
var req_data = {};
@@ -715,7 +715,7 @@ function getPHPInfo_old(version) {
}
function getPHPInfo(version) {
phpPostCallbak('get_php_info', version, {}, function(data){
phpPostCallback('get_php_info', version, {}, function(data){
if (!data.status){
layer.msg(rdata.msg, { icon: 2 });
return;
@@ -739,7 +739,7 @@ function phpLibConfig(version){
// var rdata = $.parseJSON(rdata.data);
// });
phpPostCallbak('get_lib_conf', version, {}, function(rdata){
phpPostCallback('get_lib_conf', version, {}, function(rdata){
var rdata = rdata.data;
if (!rdata.status){
+6 -2
View File
@@ -210,13 +210,17 @@ def __delete_port(port):
def openPort():
for i in ["888"]:
conf = getCfg()
port = conf['port']
for i in [port]:
__release_port(i)
return True
def delPort():
for i in ["888"]:
conf = getCfg()
port = conf['port']
for i in [port]:
__delete_port(i)
return True
+7 -5
View File
@@ -111,6 +111,7 @@ function autoMakeConf(){
</div>\
<ul class='help-info-text c7'>\
<li style='color:red;'>具体配置,仍须手动修改!!!</li>'\
<li style='color:red;'>增量索引,需要有更新权限,主从分离时,需要主库配置</li>'\
</ul>\
</form>\
",
@@ -286,7 +287,6 @@ function readme(){
con += '<li style="color:red;">如果数据量比较大,第一次启动会失败!(可通过手动建立索引)</li>';
con += '<li style="color:red;">以下内容,需手动加入计划任务。</li>';
con += '<li>全量:' + rdata['data']['cmd'] + ' --all --rotate</li>';
@@ -294,11 +294,13 @@ function readme(){
for (var i = 0; i < rdata['data']['index'].length; i++) {
var index_kv = rdata['data']['index'][i];
var index = index_kv['index'];
var delta = index_kv['delta'];
// console.log(index,delta);
// console.log(index);
con += '<li>主索引 :' + rdata['data']['cmd'] + ' '+ index +' --rotate</li>';
con += '<li>增量索引 :' + rdata['data']['cmd'] + ' '+ delta +' --rotate</li>';
con += '<li>合并索引 :' + rdata['data']['cmd'] + ' --merge '+ index + ' ' + delta +' --rotate</li>';
if (typeof(index_kv['delta']) != 'undefined'){
var delta = index_kv['delta'];
con += '<li>增量索引 :' + rdata['data']['cmd'] + ' '+ delta +' --rotate</li>';
con += '<li>合并索引 :' + rdata['data']['cmd'] + ' --merge '+ index + ' ' + delta +' --rotate</li>';
}
}
con += '</ul>';
Binary file not shown.

After

Width:  |  Height:  |  Size: 455 B

+553
View File
@@ -0,0 +1,553 @@
<style>
/*任务管理器*/
.man-menu-sub {
background-color: #fff;
border-bottom: 1px solid #ccc;
height: 50px;
display: flex;
flex-direction: row;
justify-content: space-between;
flex-wrap: nowrap;
align-items: center;
}
.t-mana .table > tbody > tr.active td {
background-color: #E4EEE0;
}
.search-bar {
}
.search-bar .search_input {
height: 30px;
line-height: 30px;
border-radius: 2px;
border: 1px solid #ccc;
outline: none;
padding-left: 8px;
vertical-align: top;
width: 230px;
}
.search-bar .search_input:focus {
border-color: #20a53a;
span.glyphicon-search {
color: #20a53a;
}
}
.search-bar .glyphicon {
position: relative;
top: 1px;
display: inline-block;
font-family: 'Glyphicons Halflings';
font-style: normal;
font-weight: 400;
line-height: 1;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
}
.search-bar .glyphicon-search {
height: 28px;
line-height: 28px;
padding: 0 10px;
color: #888;
position: absolute;
right: 14px;
top: 11px;
font-size: 14px;
cursor: pointer;
}
.r56 {
right: 56px !important;
}
.man-menu-sub {
background-color: #fff;
border-bottom: 1px solid #ccc;
height: 50px;
}
.man-menu-sub span {
padding: 14px 15px;
cursor: pointer;
display: inline-block;
font-size: 14px;
}
.man-menu-sub .on {
border-bottom: 2px solid #20a53a;
color: #20a53a;
font-weight: bold;
}
.soft-ico-min {
display: inline-block;
width: 16px;
margin-right: 5px;
}
.soft-ico-min img {
width: 16px;
}
.table-cont {
height: 500px;
overflow: auto;
border: #ddd 1px solid;
position: relative;
}
.ts-line {
position: absolute;
top: 99px;
height: 1px;
background-color: #ddd;
left: 16px;
z-index: 2;
}
.t-mana .bt-ico-ask {
height: 14px;
width: 14px;
line-height: 14px;
}
.mini-info {
width: 100%;
border-top: #DBDBEA 1px solid;
background-color: #f6f6f6;
padding: 15px;
}
.mini-info-box {
width: 500px;
margin: 0 auto;
background-color: #fff;
border: #DBDBEA 1px solid;
border-radius: 6px;
height: 66px;
box-shadow: 0 0 40px #eee;
}
.mini-info-con {
float: left;
height: 100%;
width: 33%;
line-height: 26px;
padding: 6px 16px;
}
.mini-info-con:nth-of-type(2) {
border-left: #DBDBEA 1px solid;
border-right: #DBDBEA 1px solid;
width: 34%;
}
.mini-info-con p {
text-align: right;
position: relative;
}
.mini-info-con .tname {
display: inline-block;
position: absolute;
left: 0;
}
.mini-level {
border: #ddd 1px solid;
width: 166px;
background-color: #fff;
border-radius: 6px;
margin: 0 auto;
height: 66px;
line-height: 66px;
text-align: center;
}
.jc-detail .tname {
font-size: 12px;
font-weight: bold;
margin-right: 10px;
}
.jc-detail h3.tname {
margin-bottom: 10px;
margin-top: 15px;
color: #999;
}
.jc-detail .jc-info {
margin: 10px 0;
border-bottom: #ddd 1px solid;
padding-bottom: 10px;
}
#TaskManagement .td-pid {
/* display: flex; */
/* border-top: #ddd 2px solid; */
/* height: 38px; */
/* height: 100%; */
/* margin-top: 1px; */
}
.td-pid img {
/* margin-right: 8px; */
/* display: inline-block; */
float: left;
cursor: pointer;
/* position: relative;
left: -80px;
filter: drop-shadow(#fff 80px 0); */
/* position: relative;
left: -80px;
filter: drop-shadow(#238B37 0 80px); */
}
.td-pid svg {
float: left;
cursor: pointer;
}
.td-pid .colp {
transform: rotate(-90deg);
-moz-transform: rotate(-90deg);
-webkit-transform: rotate(-90deg);
}
.td-pid .arrow:hover {
filter: drop-shadow(#999999 0 0)
}
.td-pid .process-child {
margin-left: 50px;
/* display: none; */
}
.td-pid span {
display: inline-block;
float: right;
}
.td-pid a {
display: block;
margin-left: 30px;
}
#TaskManagement th: {
height: 34px;
}
#TaskManagement tr: {
height: 38px;
}
#TaskManagement tr:hover {
/* background-color: rgb(245, 247, 250,0.6); */
background-color: rgb(246, 246, 246, 0.65);
}
#TaskManagement .process-select {
/* background-color: rgb(242, 242, 242); */
/* background-color: rgb(245, 247, 250,0.6); */
background-color: rgb(246, 246, 246, 0.6);
/* background-color: rgb(246, 246, 246,0.65); */
}
#taskResourceTable .process-select {
/* background-color: rgb(242, 242, 242); */
background-color: #F1F9F3;
}
#TaskManagement .process-select:hover {
/* background-color: rgb(211, 210, 210); */
/* background-color: rgb(245, 247, 250); */
background-color: rgb(246, 246, 246);
/* border: 1px solid #ddd;
box-sizing: border-box; */
}
#taskResourceTable .process-select:hover {
/* background-color: rgb(211, 210, 210); */
background-color: #BCE4C4;
}
.resource-panel {
display: none;
width: 100%;
height: 700px;
/* height: 300px; */
}
.resource-tab {
float: left;
width: 100px;
height: 300px;
background-color: thistle;
}
.resource-right {
margin-left: 100px;
/* width: 100%; */
/* background-color: gray; */
/* height: 600px; */
}
.resource-panel-show {
display: block;
}
.divtable-hide {
display: none;
/* visibility: hidden; */
}
.resource-tab span {
display: block;
height: 30px;
line-height: 30px;
text-align: center;
cursor: pointer;
}
.resource-tab span:hover {
background-color: #ccc;
}
.plug_menu {
display: none;
}
.plug_menu .setting_ul::after{
display: none;
}
.plug_menu .setting_ul.undisplay::after {
content: '';
position: absolute;
color: #fff;
width: 12px;
height: 12px;
font-size: 14px;
top: -7px;
right: 8px;
display: inline-block;
background: #fff;
transform: rotate(-45deg);
border-top: 1px solid rgba(0, 0, 0, 0.25);
border-right: 1px solid rgba(0, 0, 0, 0.25);
}
.set_list_fid_dropdown {
display: inline-block;
line-height: 30px;
margin-left: 10px;
position: relative;
vertical-align: bottom;
}
.set_list_fid_dropdown .setting_btn {
border: 1px solid #ccc;
height: 30px;
width: 30px;
border-radius: 4px;
display: flex;
align-items: center;
justify-content: center;
cursor: pointer;
}
.set_list_fid_dropdown .setting_btn:hover{
background: #e9f6eb;
border-color: #bce4c4;
}
.set_list_fid_dropdown .icon-setting {
color: rgb(102, 102, 102);
}
.set_list_fid_dropdown .setting_btn:hover .icon-setting {
color: #20a53a;
}
.set_list_fid_dropdown .setting_ul {
position: absolute;
z-index: 99;
background: #fff;
width: 150px;
right: 0;
border-radius: 4px;
box-shadow: 0px 0px 4px rgba(0, 0, 0, 0.25);
padding: 10px 0;
margin-top: 10px;
display: none;
}
.set_list_fid_dropdown .setting_ul::after {
content: '';
position: absolute;
color: #fff;
width: 12px;
height: 12px;
font-size: 14px;
top: -7px;
right: 8px;
display: inline-block;
background: #fff;
transform: rotate(-45deg);
border-top: 1px solid rgba(0, 0, 0, 0.25);
border-right: 1px solid rgba(0, 0, 0, 0.25);
}
.setting_ul .setting_ul_li{
display: flex;
align-items: center;
line-height: 27px;
padding: 0 16px;
cursor: pointer;
}
.setting_ul .setting_ul_li:hover{
background: #e9f6eb;
}
.setting_ul .setting_ul_li.disabled {
cursor: not-allowed;
}
.setting_ul .setting_ul_li i {
display: inline-block;
width: 16px;
height: 16px;
border: 1px solid #ccc;
background-color: #fff;
vertical-align: top;
position: relative;
border-radius: 2px;
}
.setting_ul .setting_ul_li.active i {
background-color: #20a53a;
border-color: #20a53a;
}
.setting_ul .setting_ul_li.disabled i {
background-color: #edf2fc;
border-color: #dcdfe6;
}
.setting_ul .setting_ul_li.active.disabled i {
background-color: #f2f6fc;
border-color: #dcdfe6;
}
.setting_ul .setting_ul_li.active.disabled i::after{
border: solid #dcdfe6;
border-width: 0 2px 2px 0;
}
.setting_ul .setting_ul_li.active i::after{
content: '';
position: absolute;
display: block;
left: 50%;
top: 50%;
margin-left: -2px;
margin-top: -6px;
width: 5px;
height: 10px;
border: solid #fff;
border-width: 0 2px 2px 0;
transform: rotate(45deg);
}
</style>
<div class="t-mana TaskManView">
<div class="man-menu-sub mlr15">
<div>
<span class="p_list on">进程</span>
<span class="p_run">启动项</span>
<span class="p_service">服务</span>
<span class="p_network">网络</span>
<span class="p_user">用户</span>
<span class="p_cron">计划任务</span>
<span class="p_session">会话</span>
</div>
<div style="display: flex;flex-direction: row;align-items: center;justify-content: flex-end;">
<div class="search-bar ">
<input type="text" class="search_input " style="" placeholder="支持名称、字段模糊搜索">
<span class="glyphicon glyphicon-search r56" aria-hidden="true"></span>
</div>
<div class="table_config set_list_fid_dropdown" title="设置表头">
<div class="setting_btn"><i class="glyphicon glyphicon-cog icon-setting"></i></div>
</div>
</div>
</div>
<div class="ts-line"></div>
<div class="divtable taskdivtable pd15">
<div id="table-cont" class="table-cont">
<table id="TaskManagement" class="table table-hover" width="100%" style="border:none"></table>
</div>
</div>
<div class="resource-panel pd15">
<div class="resource-tab">
<span>CPU</span>
<span>内存</span>
<span>磁盘</span>
</div>
<div class="resource-right">
<div class="resource-top"></div>
<div class="resource-middle divtable">
<div id="table-cont" class="table-cont" style="height:100px;">
<table id="taskResourceTable" class="table table-hover" width="100%" style="border:none">
</table>
</div>
</div>
<div class="resource-bottom"></div>
</div>
</div>
<div class="mini-info" id="load_average"></div>
<div class="plug_menu set_list_fid_dropdown">
<ul class="setting_ul" style="display: none;">
<li class="setting_ul_li ps active disabled" name="ps">
<i></i><span class="ml10">应用名称</span>
</li>
<li class="setting_ul_li pid active disabled" name="pid">
<i></i> <span class="ml10">PID</span>
</li>
<li class="setting_ul_li threads active" name="threads">
<i></i> <span class="ml10">线程</span>
</li>
<li class="setting_ul_li user active" name="user">
<i></i> <span class="ml10">用户</span>
</li>
<li class="setting_ul_li cpu_percent active disabled" name="cpu_percent">
<i></i> <span class="ml10">CPU</span>
</li>
<li class="setting_ul_li memory_used active disabled" name="memory_used">
<i></i> <span class="ml10">内存</span>
</li>
<li class="setting_ul_li io_read_bytes active" name="io_read_bytes">
<i></i> <span class="ml10">io读</span>
</li>
<li class="setting_ul_li io_write_bytes active" name="io_write_bytes">
<i></i> <span class="ml10">io写</span>
</li>
<li class="setting_ul_li up active" name="up">
<i></i> <span class="ml10">上行</span>
</li>
<li class="setting_ul_li down active" name="down">
<i></i> <span class="ml10">下行</span>
</li>
<li class="setting_ul_li connects active" name="connects">
<i></i> <span class="ml10">连接</span>
</li>
<li class="setting_ul_li status active" name="status">
<i></i> <span class="ml10">状态</span>
</li>
<li class="setting_ul_li active disabled" name="">
<i></i> <span class="ml10">操作</span>
</li>
</ul>
</div>
</div>
<script type="text/javascript">
$('.layui-layer-page').css({'width': '1000px'});
$.getScript( "/plugins/file?name=task_manager&f=js/task_manager.js", function(){
console.log("load js ok!");
});
</script>
+62
View File
@@ -0,0 +1,62 @@
# coding:utf-8
import sys
import io
import os
import time
import re
sys.path.append(os.getcwd() + "/class/core")
import mw
app_debug = False
if mw.isAppleSystem():
app_debug = True
def getPluginName():
return 'task_manager'
def getPluginDir():
return mw.getPluginDir() + '/' + getPluginName()
def getServerDir():
return mw.getServerDir() + '/' + getPluginName()
def getArgs():
args = sys.argv[3:]
tmp = {}
args_len = len(args)
if args_len == 1:
t = args[0].strip('{').strip('}')
if t.strip() == '':
tmp = []
else:
t = t.split(':')
tmp[t[0]] = t[1]
tmp[t[0]] = t[1]
elif args_len > 1:
for i in range(len(args)):
t = args[i].split(':')
tmp[t[0]] = t[1]
return tmp
def checkArgs(data, ck=[]):
for i in range(len(ck)):
if not ck[i] in data:
return (False, mw.returnJson(False, '参数:(' + ck[i] + ')没有!'))
return (True, mw.returnJson(True, 'ok'))
def status():
return 'start'
if __name__ == "__main__":
func = sys.argv[1]
if func == 'status':
print(status())
else:
print('error')
+17
View File
@@ -0,0 +1,17 @@
{
"sort": 7,
"ps": "轻松管理进程、流量监控、启动项、用户、服务、计划任务、会话",
"name": "task_manager",
"title": "任务管理器",
"shell": "install.sh",
"versions":"1.0",
"tip": "soft",
"checks": "server/task_manager",
"path": "server/task_manager",
"display": 1,
"author": "task_manager",
"date": "2024-06-01",
"home": "task_manager",
"type": 0,
"pid": "4"
}
+55
View File
@@ -0,0 +1,55 @@
#!/bin/bash
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin:/opt/homebrew/bin
export PATH
curPath=`pwd`
rootPath=$(dirname "$curPath")
rootPath=$(dirname "$rootPath")
serverPath=$(dirname "$rootPath")
install_tmp=${rootPath}/tmp/mw_install.pl
VERSION=$2
# python3 plugins/task_manager/task_manager_index.py
# /www/server/mdserver-web/bin/python3 /www/server/mdserver-web/plugins/task_manager/process_network_total.py
# ps -ef|grep process_network_total| grep -v grep | awk '{print $2}' | xargs kill -9
if [ -f ${rootPath}/bin/activate ];then
source ${rootPath}/bin/activate
fi
Install_App()
{
echo '正在安装脚本文件...' > $install_tmp
mkdir -p $serverPath/task_manager
if [ -f /usr/bin/apt ]; then
apt install libpcap-dev -y
fi
if [ -f /usr/bin/yum ]; then
yum install libpcap-devel -y
fi
if [ -f /usr/bin/dnf ]; then
dnf install libpcap-devel -y
fi
pip3 install pypcap
echo "$VERSION" > $serverPath/task_manager/version.pl
echo "安装任务管理器成功"
}
Uninstall_App()
{
rm -rf $serverPath/task_manager
echo "卸载任务管理器成功"
}
action=$1
if [ "${1}" == 'install' ];then
Install_App
else
Uninstall_App
fi
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,230 @@
#coding: utf-8
import sys
import time
import os
import struct
os.chdir('/www/server/mdserver-web')
if 'class/' in sys.path: sys.path.insert(0,"class/")
import copy
try:
import pcap
except ImportError:
if os.path.exists('/usr/bin/apt'):
os.system("apt install libpcap-dev -y")
elif os.path.exists('/usr/bin/dnf'):
red_file = '/etc/redhat-release'
if os.path.exists(red_file):
f = open(red_file,'r')
red_body = f.read()
f.close()
if red_body.find('CentOS Linux release 8.') != -1:
rpm_file = '/root/libpcap-1.9.1.rpm'
down_url = "wget -O {} https://repo.almalinux.org/almalinux/8/PowerTools/x86_64/os/Packages/libpcap-devel-1.9.1-5.el8.x86_64.rpm --no-check-certificate -T 10".format(rpm_file)
print(down_url)
os.system(down_url)
os.system("rpm -ivh {}".format(rpm_file))
if os.path.exists(rpm_file): os.remove(rpm_file)
else:
os.system("dnf install libpcap-devel -y")
else:
os.system("dnf install libpcap-devel -y")
elif os.path.exists('/usr/bin/yum'):
os.system("yum install libpcap-devel -y")
os.system("pip install pypcap")
try:
import pcap
except ImportError:
print("pypcap module install failed.")
sys.exit()
class process_network_total:
__pid_file = 'logs/process_network_total.pid'
__inode_list = {}
__net_process_list = {}
__net_process_size = {}
__last_stat = 0
__last_write_time = 0
__end_time = 0
def start(self,timeout = 0):
stime = time.time()
self.__end_time = timeout + stime
self.__last_stat = stime
try:
p = pcap.pcap() # 监听所有网卡
p.setfilter('tcp') # 只监听TCP数据包
for p_time,p_data in p:
self.handle_packet(p_data)
# 过期停止监听
if timeout > 0:
if p_time > self.__end_time:
self.rm_pid_file()
break
except:
self.rm_pid_file()
def handle_packet(self, pcap_data):
# 获取IP协议头
ip_header = pcap_data[14:34]
# 解析src/dst地址
src_ip = ip_header[12:16]
dst_ip = ip_header[16:20]
# 解析sport/dport端口
src_port = pcap_data[34:36]
dst_port = pcap_data[36:38]
src = src_ip + b':' + src_port
dst = dst_ip + b':' + dst_port
# 计算数据包长度
pack_size = len(pcap_data)
# 统计进程流量
self.total_net_process(dst,src,pack_size)
def total_net_process(self,dst,src,pack_size):
self.get_tcp_stat()
direction = None
mtime = time.time()
if dst in self.__net_process_list:
pid = self.__net_process_list[dst]
direction = 'down'
elif src in self.__net_process_list:
pid = self.__net_process_list[src]
direction = 'up'
else:
if mtime - self.__last_stat > 3:
self.__last_stat = mtime
self.get_tcp_stat(True)
if dst in self.__net_process_list:
pid = self.__net_process_list[dst]
direction = 'down'
elif src in self.__net_process_list:
pid = self.__net_process_list[src]
direction = 'up'
if not direction: return False
if not pid: return False
if not pid in self.__net_process_size:
self.__net_process_size[pid] = {}
self.__net_process_size[pid]['down'] = 0
self.__net_process_size[pid]['up'] = 0
self.__net_process_size[pid]['up_package'] = 0
self.__net_process_size[pid]['down_package'] = 0
self.__net_process_size[pid][direction] += pack_size
self.__net_process_size[pid][direction + '_package'] += 1
# 写入到文件
if mtime - self.__last_write_time > 1:
self.__last_write_time = mtime
self.write_net_process()
def write_net_process(self):
w_file = '/dev/shm/mw_net_process'
process_size = copy.deepcopy(self.__net_process_size)
net_process = []
for pid in process_size.keys():
net_process.append(str(pid) + " " + str(process_size[pid]['down']) + " " + str(process_size[pid]['up']) + " " + str(process_size[pid]['down_package']) + " " + str(process_size[pid]['up_package']))
f = open(w_file,'w+',encoding='utf-8')
f.write('\n'.join(net_process))
f.close()
def hex_to_ip(self, hex_ip):
hex_ip,hex_port = hex_ip.split(':')
ip = '.'.join([str(int(hex_ip[i:i+2], 16)) for i in range(0, len(hex_ip), 2)][::-1])
port = int(hex_port, 16)
return ip,port
def get_tcp_stat(self,force = False):
if not force and self.__net_process_list: return self.__net_process_list
self.__net_process_list = {}
tcp_stat_file = '/proc/net/tcp'
tcp_stat = open(tcp_stat_file, 'rb')
tcp_stat_list = tcp_stat.read().decode('utf-8').split('\n')
tcp_stat.close()
tcp_stat_list = tcp_stat_list[1:]
if force: self.get_process_inodes(force)
for i in tcp_stat_list:
tcp_tmp = i.split()
if len(tcp_tmp) < 10: continue
inode = tcp_tmp[9]
if inode == '0': continue
local_ip,local_port = self.hex_to_ip(tcp_tmp[1])
if local_ip == '127.0.0.1': continue
remote_ip,remote_port = self.hex_to_ip(tcp_tmp[2])
if local_ip == remote_ip: continue
if remote_ip == '0.0.0.0': continue
pid = self.inode_to_pid(inode,force)
if not pid: continue
key = self.get_ip_pack(local_ip) + b':' + self.get_port_pack(local_port)
self.__net_process_list[key] = pid
return self.__net_process_list
def get_port_pack(self,port):
return struct.pack('H',int(port))[::-1]
def get_ip_pack(self,ip):
ip_arr = ip.split('.')
ip_pack = b''
for i in ip_arr:
ip_pack += struct.pack('B',int(i))
return ip_pack
def inode_to_pid(self,inode,force = False):
inode_list = self.get_process_inodes()
if inode in inode_list:
return inode_list[inode]
return None
def get_process_inodes(self,force = False):
if not force and self.__inode_list: return self.__inode_list
proc_path = '/proc'
inode_list = {}
for pid in os.listdir(proc_path):
try:
if not pid.isdigit(): continue
inode_path = proc_path + '/' + pid + '/fd'
for fd in os.listdir(inode_path):
try:
fd_file = inode_path + '/' + fd
fd_link = os.readlink(fd_file)
if fd_link.startswith('socket:['):
inode = fd_link[8:-1]
inode_list[inode] = pid
except:
continue
except:
continue
self.__inode_list = inode_list
return inode_list
def get_process_name(self,pid):
pid_path = '/proc/' + pid + '/comm'
if not os.path.exists(pid_path): return ''
pid_file = open(pid_path, 'rb')
pid_name = pid_file.read().decode('utf-8').strip()
pid_file.close()
return pid_name
def write_pid(self):
self_pid = os.getpid()
pid_file = open(self.__pid_file,'w')
pid_file.write(str(self_pid))
pid_file.close()
def rm_pid_file(self):
if os.path.exists(self.__pid_file):
os.remove(self.__pid_file)
if __name__ == '__main__':
p = process_network_total()
p.write_pid()
p.start(600)
File diff suppressed because it is too large Load Diff
+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 request_time_idx ON web_logs (`request_time`);
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`);
@@ -84,6 +85,7 @@ CREATE TABLE `request_stat`(
`status_402` INTEGER DEFAULT 0,
`status_403` INTEGER DEFAULT 0,
`status_404` INTEGER DEFAULT 0,
`status_499` INTEGER DEFAULT 0,
`http_get` INTEGER DEFAULT 0,
`http_post` INTEGER DEFAULT 0,
`http_put` INTEGER DEFAULT 0,
+16
View File
@@ -791,7 +791,23 @@ def getLogsList():
attacHistoryLogHack(conn, domain, query_date)
conn.changeTextFactoryToBytes()
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)
count_key = "count(*) as num"
count = conn.field(count_key).limit('').order('').inquiry()
+16 -11
View File
@@ -683,8 +683,7 @@ function wsSitesListRequest(page){
var stat_length = 0;
var stat_req = 0;
console.log(rdata, data);
// console.log(rdata, data);
var list = '';
if (data.length > 0){
for(i in data){
@@ -2257,11 +2256,13 @@ function wsTableLogRequest(page){
}
$('#logs_search').attr('req','start');
// wsPost('get_logs_list', '' ,args, function(rdata){
wsPostCallbak('get_logs_list', '' ,args, function(rdata){
$('#logs_search').attr('req','end');
var rdata = $.parseJSON(rdata.data);
var list = '';
var data = rdata.data.data;
// console.log(data);
if (data.length > 0){
for(i in data){
@@ -2346,20 +2347,23 @@ function wsTableLogRequest(page){
var index = $(this).attr('data-id');
var res = data[index];
var request_headers = res.request_headers;
var req_data = $.parseJSON(request_headers);
var req_data_html = res.method +' ' + res.uri + '<br/>';
for (var d in req_data) {
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/>";
try {
var req_data = $.parseJSON(request_headers);
for (var d in req_data) {
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({
type: 1,
title: "【"+res.domain + "】HTTP详情",
@@ -2416,8 +2420,9 @@ var html = '<div>\
<option value="500">500</option>\
<option value="502">502</option>\
<option value="503">503</option>\
<option value="499">499</option>\
<option value="400">400</option>\
<option value="404">404</option>\
<option value="499">499</option>\
<option value="301">301</option>\
<option value="302">302</option>\
<option value="200">200</option>\
+25
View File
@@ -616,7 +616,32 @@ function _M.statistics_request(self, ip, is_spider, body_length)
-- 计算pv uv
local pvc = 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
local ua = ''
if request_header['user-agent'] then
+2 -2
View File
@@ -260,7 +260,7 @@ log_by_lua_block {
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
request_stat_fields = request_stat_fields .. ","..field.."="..field.."+1"
end
@@ -392,7 +392,7 @@ log_by_lua_block {
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
request_stat_fields[field] = 1
end
+15 -1
View File
@@ -791,8 +791,22 @@ def get_logs_list(args):
attacHistoryLogHack(conn, domain, query_date)
conn.changeTextFactoryToBytes()
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 = conn.field(count_key).limit('').order('').inquiry()
# print(count)
+2 -2
View File
@@ -1,7 +1,7 @@
setuptools>=33.1.1
Werkzeug>=1.0.1,<3.0.0
wheel>=0.37.1
gunicorn==22.0.0
gunicorn>=21.2.0
requests>=2.27.1
urllib3>=1.21.1
flask>=2.0.3,<3.0.0
@@ -29,7 +29,7 @@ pymemcache
redis
pillow
Jinja2>=2.11.2
PyMySQL==1.1.1
PyMySQL>=1.0.2
whitenoise==5.3.0
pyotp
pytz
+8 -29
View File
@@ -60,8 +60,7 @@ try:
sdb.create_all()
except:
app.config['SESSION_TYPE'] = 'filesystem'
app.config['SESSION_FILE_DIR'] = '/tmp/py_mw_session_' + \
str(sys.version_info[0])
app.config['SESSION_FILE_DIR'] = '/tmp/py_mw_session_' + str(sys.version_info[0])
app.config['SESSION_FILE_THRESHOLD'] = 1024
app.config['SESSION_FILE_MODE'] = 384
@@ -296,9 +295,6 @@ def code():
out = io.BytesIO()
codeImage[0].save(out, "png")
# print(codeImage[1])
session['code'] = mw.md5(''.join(codeImage[1]).lower())
img = Response(out.getvalue(), headers={'Content-Type': 'image/png'})
@@ -599,9 +595,6 @@ def index(reqClass=None, reqAction=None, reqData=None):
##################### ssh start ###########################
shell = None
shell_client = None
@socketio.on('webssh_websocketio')
def webssh_websocketio(data):
@@ -609,35 +602,21 @@ def webssh_websocketio(data):
emit('server_response', {'data': '会话丢失,请重新登陆面板!\r\n'})
return
global shell_client
if not shell_client:
import ssh_terminal
shell_client = ssh_terminal.ssh_terminal()
import ssh_terminal
shell_client = ssh_terminal.ssh_terminal.instance()
shell_client.run(request.sid, data)
return
@socketio.on('webssh')
def webssh(msg):
global shell
def webssh(data):
if not isLogined():
emit('server_response', {'data': '会话丢失,请重新登陆面板!\r\n'})
return None
if not shell:
shell = mw.connectSsh()
if shell.exit_status_ready():
shell = mw.connectSsh()
if shell:
shell.send(msg)
try:
time.sleep(0.005)
recv = shell.recv(4096)
emit('server_response', {'data': recv.decode("utf-8")})
except Exception as ex:
emit('server_response', {'data': str(ex)})
import ssh_local
shell = ssh_local.ssh_local.instance()
shell.run(data)
return
##################### ssh end ###########################
+222 -58
View File
@@ -75,16 +75,106 @@ $('input[name="bind_domain"]').change(function(){
$('.btn_bind_domain').removeAttr('disabled');
$('.btn_bind_domain').unbind().click(function(){
$.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');
});
});
$('input[name="bind_ssl"]').click(function(){
var open_ssl = $(this).prop("checked");
$.post('/config/set_panel_ssl',{}, function(rdata){
showMsg(rdata.msg,function(){window.location.reload();},{icon:rdata.status?1:2},2000);
},'json');
var panel_ssl = $(this).prop("checked");
$(this).prop("checked",!panel_ssl);
//开启证书
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 **/
@@ -714,8 +804,30 @@ function getPanelSSL(){
$.post('/config/get_panel_ssl',{},function(cert){
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 = '';
// <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']){
cert_data = "<div class='ssl_state_info'><div class='state_info_flex'>\
<div class='state_item'><span>证书品牌:</span>\
@@ -726,16 +838,11 @@ function getPanelSSL(){
<div class='state_info_flex'>\
<div class='state_item'><span>认证域名:</span>\
<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>";
}
// <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">\
<div class="myKeyCon ptb15">\
'+cert_data+'\
@@ -750,7 +857,9 @@ function getPanelSSL(){
<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 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>\
<ul class="help-info-text c7 pull-left">\
@@ -768,65 +877,120 @@ function getPanelSSL(){
content:certBody,
success:function(layero, layer_id){
//保存SSL
$('.save-panel-ssl').click(function(){
var data = {
privateKey:$("#key").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.close(loadT);
if(rdata.status){
layer.closeAll();
}
layer.msg(rdata.msg,{icon:rdata.status?1:2});
},'json');
layer.confirm('选择保存面板SSL方式?',
{
title:'提示',
shade:0.001,
btn: ['本地SSL', '取消'],//'OpenResty'
btn3:function(){
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
$('.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
$('.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_let_ssl',{},function(rdata){
layer.confirm('选择删除面板SSL方式?',
{
title:'提示',
shade:0.001,
btn: ['本地SSL', '取消'],//, 'OpenResty'
btn3:function(){
var data = {};
data['choose'] = 'nginx';
var loadT = layer.msg('正在删除面板SSL【nginx】...',{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');
},
},
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);
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});
showMsg(rdata.msg, function(){
if(rdata.status){
location.href = rdata.data;
}
},{icon:rdata.status?1:2},3000);
},'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');
+1 -1
View File
@@ -294,7 +294,7 @@ function openFilename(obj){
onlineEditFile(0, path);
}
if (inArray(ext,['png','jpeg','gif','jpg','ico'])){
if (inArray(ext,['png','jpeg','jpg','gif','webp','bmp','ico'])){
getImage(path);
}
+36 -13
View File
@@ -1032,7 +1032,18 @@ var index = {
}
},
tooltip: {
trigger: 'axis'
trigger: 'axis',
formatter :function (config) {
var _config = config, _tips = "时间:" + _config[0].axisValue + "<br />";
for (var i = 0; i < config.length; i++) {
if (typeof config[i].data == "undefined") {
return false;
}
_tips += '<span style="display: inline-block;width: 10px;height: 10px;border-radius: 50%;background: ' + config[i].color + ';"></span>&nbsp;&nbsp;<span>' + config[i].seriesName + ':' + config[i].data + ' '+ index.net.default_unit + '</span><br />'
}
return _tips;
}
},
legend: {
data: [lan.index.net_up, lan.index.net_down],
@@ -1130,19 +1141,31 @@ var index = {
if (_net.data.yData.length >= limit) _net.data.yData.splice(0, 1);
if (_net.data.zData.length >= limit) _net.data.zData.splice(0, 1);
var upTmpSize = toSize(up).split(' ')[0];
var downTmpSize = toSize(down).split(' ')[0];
var tmp = up;
if (down>up){
tmp = down;
}
index.net.default_unit = toSize(tmp).split(' ')[1] + '/s';
_net.data.zData.push(downTmpSize);
_net.data.yData.push(upTmpSize);
_net.data.xData.push(d.getHours() + ':' + d.getMinutes() + ':' + d.getSeconds());
if (up>down){
var upTmp = toSizePos(up);
var upTmpSize = upTmp['name'].split(' ')[0];
index.net.default_unit = upTmp['name'].split(' ')[1] + '/s';
var downTmpSize = toSizePos(down,upTmp['pos'])['name'].split(' ')[0];
// console.log('up',upTmp['pos'],toSizePos(down, upTmp['pos']),downTmpSize);
_net.data.zData.push(downTmpSize);
_net.data.yData.push(upTmpSize);
} else {
var downTmp = toSizePos(down);
var downTmpSize = downTmp['name'].split(' ')[0];
index.net.default_unit = downTmp['name'].split(' ')[1] + '/s';
var upTmpSize = toSizePos(up, downTmp['pos'])['name'].split(' ')[0];
// console.log('down',downTmp['pos'],toSizePos(up, downTmp['pos']),upTmpSize);
_net.data.zData.push(downTmpSize);
_net.data.yData.push(upTmpSize);
}
},
renderSelect:function(net){
// console.log(net);
+34 -12
View File
@@ -16,6 +16,28 @@ function toSize(a) {
}
}
function toSizePos(a, pos = 0) {
var d = [" B", " KB", " MB", " GB", " TB", " PB"];
var e = 1024;
var r = {};
for(var b = 0; b < d.length; b++) {
if (pos > 0){
if (b == pos){
r['name'] = (b == 0 ? a : a.toFixed(2)) + d[b];
r['pos'] = b;
return r
}
} else {
if( a < e) {
r['name'] = (b == 0 ? a : a.toFixed(2)) + d[b];
r['pos'] = b;
return r
}
}
a /= e;
}
}
function toSizeMB(a) {
var d = [" KB", " MB"];
var e = 1024;
@@ -588,7 +610,7 @@ function showMsg(msg, callback ,icon, time){
function openPath(a) {
setCookie("open_dir_path", a);
window.location.href = "/files/"
window.location.href = "/files/";
}
function onlineEditFile(k, f, callback) {
@@ -1000,7 +1022,7 @@ function setSelectChecked(c, d) {
function jump() {
layer.closeAll();
window.location.href = "/soft"
window.location.href = "/soft";
}
function installTips() {
@@ -1134,15 +1156,15 @@ function getPanelList(){
var user = $(this).attr("data-user");
var pw = $(this).attr("data-pw");
layer.open({
type: 2,
title: false,
closeBtn: 0, //不显示关闭按钮
shade: [0],
area: ['340px', '215px'],
offset: 'rb', //右下角弹出
time: 5, //2秒后自动关闭
anim: 2,
content: [murl+'/login', 'no']
type: 2,
title: false,
closeBtn: 0, //不显示关闭按钮
shade: [0],
area: ['340px', '215px'],
offset: 'rb', //右下角弹出
time: 5, //2秒后自动关闭
anim: 2,
content: [murl+'/login', 'no']
});
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">\
@@ -1375,7 +1397,7 @@ function remind(a){
<td>\
<div class="titlename c3">'+g.data[d].name+'</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>\
</td>\
<td class="text-right c3">'+g.data[d].addtime+'</td>\
+5
View File
@@ -276,6 +276,10 @@ html {
margin-left: 5px;
}
.ml10 {
margin-left: 10px;
}
.mr5 {
margin-right: 5px;
}
@@ -283,6 +287,7 @@ html {
.mr20 {
margin-right: 20px;
}
.ml0{
margin-left: 0;
}
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">
<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>
<span class="set-info c7">建议端口范围7200 - 65535</span>
</p>
+26 -5
View File
@@ -209,6 +209,21 @@ mw_unbind_domain()
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()
{
tail -n 100 $mw_path/logs/error.log
@@ -445,6 +460,7 @@ case "$1" in
'install_app') mw_install_app;;
'close_admin_path') mw_close_admin_path;;
'unbind_domain') mw_unbind_domain;;
'unbind_ssl') mw_unbind_domain;;
'debug') mw_debug;;
'mirror') mw_mirror;;
'db') mw_connect_mysql;;
@@ -454,6 +470,11 @@ case "$1" in
'default')
cd $mw_path
port=7200
scheme=http
if [ -f $mw_path/ssl/choose.pl ];then
scheme=https
fi
if [ -f $mw_path/data/port.pl ];then
port=$(cat $mw_path/data/port.pl)
@@ -484,9 +505,9 @@ case "$1" in
mw_start
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
address="MW-Panel-Url: http://$v4:$port$auth_path"
address="MW-Panel-Url: ${scheme}://$v4:$port$auth_path"
elif [ "$v6" != "" ]; then
if [ ! -f $mw_path/data/ipv6.pl ];then
@@ -495,12 +516,12 @@ case "$1" in
mw_stop
mw_start
fi
address="MW-Panel-Url: http://[$v6]:$port$auth_path"
address="MW-Panel-Url: ${scheme}://[$v6]:$port$auth_path"
else
address="MW-Panel-Url: http://you-network-ip:$port$auth_path"
address="MW-Panel-Url: ${scheme}://you-network-ip:$port$auth_path"
fi
else
address="MW-Panel-Url: http://$address:$port$auth_path"
address="MW-Panel-Url: ${scheme}://$address:$port$auth_path"
fi
show_panel_ip="$port|"
+11 -5
View File
@@ -59,11 +59,17 @@ if os.path.exists('data/ipv6.pl'):
else:
bind.append('0.0.0.0:%s' % mw_port)
# if os.path.exists('data/ssl.pl'):
# certfile = 'ssl/certificate.pem'
# keyfile = 'ssl/privateKey.pem'
# ciphers = 'TLSv1 TLSv1.1 TLSv1.2 TLSv1.3'
# ssl_version = 2
ssl_choose_file = 'ssl/choose.pl'
if os.path.exists(ssl_choose_file):
ssl_choose = mw.readFile(ssl_choose_file).strip()
if mw.inArray(['local','nginx'],ssl_choose):
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'):
+43 -7
View File
@@ -64,6 +64,29 @@ def service_cmd(method):
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 wrapper(*args, **kwargs):
thr = threading.Thread(target=f, args=args, kwargs=kwargs)
@@ -539,13 +562,7 @@ def openrestyAutoRestart():
time.sleep(86400)
continue
# systemd
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')
openresty_cmd('reload')
time.sleep(86400)
except Exception as e:
print(str(e))
@@ -553,6 +570,18 @@ def openrestyAutoRestart():
# --------------------------------------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 --------------------------------------------- #
def restartPanelService():
@@ -584,11 +613,18 @@ if __name__ == "__main__":
php502 = setDaemon(php502)
php502.start()
# OpenResty Restart At Once Start
oraos = threading.Thread(target=openrestyRestartAtOnce)
oraos = setDaemon(oraos)
oraos.start()
# OpenResty Auto Restart Start
oar = threading.Thread(target=openrestyAutoRestart)
oar = setDaemon(oar)
oar.start()
# Panel Restart Start
rps = threading.Thread(target=restartPanelService)
rps = setDaemon(rps)
+18 -11
View File
@@ -58,11 +58,12 @@ def mwcli(mw_input=0):
print("(13) 显示面板错误日志")
print("(20) 关闭BasicAuth认证")
print("(21) 解除域名绑定")
print("(22) 开启IPV6支持")
print("(23) 关闭IPV6支持")
print("(24) 开启防火墙SSH端口")
print("(25) 关闭二次验证")
print("(26) 查看防火墙信息")
print("(22) 解除面板SSL绑定")
print("(23) 开启IPV6支持")
print("(24) 关闭IPV6支持")
print("(25) 开启防火墙SSH端口")
print("(26) 关闭二次验证")
print("(27) 查看防火墙信息")
print("(100) 开启PHP52显示")
print("(101) 关闭PHP52显示")
print("(200) 切换Linux系统软件源")
@@ -77,8 +78,8 @@ def mwcli(mw_input=0):
mw_input = 0
nums = [
1, 2, 3, 4, 5, 10, 11, 12, 13,
20, 21, 22, 23, 24, 25, 26,
1, 2, 3, 4, 5, 10, 11, 12, 13,
20, 21, 22, 23, 24, 25, 26, 27,
100, 101,
200, 201
]
@@ -134,6 +135,12 @@ def mwcli(mw_input=0):
os.system(INIT_CMD + " unbind_domain")
print("|-解除域名绑定成功")
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'
if not os.path.exists(listen_ipv6):
mw.writeFile(listen_ipv6,'True')
@@ -141,7 +148,7 @@ def mwcli(mw_input=0):
print("|-开启IPv6支持了")
else:
print("|-已开启IPv6支持!")
elif mw_input == 23:
elif mw_input == 24:
listen_ipv6 = 'data/ipv6.pl'
if not os.path.exists(listen_ipv6):
print("|-已关闭IPv6支持!")
@@ -149,17 +156,17 @@ def mwcli(mw_input=0):
os.remove(listen_ipv6)
os.system(INIT_CMD + " restart")
print("|-关闭IPv6支持了")
elif mw_input == 24:
elif mw_input == 25:
open_ssh_port()
print("|-已开启!")
elif mw_input == 25:
elif mw_input == 26:
auth_secret = 'data/auth_secret.pl'
if os.path.exists(auth_secret):
os.remove(auth_secret)
print("|-关闭二次验证成功!")
else:
print("|-二次验证已关闭!")
elif mw_input == 26:
elif mw_input == 27:
cmd = 'which ufw'
run_cmd = False
find_cmd = mw.execShell(cmd)