Compare commits

...
793 Commits
Author SHA1 Message Date
Mr Chen a75a49c2ae Merge pull request #402 from midoks/dev
0.14.0
2023-04-13 15:21:19 +08:00
midoks 1204e72f41 0.14.0
* 加入系统加固插件。
2023-04-13 15:20:15 +08:00
midoks 13ca5544a5 Update system_safe.tpl 2023-04-13 15:14:32 +08:00
midoks e53fe26d52 Update system_safe.tpl 2023-04-13 15:09:20 +08:00
midoks 13772399f6 Update system_safe.py 2023-04-13 15:05:06 +08:00
midoks 98f11e2662 Update plugins_api.py 2023-04-13 14:49:46 +08:00
midoks f0b0302f75 Update install.sh 2023-04-13 14:42:07 +08:00
midoks 5b9b466cb9 系统加固放出 2023-04-13 14:39:18 +08:00
midoks 5588a11ea5 Update cert_request.py 2023-04-10 22:35:09 +08:00
midoks da05a4ef19 up 2023-04-10 22:34:12 +08:00
midoks 34cf899529 Update mw.py 2023-04-04 12:43:09 +08:00
midoks 0d7d67b012 up 2023-04-04 12:42:47 +08:00
midoks 5b778bfc0d Update vip_api.py 2023-04-04 12:20:28 +08:00
midoks 6ce414cecf up 2023-04-03 22:39:07 +08:00
midoks c8160b63e7 up 2023-04-03 22:30:08 +08:00
midoks 68d957fd6e Update README.md 2023-04-03 19:34:05 +08:00
midoks f09c438500 Update config.html 2023-04-03 17:46:18 +08:00
Mr Chen 9bc9944a3e Merge pull request #401 from midoks/dev
0.13.6
2023-04-03 17:43:58 +08:00
midoks 9e4c2b13d5 Update README.md 2023-04-03 17:43:05 +08:00
midoks cd7143047f 0.13.6
1.修复初始化安装,缺少解压命令。
2.加入aes加密/解密方法
2023-04-03 17:41:48 +08:00
midoks d608ac4d94 Update install.sh 2023-04-03 16:48:53 +08:00
midoks 9fedf9d10d Update push_notice_msg.py 2023-04-01 10:37:33 +08:00
midoks 9936191da2 up 2023-04-01 10:35:54 +08:00
midoks 538eba3acf up 2023-03-31 22:37:40 +08:00
midoks e39ee9a6f7 VIP用户管理 2023-03-30 21:57:33 +08:00
Mr Chen e1820ad5b3 Merge pull request #400 from midoks/dev
0.13.5
2023-03-30 17:31:54 +08:00
midoks 22133f1a6c 0.13.5
### 版本更新 0.13.5

* 添加邮件通知功能。
* 修复初始安装脚本。
2023-03-30 17:30:15 +08:00
midoks 0041cbdff3 添加邮件通知功能 2023-03-30 17:18:40 +08:00
midoks 889da3c769 Update install.sh 2023-03-30 11:59:55 +08:00
midoks d086eb9c61 Update install.sh 2023-03-30 11:56:48 +08:00
midoks 832fa9cd1a Update install_dev.sh 2023-03-30 11:54:40 +08:00
midoks 8c19fb9629 Update config_api.py 2023-03-30 11:22:36 +08:00
midoks a2e4dd6753 Update push_notice_msg.py 2023-03-30 11:22:19 +08:00
midoks 22ba7dda55 Update install.sh 2023-03-29 16:35:09 +08:00
midoks 2ffa36eecb up 2023-03-29 16:32:46 +08:00
Mr Chen fe5843b35d Merge pull request #398 from midoks/dev
修复python pip源安装
2023-03-29 16:27:46 +08:00
midoks e95acbe947 Update lib.sh 2023-03-29 16:08:55 +08:00
midoks aed8319dd3 Update push_ad.py 2023-03-29 14:08:06 +08:00
midoks babe1b885e Update push_notice_msg.py 2023-03-29 13:17:38 +08:00
midoks 0612f52402 Update push_notice_msg.py 2023-03-29 13:03:52 +08:00
midoks db67cea737 AD 2023-03-29 13:01:12 +08:00
midoks 52cf675843 Update install_dev.sh 2023-03-29 08:49:26 +08:00
Mr Chen 3421bf6ab5 Merge pull request #397 from midoks/dev
0.13.4
2023-03-28 23:54:51 +08:00
midoks 2f39bd600b Update config_api.py
### 版本更新 0.13.4

无重大更新

* 国内安装不在依赖gitee。
* tgbot优化。
2023-03-28 23:53:14 +08:00
midoks acd5ec69e8 Update tgbot.py 2023-03-28 23:49:59 +08:00
midoks 09878728af Update push_ad.py 2023-03-28 20:25:09 +08:00
midoks af82e9f129 Update push_notice_msg.py 2023-03-28 20:23:08 +08:00
midoks c74630e738 Update push_notice_msg.py 2023-03-28 20:19:49 +08:00
midoks e4e0697b4b Update push_notice_msg.py 2023-03-28 20:18:52 +08:00
midoks 11b5ac658b Update mw.tpl 2023-03-28 18:00:50 +08:00
midoks 1306731164 up 2023-03-28 17:47:59 +08:00
midoks bb6c933aee Update README.md 2023-03-28 17:41:50 +08:00
midoks d6a93ef1e6 Update info.json 2023-03-28 17:18:59 +08:00
midoks 6610da402a Update info.json 2023-03-28 17:18:47 +08:00
midoks 3ace9cf51e Update install.sh 2023-03-28 16:54:21 +08:00
midoks 49c7ec6073 Update tgbot.py 2023-03-28 16:31:46 +08:00
midoks 8cf526667e up 2023-03-25 22:05:19 +08:00
midoks 6c42876411 Update receive_faq.py 2023-03-25 21:58:08 +08:00
midoks 79471e3806 Update receive_faq.py 2023-03-25 21:56:50 +08:00
midoks cad06425fc Update lib.sh 2023-03-23 15:24:49 +08:00
midoks 6f1e11e474 Update lib.sh 2023-03-23 15:21:22 +08:00
midoks 59d13e50ce Update rhel.sh 2023-03-22 22:26:32 +08:00
midoks 5da0746240 Update push_bbs_ntid.py 2023-03-22 18:00:32 +08:00
midoks 192ea8b029 Update push_bbs_ntid.py 2023-03-22 17:34:18 +08:00
midoks bfc20b3e42 Update push_tmp_msg.py 2023-03-22 17:01:50 +08:00
midoks 56bee5c752 Update push_tmp_msg.py 2023-03-22 16:41:26 +08:00
midoks 312df5de1f Create push_bbs_ntid.py 2023-03-22 16:41:23 +08:00
midoks 4d3640286c Update README.md 2023-03-20 17:49:48 +08:00
midoks 20930806b6 Update install.sh 2023-03-20 12:19:35 +08:00
midoks 71360b61da Update install.sh 2023-03-20 12:04:54 +08:00
midoks 411645c9e2 Update install.sh 2023-03-20 12:01:46 +08:00
midoks 3de4e1bfff up 2023-03-19 00:21:36 +08:00
midoks 34285f6051 Update push_tmp_msg.py 2023-03-19 00:20:23 +08:00
midoks 84f0cda89b up 2023-03-18 18:32:30 +08:00
midoks 04012085ce Update mdw.jpg 2023-03-17 19:18:27 +08:00
midoks 9ad00f3ce7 Update push_tmp_msg.py 2023-03-16 22:36:25 +08:00
midoks 609a4ed351 Update tgbot.py 2023-03-16 21:34:44 +08:00
midoks 5ca74fd312 Update push_tmp_msg.py 2023-03-16 19:22:36 +08:00
midoks 3b1bb06c68 Update push_tmp_msg.py 2023-03-16 18:57:09 +08:00
midoks b6b7de42d6 Update push_tmp_msg.py 2023-03-16 18:53:29 +08:00
midoks 3aa3b532ac Update push_tmp_msg.py 2023-03-16 18:48:19 +08:00
midoks 290c740ab1 Update push_tmp_msg.py 2023-03-16 18:43:02 +08:00
midoks 23fd4ca1de Create push_tmp_msg.py 2023-03-16 18:42:42 +08:00
midoks c128a799b2 Update receive_faq.py 2023-03-16 15:50:52 +08:00
midoks 401c103b76 Update receive_faq.py 2023-03-16 15:47:14 +08:00
midoks 6fb88eb428 Update push_ad.py 2023-03-16 15:33:18 +08:00
midoks 956f20d295 tgbot init命令分离 2023-03-16 15:30:43 +08:00
midoks 03f8578a37 Update README.md 2023-03-16 13:04:29 +08:00
Mr Chen 265e17beac Merge pull request #393 from midoks/dev
0.13.3
2023-03-16 13:00:56 +08:00
midoks f3d83ad8a5 Update README.md 2023-03-16 12:58:06 +08:00
midoks 39b376a7cb Update webstats_common.lua 2023-03-16 12:24:02 +08:00
midoks 07858794ef Update webstats_common.lua 2023-03-16 12:02:33 +08:00
midoks 391473fc14 Update lib.sh 2023-03-15 23:26:23 +08:00
midoks 0993adcc5f Update lib.sh 2023-03-15 23:24:47 +08:00
midoks 90b1d88111 Update lib.sh 2023-03-15 23:17:49 +08:00
midoks 87098cff95 up 2023-03-15 23:00:37 +08:00
midoks e6e64b2a4c Update mysql.js 2023-03-15 22:59:23 +08:00
midoks 81436ea81b Update index.html 2023-03-15 22:57:22 +08:00
midoks cf7d29190d Update index.html 2023-03-15 22:53:36 +08:00
midoks 4ff62ec6ad Update tgbot.py 2023-03-15 22:09:53 +08:00
midoks dac3ccce8d Update tgbot.py 2023-03-15 21:53:41 +08:00
midoks 10bd7aa70f Update tool_migrate.py 2023-03-15 18:28:31 +08:00
midoks dec0afc3d3 Update tool_migrate.py 2023-03-15 18:25:43 +08:00
midoks 2331853b3b Update info.json 2023-03-15 18:13:34 +08:00
midoks b9e063e423 Update README.md 2023-03-15 18:11:35 +08:00
midoks 52294948b2 Update config_api.py 2023-03-15 18:09:20 +08:00
midoks 87bec2b988 Update tgbot.py 2023-03-15 18:06:14 +08:00
midoks 9ab91ecc93 Update index.py 2023-03-15 17:48:05 +08:00
midoks 18312a3596 Update tgbot.py 2023-03-15 17:37:35 +08:00
midoks 8fb35440c8 Update info.json 2023-03-15 17:35:28 +08:00
midoks a874934379 Update receive_faq.py 2023-03-15 17:34:05 +08:00
midoks 2c4e4aea3f Update info.json 2023-03-15 17:22:36 +08:00
midoks 6b115261be up 2023-03-15 17:19:15 +08:00
midoks 059ce6228b Update receive_faq.py 2023-03-15 15:09:03 +08:00
midoks cfa396b4bb 更新 2023-03-14 20:03:53 +08:00
midoks 11e4b377ed up 2023-03-14 15:21:33 +08:00
midoks 31fe2aab92 Update receive_faq.py 2023-03-14 13:36:00 +08:00
midoks 19042a9088 Update receive_faq.py 2023-03-14 11:48:54 +08:00
midoks 2eb4543c3b Update receive_faq.py 2023-03-14 01:54:58 +08:00
midoks e33ccbfc7d Update tgbot.py 2023-03-14 01:54:08 +08:00
midoks ed8c9eed0a Update receive_faq.py 2023-03-14 01:50:56 +08:00
midoks 7163a7bbcf Update receive_faq.py 2023-03-14 01:46:19 +08:00
midoks 3f1c23ec8b Update receive_faq.py 2023-03-14 01:32:52 +08:00
midoks 3a6cb0ae2a Update receive_faq.py 2023-03-14 01:15:14 +08:00
midoks 936492d410 up 2023-03-14 01:10:04 +08:00
midoks 39d265aae7 Update tgbot.py 2023-03-14 00:15:27 +08:00
midoks 5a33098dd3 Update tgbot.py 2023-03-14 00:09:52 +08:00
midoks c9927c3fb9 tgbot加入扩展机制 2023-03-14 00:09:30 +08:00
midoks da90386816 Update tool_migrate.py 2023-03-13 11:29:00 +08:00
midoks 6be4c5977b up 2023-03-13 02:33:29 +08:00
midoks bbf93bfd9e up 2023-03-13 02:33:22 +08:00
midoks a2570af5f7 Update webstats_common.lua 2023-03-13 01:33:07 +08:00
midoks 77120cf31b up 2023-03-12 12:13:43 +08:00
midoks 395f449b93 Update webstats_common.lua 2023-03-12 12:09:53 +08:00
midoks 8be5f4aa7b Update info.json 2023-03-11 22:54:23 +08:00
midoks 3ccebf4f47 Update index.py 2023-03-11 22:48:42 +08:00
midoks 15d48accad Update tgbot.py 2023-03-11 22:43:15 +08:00
midoks d0045d831c Update tgbot.py 2023-03-11 22:42:39 +08:00
midoks a7cac3d671 up 2023-03-11 20:47:31 +08:00
midoks 7bc7fdecae Update webstats_common.lua 2023-03-11 16:40:45 +08:00
midoks 480663e2ad Update tools.py 2023-03-11 15:41:15 +08:00
midoks 2290efd060 Update config_api.py 2023-03-11 15:36:11 +08:00
midoks dece5a0532 Update config_api.py 2023-03-11 15:30:16 +08:00
midoks b0a44d24d4 Update config_api.py 2023-03-11 15:28:13 +08:00
midoks d092d9f5f2 up 2023-03-11 15:24:40 +08:00
midoks 944b3e779c Update config_api.py 2023-03-11 15:21:43 +08:00
midoks 55dc2715e9 Update config_api.py 2023-03-11 15:20:13 +08:00
midoks 84bf3abd5d Update config_api.py 2023-03-11 15:15:49 +08:00
midoks 00c9157f9b up 2023-03-11 15:15:27 +08:00
midoks faa95b0d88 Update config_api.py 2023-03-11 15:07:06 +08:00
midoks 52d0b3c940 Update config_api.py 2023-03-11 15:04:20 +08:00
midoks 0141028d41 Update config_api.py 2023-03-11 15:02:15 +08:00
midoks 9e6bf08a72 Update config_api.py 2023-03-11 15:01:46 +08:00
midoks 49ff9750cb Update config_api.py 2023-03-11 14:58:18 +08:00
midoks 75b2b4dbc2 Update config_api.py 2023-03-11 14:56:43 +08:00
midoks 667c60dda0 Update config.js 2023-03-11 14:55:48 +08:00
midoks aa6818f953 Update config_api.py 2023-03-11 14:52:58 +08:00
midoks 6f884fe60a panel ssl https 功能添加 2023-03-11 14:48:38 +08:00
midoks a56d53c672 Update config_api.py 2023-03-11 14:29:28 +08:00
midoks 00b1788172 up 2023-03-11 14:26:22 +08:00
midoks cee268fcbc Update config_api.py 2023-03-11 13:56:20 +08:00
midoks 40dd929823 Update config_api.py 2023-03-11 13:47:16 +08:00
midoks b59949e181 Update config_api.py 2023-03-11 13:41:36 +08:00
midoks 9c5975253a Update cert_api.py 2023-03-11 13:23:30 +08:00
midoks 62003b987a Update discuzx.conf 2023-03-10 14:12:27 +08:00
midoks df0a454c3a up 2023-03-10 14:09:40 +08:00
midoks 02048c3265 Update webstats_common.lua 2023-03-10 13:19:58 +08:00
midoks 65649d171d Update tgbot.py 2023-03-09 19:11:51 +08:00
midoks a136da6ec2 Update tgbot.py 2023-03-09 17:23:28 +08:00
midoks 3b25ca7e60 Update tgbot.py 2023-03-09 16:28:11 +08:00
midoks c5cf8f8afb Update info.json 2023-03-09 15:40:54 +08:00
midoks 89f7d464ab Update info.json 2023-03-09 15:28:00 +08:00
midoks 4cac6d1921 Update info.json 2023-03-09 15:25:37 +08:00
midoks dca5c82d2f Update webstats_common.lua 2023-03-09 14:56:43 +08:00
midoks 08988130b5 Update config_api.py 2023-03-09 03:00:30 +08:00
Mr Chen 8849001384 Merge pull request #392 from midoks/dev
升级图表echarts
2023-03-09 02:49:57 +08:00
midoks ae0a6b5079 升级图表echarts 2023-03-09 02:44:57 +08:00
Mr Chen d6c6ae5561 Merge pull request #391 from midoks/dev
0.13.2
2023-03-09 01:49:10 +08:00
midoks a6dc15f22d 0.13.2
* supervisor添加重启功能。
* 加入滚动写入日志 mw.writeFileLog方法。
* mysql导出优化优化。
* 添加tgbot插件[仅测试]。
* TelegramBot机器人通知-加入配置。
* mariadb更新版本。
* 在线文件编辑优化。
2023-03-09 01:47:35 +08:00
midoks 360b962bd2 Update mw.py 2023-03-09 01:42:55 +08:00
midoks 37d37e0f1d Update task.py 2023-03-09 01:32:05 +08:00
midoks ddc6da9172 Update mw.py 2023-03-09 01:30:19 +08:00
midoks ceba7ab673 up 2023-03-09 01:26:39 +08:00
midoks 384704b8e5 Update mw.py 2023-03-09 01:10:04 +08:00
midoks 232d0c2f74 Update mw.py 2023-03-09 01:01:00 +08:00
midoks ef8e6f07f5 Update mw.py 2023-03-09 00:43:34 +08:00
midoks 435e565559 Update mw.py 2023-03-09 00:42:15 +08:00
midoks ec9d7e337d Update mw.py 2023-03-09 00:36:46 +08:00
midoks b5c7a2a778 Update task.py 2023-03-09 00:32:23 +08:00
midoks 72d9c9b761 Update task.py 2023-03-09 00:28:59 +08:00
midoks 30812e1fba Update task.py 2023-03-09 00:08:49 +08:00
midoks c95cb07e8d Update task.py 2023-03-09 00:05:45 +08:00
midoks f3f2ea8ef0 通知限制频率 2023-03-08 19:40:23 +08:00
midoks 9e5a5192cb Update mw.py 2023-03-08 18:02:30 +08:00
midoks 271b3d448c Update mw.py 2023-03-08 18:01:06 +08:00
midoks b7424e7195 Update config_api.py 2023-03-08 17:57:23 +08:00
midoks b607527e1f Update config_api.py 2023-03-08 17:56:18 +08:00
midoks 3c42a4e450 对通知配置进行加密处理 2023-03-08 17:56:07 +08:00
midoks b11fd9508a Update .gitignore 2023-03-08 17:29:42 +08:00
midoks dc0504cf24 Delete notify.json 2023-03-08 17:25:12 +08:00
midoks 1d8b82395f Update public.js 2023-03-08 16:25:30 +08:00
midoks 39ac55760e Update config_api.py 2023-03-08 16:20:02 +08:00
midoks ef6071956d 在线文件编辑优化 2023-03-08 16:14:28 +08:00
midoks bbbe1c84ce Update mw.py 2023-03-07 19:24:03 +08:00
midoks 064252daad Update config.html 2023-03-07 19:20:07 +08:00
midoks 3cb63c6265 Update index.html 2023-03-07 16:28:48 +08:00
midoks b541abad42 Update index.py 2023-03-07 13:44:28 +08:00
midoks 547027be0d Update install.sh 2023-03-06 23:40:56 +08:00
midoks d786b2524a up 2023-03-06 23:21:55 +08:00
midoks abac345628 Update info.json 2023-03-06 23:10:12 +08:00
midoks d4290cbdd2 mariadb更新版本 2023-03-06 22:29:20 +08:00
midoks cfd2d99e24 Update mw.py 2023-03-06 21:31:24 +08:00
midoks 28437ccfb0 Update mw.py 2023-03-06 21:30:18 +08:00
midoks 43bf755609 Update mw.py 2023-03-06 20:35:06 +08:00
midoks de225fc8b9 0.13.1.2 2023-03-06 20:28:29 +08:00
midoks 44f891f7a3 Update mw.py 2023-03-06 20:12:52 +08:00
midoks 64d1387b03 Telegram Bot机器人通知 加入配置 2023-03-06 19:32:36 +08:00
midoks 51ccbefb04 up 2023-03-06 17:15:41 +08:00
midoks 29e3017e51 up 2023-03-06 16:49:42 +08:00
midoks 197e2d80c6 Update tgbot.py 2023-03-06 16:42:29 +08:00
midoks b8f04043bc Update tgbot.service.tpl 2023-03-06 16:41:25 +08:00
midoks 2ed70b4b96 Update tgbot.py 2023-03-06 16:39:27 +08:00
midoks d056c37be5 Update index.py 2023-03-06 16:33:18 +08:00
midoks e75abae4b9 Update tgbot.service.tpl 2023-03-06 16:32:05 +08:00
midoks 3e695770d7 Update config_api.py 2023-03-06 16:26:49 +08:00
midoks 7301edbc5a 添加tgbot插件
现在只是为了获取chat_id
2023-03-06 16:19:13 +08:00
Mr Chen 704b256408 Merge pull request #390 from midoks/dev
网站统计优化
2023-03-05 18:36:57 +08:00
midoks e129051a06 Update webstats_common.lua 2023-03-05 18:34:35 +08:00
Mr Chen d38e9f7ad0 Merge pull request #389 from midoks/dev
OP防火墙优化
2023-03-05 18:07:54 +08:00
midoks c696742866 Update init.lua 2023-03-05 18:03:41 +08:00
midoks 046389df99 Update init.lua 2023-03-05 17:37:01 +08:00
midoks 3e244ed227 导出优化 2023-03-05 14:04:16 +08:00
midoks 5016aedb0f Update index.py 2023-03-05 02:08:04 +08:00
midoks c1cd45a5f3 Update my8.0.cnf 2023-03-05 00:00:59 +08:00
midoks 79c9865a5d Update index.py 2023-03-04 23:53:54 +08:00
midoks 4082f29424 加入滚动写入日志 2023-03-04 23:07:23 +08:00
midoks d777a29d8c Update update.sh 2023-03-04 21:47:12 +08:00
Mr Chen 04e43e051e Merge pull request #388 from midoks/dev
局部优化
2023-03-02 16:57:17 +08:00
midoks dffe9a77e7 Update layout.html 2023-03-02 00:57:02 +08:00
midoks 1b1462586e Update orm.py 2023-03-02 00:49:44 +08:00
midoks f11d2db761 添加重启功能 2023-03-01 18:41:32 +08:00
midoks 842c16540a Update index.py 2023-03-01 18:03:14 +08:00
midoks 614cc1032f Update webstats_log.lua 2023-03-01 14:03:46 +08:00
midoks ad46b2050e Update config.json 2023-03-01 01:40:57 +08:00
midoks 8109989927 Update layout.html 2023-02-28 19:41:13 +08:00
Mr Chen 9d9b39dfe2 Merge pull request #387 from midoks/dev
0.13.1
2023-02-28 19:11:06 +08:00
midoks 422514c40a 0.13.1
* 关闭issue,建立bbs.midoks.me。问题集中处理,发挥群众的力量。
* 添加站操作回调钩子site_cb。 网站统计和OP防火墙不再手动重载配置
* 修复ubuntu20 pureftp 安装时,端口没有加入安全里
* 增加global_static hook关键字,并优化menu的文件加载hook
* 各种细节优化。
2023-02-28 19:09:54 +08:00
midoks 632568643b Update site_api.py 2023-02-28 17:08:26 +08:00
midoks a5f29a2a6a Update cert_api.py 2023-02-28 16:26:31 +08:00
midoks b02705aa5a up 2023-02-28 03:00:46 +08:00
midoks 1f9ab560e3 Update files.js 2023-02-28 02:18:39 +08:00
midoks b3970326cd Update index.py 2023-02-28 01:57:19 +08:00
midoks f6d008e0b6 Update index.py 2023-02-28 01:50:54 +08:00
midoks 6f9a49ad7a up 2023-02-28 01:48:51 +08:00
midoks fc3c7d334a Update public.js 2023-02-28 00:17:39 +08:00
midoks 1030d1b923 site_cb 添加站操作回调钩子。 网站统计和OP防火墙不再手动重载配置 2023-02-28 00:02:16 +08:00
midoks 94d4a90bcc Update webstats_common.lua 2023-02-27 19:35:52 +08:00
midoks da4b872666 Update README.md 2023-02-27 17:57:17 +08:00
midoks cf15b19f4b Update install.sh 2023-02-26 21:46:25 +08:00
midoks aa9cdaada4 Update commit.tpl 2023-02-26 03:16:45 +08:00
midoks 659c9fe16b 修复ubuntu 20 pureftp 安装时,端口没有加入安全里 2023-02-26 02:20:06 +08:00
midoks 2b8c6d6e43 Update firewall_api.py 2023-02-26 01:39:52 +08:00
midoks 263f83088f Update firewall_api.py 2023-02-26 01:38:55 +08:00
midoks 374c938ac0 Update firewall_api.py 2023-02-26 01:38:32 +08:00
midoks 6aa73b0cae Update firewall_api.py 2023-02-26 01:36:45 +08:00
midoks 7f731ae06f Update README.md 2023-02-26 01:12:08 +08:00
midoks f87aff704c Update rhel.sh 2023-02-25 23:36:14 +08:00
midoks be9fa87133 Update rhel.sh 2023-02-25 23:31:36 +08:00
midoks de6fc19dee Create discuz.conf 2023-02-25 21:33:50 +08:00
midoks 3987c6d3dc Update config_api.py 2023-02-25 08:37:26 +08:00
midoks 96875052cd Update index.py 2023-02-25 08:25:05 +08:00
midoks 583de57f96 Update plugins_api.py 2023-02-25 08:21:20 +08:00
midoks 41b7245f03 Update plugins_api.py 2023-02-25 08:15:38 +08:00
midoks ee3684706f Update info.json 2023-02-25 07:48:32 +08:00
midoks 07936dfb81 update 2023-02-25 07:35:46 +08:00
midoks 5c46202be0 增加global_static hook关键字,并优化menu的文件加载hook
"hook":[
		{
			"tag":"global_static",
			"global_static": {
				"css_path":"menu/own_style.css",
				"js_path":"js/own_style.js"
			}
		}
	],
2023-02-25 07:26:48 +08:00
midoks 7123a15457 Update install.sh 2023-02-24 18:08:46 +08:00
midoks 4aeb45b1d5 Update install.sh 2023-02-24 17:55:13 +08:00
midoks 006a62be22 Update lib.sh 2023-02-24 17:25:04 +08:00
midoks 1e91d11125 Update mw.tpl 2023-02-24 17:22:22 +08:00
midoks 99fb4aa723 up 2023-02-24 17:14:28 +08:00
midoks 5c5bbb2da2 up 2023-02-24 17:12:31 +08:00
midoks 8b05045901 Update install.sh 2023-02-24 17:07:24 +08:00
midoks 1b73439525 Update install_dev.sh 2023-02-24 17:00:10 +08:00
midoks ec51ad016a Update install_dev.sh 2023-02-24 16:57:24 +08:00
midoks a42fae66ac Update install_dev.sh 2023-02-24 16:53:48 +08:00
midoks b4c635c2e5 Update install_dev.sh 2023-02-24 16:49:03 +08:00
midoks 9802bc5915 Update pure-ftpd.conf 2023-02-24 13:29:16 +08:00
Mr Chen 196fbbdd7d Merge pull request #382 from midoks/dev
0.13.0
2023-02-24 05:59:59 +08:00
midoks fa278abd50 Update index.py 2023-02-24 05:53:33 +08:00
midoks f9b2dbcb5e Update index.py 2023-02-24 05:46:39 +08:00
midoks a8808be9ae Update index.py 2023-02-24 05:41:50 +08:00
midoks 4122de13e3 Update index.py 2023-02-24 05:39:52 +08:00
midoks 4acae8e5f5 up 2023-02-24 05:38:59 +08:00
midoks 1abb611c3e 版本更新 0.13.0 2023-02-24 05:17:44 +08:00
midoks 726f9fa0d2 #374 2023-02-24 04:15:23 +08:00
midoks cfb64caadc Update site_api.py 2023-02-24 04:09:50 +08:00
midoks f501dd5150 Update site_api.py 2023-02-24 04:07:19 +08:00
midoks d1fe006e39 Update site_api.py 2023-02-24 03:28:48 +08:00
midoks d4db5dbfac up 2023-02-24 03:28:18 +08:00
midoks efa512a3f6 #374 2023-02-24 03:16:52 +08:00
midoks b53436343d up 2023-02-24 02:40:09 +08:00
midoks bb1a634f3c Update site_api.py 2023-02-24 02:29:14 +08:00
midoks acda55cc55 Update index.py 2023-02-24 01:40:02 +08:00
midoks 0ff0728517 Update index.py 2023-02-24 01:30:17 +08:00
midoks c575f55354 Update app.js 2023-02-24 01:05:32 +08:00
midoks d58339dbcb Update index.py 2023-02-24 00:07:19 +08:00
midoks f6a18fa25f Update index.py 2023-02-24 00:03:48 +08:00
midoks 3016eff372 Update app.js 2023-02-23 17:29:38 +08:00
midoks ca518591f3 up 2023-02-23 17:27:15 +08:00
midoks d2c74f2243 up 2023-02-23 17:17:46 +08:00
midoks 71dbf8e359 Update app.js 2023-02-23 15:39:16 +08:00
midoks cb2ad0ca82 up 2023-02-23 15:18:44 +08:00
midoks a157db7879 Update index.py 2023-02-23 03:32:30 +08:00
midoks fb949f4ee6 up 2023-02-23 01:25:00 +08:00
midoks b91ae3fde2 Update index.py 2023-02-22 23:46:28 +08:00
midoks b508e8003b Update index.py 2023-02-22 23:13:44 +08:00
midoks 0632a4b535 Update index.py 2023-02-22 23:13:17 +08:00
midoks 397e0b89a5 Update index.py 2023-02-22 22:59:45 +08:00
midoks 2427bf81cd Update index.py 2023-02-22 22:44:16 +08:00
midoks 4179bbf580 Update index.py 2023-02-22 22:40:29 +08:00
midoks b93c3bb88e Update index.py 2023-02-22 22:37:19 +08:00
midoks a52c854afd Update index.py 2023-02-22 22:36:10 +08:00
midoks d7e5cf1f45 Update index.py 2023-02-22 21:55:49 +08:00
midoks cfb444deac Update files_api.py 2023-02-22 20:54:54 +08:00
midoks 78615451ac up 2023-02-22 20:52:12 +08:00
midoks 0134643c83 Update db.py 2023-02-22 18:18:40 +08:00
midoks 70ddd1c86f Update index.py 2023-02-22 17:55:17 +08:00
midoks 808dc743ef Update files_api.py 2023-02-22 14:59:40 +08:00
midoks e30693f9f2 Update files_api.py 2023-02-22 14:28:01 +08:00
midoks 5c97fbe011 Update __init__.py 2023-02-22 14:27:48 +08:00
midoks 6419a18525 up 2023-02-21 21:10:34 +08:00
midoks 4a804e9243 Update index.py 2023-02-19 18:12:08 +08:00
midoks d44526004f up 2023-02-19 18:02:26 +08:00
midoks c1f402b278 up 2023-02-19 00:10:06 +08:00
midoks 45a3eec1b5 Update install_dev.sh 2023-02-18 23:09:51 +08:00
midoks d6f41ceb6a Update install_dev.sh 2023-02-18 23:06:53 +08:00
midoks a1bc0f6faf Update install_dev.sh 2023-02-18 22:58:21 +08:00
midoks 8420961cf7 Update install_dev.sh 2023-02-18 22:54:58 +08:00
midoks 421594c01d Update install_dev.sh 2023-02-18 22:48:30 +08:00
midoks ca13e8ad77 Update install_dev.sh 2023-02-18 22:46:05 +08:00
midoks f161f24253 up 2023-02-18 22:44:42 +08:00
midoks e5ab03951f Update README.md 2023-02-18 22:08:13 +08:00
midoks 5b45bab36d Update install_dev.sh 2023-02-18 22:06:51 +08:00
midoks 0594d3f9ce Update README.md 2023-02-18 22:04:11 +08:00
midoks ad541216c5 up 2023-02-18 16:59:33 +08:00
midoks 1adc0c1405 Update rhel.sh 2023-02-18 15:35:16 +08:00
midoks b22185b374 Update rhel.sh 2023-02-18 14:09:02 +08:00
midoks 02f3107e93 Update app.js 2023-02-18 02:04:52 +08:00
midoks 52fece867d up 2023-02-18 01:49:20 +08:00
midoks 2e3578dcf1 up 2023-02-18 01:39:26 +08:00
midoks 73b09a7886 up 2023-02-17 22:39:49 +08:00
midoks f33c7e5a5e up 2023-02-17 19:57:03 +08:00
midoks 3e90db2c8d Update system_api.py 2023-02-17 19:54:44 +08:00
midoks d599363609 Update index.py 2023-02-17 16:30:32 +08:00
midoks ec3167f239 Update __init__.py 2023-02-17 16:30:25 +08:00
midoks 2b1099b6e9 Update __init__.py 2023-02-17 15:42:28 +08:00
midoks e55a0760e3 Update mw.tpl 2023-02-17 15:22:15 +08:00
midoks 5c1353a38f Update mw.tpl 2023-02-17 15:20:52 +08:00
midoks f95684b316 Update mw.tpl 2023-02-17 15:20:39 +08:00
midoks 6fc729ccf9 Update __init__.py 2023-02-17 15:17:41 +08:00
midoks 45d0c4fc4a Update __init__.py 2023-02-17 15:16:11 +08:00
midoks 6759b9055c ip 2023-02-17 15:01:26 +08:00
midoks de694b2465 up 2023-02-17 14:56:44 +08:00
midoks c7b3daca36 up 2023-02-16 17:02:33 +08:00
midoks 452769233f up 2023-02-16 17:00:09 +08:00
midoks 8d196a3aa1 Update index.py 2023-02-16 16:26:22 +08:00
midoks 01baf509ad up 2023-02-16 15:49:55 +08:00
midoks d59ee2c843 Update install.sh 2023-02-16 15:39:08 +08:00
midoks 8919d5734e Update index.py 2023-02-16 14:15:15 +08:00
midoks bb91b30d6d Update index.py 2023-02-16 14:06:41 +08:00
midoks 103ab2e099 Update index.py 2023-02-15 22:45:44 +08:00
midoks 0d33d0db79 Update install.sh 2023-02-15 22:35:29 +08:00
midoks 6d9f4cfa08 Update tools.py 2023-02-15 22:31:22 +08:00
midoks c88c95ba21 Update macos.sh 2023-02-15 22:26:53 +08:00
midoks a705f6a8e4 Update install.sh 2023-02-15 22:24:51 +08:00
midoks 3f40ed0a01 up 2023-02-15 22:24:46 +08:00
midoks 39653896bf Update install_dev.sh 2023-02-15 22:15:08 +08:00
midoks 31d665d962 Update install.sh 2023-02-15 22:05:16 +08:00
midoks 811fa4a40d Update install_dev.sh 2023-02-15 22:04:58 +08:00
midoks 320f58424d Update README.md 2023-02-15 21:56:19 +08:00
midoks 376f0f0a4e up 2023-02-15 21:55:21 +08:00
midoks 0efb700cee Update README.md 2023-02-15 15:54:14 +08:00
midoks ff3fcae366 Update config_api.py 2023-02-15 15:50:55 +08:00
Mr Chen 22d94ce67c Merge pull request #371 from midoks/dev
安装脚本优化3
2023-02-14 22:24:04 +08:00
midoks 042fd7ac6d Update ubuntu.sh 2023-02-14 22:23:04 +08:00
midoks 1a48f52dad up 2023-02-14 22:22:06 +08:00
Mr Chen fc5970c65d Merge pull request #370 from midoks/dev
安装脚本优化2
2023-02-14 22:09:05 +08:00
midoks e758698ee2 Update debian.sh 2023-02-14 22:02:28 +08:00
Mr Chen ea03bb056a Merge pull request #369 from midoks/dev
安装脚本优化
2023-02-14 21:59:22 +08:00
midoks 088346fd5e 安装脚本优化 2023-02-14 21:58:30 +08:00
midoks cbcb686363 Update firewall_api.py 2023-02-14 21:07:22 +08:00
Mr Chen 628175a8ee Merge pull request #368 from midoks/dev
0.12.3
2023-02-14 20:13:04 +08:00
Mr Chen 96d0115257 Merge branch 'master' into dev 2023-02-14 20:12:50 +08:00
midoks 801fdf51a7 0.12.3
### 版本更新 0.12.3

* 优先使用firewalld防火墙。
* PHP[APT]加入curl扩展。
* 配置添加【未认证响应状态】功能。
* 存在ipv6时,启动时强制开启。
* OP防火墙-性能优化。
* 网站统计优化。
* 优化backup_ftp插件。
* 各种细节优化。
2023-02-14 20:11:16 +08:00
midoks 24fa3a7ca9 优化backup_ftp插件 2023-02-14 18:29:08 +08:00
midoks 3dc781498c Update index.py 2023-02-14 18:24:43 +08:00
midoks c3afaeb35a Update index.py 2023-02-14 18:23:42 +08:00
midoks 4390180a89 Update config_api.py 2023-02-14 18:20:54 +08:00
midoks 98e22f6eeb Update index.py 2023-02-14 18:19:00 +08:00
midoks 3bfd0f34f5 Update index.py 2023-02-14 18:14:27 +08:00
midoks ec8e46494a Update crontab.js 2023-02-14 18:10:14 +08:00
midoks 7cb9b0acec Update index.py 2023-02-14 17:56:16 +08:00
midoks f97d2a5987 Update ngx_debug.sh 2023-02-13 20:46:19 +08:00
midoks 5a50fe70a8 Update ngx_debug.sh 2023-02-13 20:45:57 +08:00
midoks 4ecb28575e Update install.sh 2023-02-13 20:34:25 +08:00
midoks 07bf8f162c Update debian.sh 2023-02-13 19:27:08 +08:00
midoks 19beb1bccc Update requirements.txt 2023-02-13 19:21:24 +08:00
midoks 33c27d7924 up 2023-02-13 18:56:49 +08:00
midoks a9fb6c9ec9 up 2023-02-13 18:15:10 +08:00
midoks 7198a4de95 up 2023-02-13 15:56:41 +08:00
midoks 53cb56ddf2 up 2023-02-13 15:39:57 +08:00
midoks e4582f328c Update index.py 2023-02-13 14:35:14 +08:00
midoks bb7f1b6a7d up 2023-02-13 14:16:06 +08:00
midoks 1781d879d1 up 2023-02-13 13:36:32 +08:00
midoks d89158124f Update ngx_debug.sh 2023-02-13 03:29:54 +08:00
midoks bfdad5f9a3 Update ngx_debug.sh 2023-02-13 03:23:40 +08:00
midoks 165d9ba14d Update webstats_common.lua 2023-02-13 03:05:26 +08:00
midoks ad102aa940 Update ngx_debug.sh 2023-02-13 03:01:16 +08:00
midoks 1489d15d74 Update ngx_debug.sh 2023-02-13 02:31:08 +08:00
midoks 2dbda3e6dd 优化,Linux系统不再加入到计划任务获取cpu使用率 2023-02-13 01:58:36 +08:00
midoks 4775d4f756 Update waf_common.lua 2023-02-13 01:45:49 +08:00
midoks f57fd600e6 Update test_get_cpu.lua 2023-02-13 01:38:24 +08:00
midoks bf281107b2 up 2023-02-13 00:26:31 +08:00
midoks 083fe59fa5 up 2023-02-12 17:29:29 +08:00
midoks 8f36e0ccb2 Update centos.sh 2023-02-12 02:15:12 +08:00
midoks a5b3a650eb up 2023-02-12 02:14:18 +08:00
midoks 177a08af91 Update waf_common.lua 2023-02-12 02:12:34 +08:00
midoks 5ef4b3d05d firewalld优先 2023-02-12 02:12:28 +08:00
midoks ad3d93cf8b firewalld防火墙优先 2023-02-12 02:03:57 +08:00
midoks 72643b7776 Update rhel.sh 2023-02-12 02:02:27 +08:00
midoks 45128636b0 up 2023-02-12 01:17:23 +08:00
midoks 7b962335e4 up 2023-02-12 01:08:03 +08:00
midoks c0c67095be up 2023-02-12 01:02:19 +08:00
midoks b3a0f11a80 up 2023-02-12 00:44:35 +08:00
midoks 088337765c Update webstats_worker.lua 2023-02-12 00:35:52 +08:00
midoks 066e7087c9 Update webstats_common.lua 2023-02-12 00:35:39 +08:00
midoks 29144595a6 网站统计 2023-02-12 00:22:39 +08:00
midoks 45c2b3cdca Update index.py 2023-02-11 23:44:19 +08:00
midoks e3ecdfb2b3 Update index.py 2023-02-11 23:43:51 +08:00
midoks 560d482f31 up 2023-02-11 23:25:29 +08:00
midoks 62ffc94b69 up 2023-02-11 22:34:18 +08:00
midoks 0cb10cc926 Update webstats_common.lua 2023-02-11 22:23:34 +08:00
midoks fb4eb8747c Update webstats_common.lua 2023-02-11 22:16:08 +08:00
midoks 1a26b83a46 Update webstats_log.lua 2023-02-11 22:06:04 +08:00
midoks 7c2316f005 Update webstats_common.lua 2023-02-11 21:50:32 +08:00
midoks 149baeb1fc Update webstats_log.lua 2023-02-11 21:42:57 +08:00
midoks 9714bcb419 Update init.lua 2023-02-11 17:45:17 +08:00
midoks 4e6a54dbad Update index.py 2023-02-11 17:18:24 +08:00
midoks 18498813c6 up 2023-02-11 17:06:24 +08:00
midoks 854e34a90c Update mw.py 2023-02-11 16:48:57 +08:00
midoks a0898c849e Update lua.conf 2023-02-11 16:47:46 +08:00
midoks 3a26b71828 ip 2023-02-11 16:45:28 +08:00
midoks f500dcac8b Update init_worker.lua 2023-02-11 16:06:25 +08:00
midoks 3a6e2e6e4b Update cpu_usage_file.sh 2023-02-11 16:06:08 +08:00
midoks 1d439e788c Update soft.html 2023-02-11 15:25:44 +08:00
midoks 75a2fcdaaf Update debian.sh 2023-02-11 15:24:17 +08:00
midoks 2ca8598671 up 2023-02-11 06:04:55 +08:00
midoks 2d42486ac5 Update index.py 2023-02-11 05:30:38 +08:00
midoks 24f22b5b8c up 2023-02-11 05:28:54 +08:00
midoks 68f8638638 up 2023-02-11 05:28:27 +08:00
midoks fe6b16eda4 Update index.py 2023-02-11 05:27:34 +08:00
midoks 4e35c34854 up 2023-02-11 05:24:53 +08:00
midoks cc51d9501f Update init.lua 2023-02-11 05:04:37 +08:00
midoks c08cfd4763 Update ip_white.json 2023-02-11 05:00:58 +08:00
midoks 5fdcb8bf71 up 2023-02-11 04:43:30 +08:00
midoks ce3b232c4b Update mw.py 2023-02-11 04:03:41 +08:00
midoks ec7ae65f7d up 2023-02-11 02:52:02 +08:00
midoks e4c5212ac4 Update public.js 2023-02-11 02:51:42 +08:00
midoks 23442accaf Update mw.py 2023-02-11 02:51:25 +08:00
midoks 2e6f5f9add up 2023-02-11 02:51:17 +08:00
midoks 110e356fae up 2023-02-09 17:05:06 +08:00
midoks a243d088ee Update tool_task.py 2023-02-05 16:22:17 +08:00
midoks 365fd466c2 更快获取cpu使用率 2023-02-05 16:21:30 +08:00
midoks d1304de69e Update cli.sh 2023-02-05 15:22:53 +08:00
midoks e51fa2608d Update cli.sh 2023-02-05 15:21:45 +08:00
midoks cbfcd00317 Update mw.tpl 2023-02-05 15:20:08 +08:00
midoks 7a96d3abc5 Update mw.tpl 2023-02-05 15:19:50 +08:00
midoks 33a510a5b5 Update mw.tpl 2023-02-05 12:50:26 +08:00
midoks e8942128b8 Update tool_task.py 2023-02-05 12:49:21 +08:00
midoks 7e2bd928e2 Update tool_task.py 2023-02-05 12:42:30 +08:00
midoks 89185476aa 优化获取cpu方式。 2023-02-05 12:37:49 +08:00
midoks f491641ab8 Create cpu_usage.sh 2023-02-05 12:25:57 +08:00
midoks 8b108d1aff Update ubuntu.sh 2023-02-05 12:16:55 +08:00
midoks 2cfa7ec2ab Update ubuntu.sh 2023-02-05 12:07:29 +08:00
midoks d63aae100d Update ubuntu.sh 2023-02-05 11:57:44 +08:00
midoks e8249adc53 Update tool_task.py 2023-02-05 03:48:22 +08:00
midoks 281427e479 Update tool_task.py 2023-02-05 03:48:03 +08:00
midoks af775fc77d OP防火墙-获取cpu优化 2023-02-05 03:40:18 +08:00
midoks f328354125 #362 2023-02-05 00:25:59 +08:00
midoks 1f70f962cf Update crontab.js 2023-02-05 00:25:25 +08:00
midoks 414787447e Update README.md 2023-02-04 19:27:55 +08:00
midoks 979f2174e5 Update README.md 2023-02-03 21:28:29 +08:00
midoks 84d1c96094 Update config_api.py 2023-02-03 21:17:25 +08:00
midoks 0d8d9022a0 Update mw.py 2023-02-03 21:17:07 +08:00
midoks 44d0e9e11c 存在ipv6,启动时强制开启 2023-02-03 13:42:35 +08:00
midoks 771d6e04ba Update mw.tpl 2023-02-03 13:39:45 +08:00
midoks 87563daa1d Update index.html 2023-02-02 20:50:42 +08:00
midoks 9fefb285e1 Update .gitignore 2023-02-02 20:47:18 +08:00
midoks b9ca4fc11e 配置添加【未认证响应状态】功能 2023-02-02 18:04:44 +08:00
midoks 2646d0565d Update debian.sh 2023-02-02 15:23:35 +08:00
midoks e1f900f1c5 Update tools.py 2023-02-02 14:59:15 +08:00
midoks 908b3341b2 Update rhel.sh 2023-02-01 13:25:01 +08:00
midoks 5aef2467b1 Update debian.sh 2023-02-01 13:20:53 +08:00
midoks f59114a688 自动获取ssh端口 2023-02-01 13:09:28 +08:00
midoks 363ed8b3ff Update index.py 2023-01-31 20:07:39 +08:00
midoks 3970feacc3 Update gitea.js 2023-01-31 19:48:30 +08:00
midoks f6c528149e Update gitea.js 2023-01-31 19:41:00 +08:00
midoks 8f6b5ddba9 Update gitea.js 2023-01-31 19:36:50 +08:00
midoks ef350b9fd2 Update gitea.js 2023-01-31 19:33:46 +08:00
midoks 004dfb5e7d apt php curl是必须的扩展 2023-01-30 14:01:57 +08:00
midoks 3c08d7c159 Update files_api.py 2023-01-30 01:17:09 +08:00
midoks e227204065 编码识别优化。 2023-01-30 00:50:10 +08:00
midoks ce653b7a6a Update README.md 2023-01-29 22:31:52 +08:00
Mr Chen a207215775 Merge pull request #355 from midoks/midoks-patch-1
Update debian.sh
2023-01-29 20:52:22 +08:00
Mr Chen 8115b0fb4d Update debian.sh 2023-01-29 20:48:51 +08:00
midoks 048bd573e8 修复API接口使用 2023-01-29 14:32:38 +08:00
midoks 918ea8d0f9 Update mw.tpl 2023-01-28 11:22:57 +08:00
midoks 97d2fd5a63 Update debian.sh 2023-01-28 10:50:31 +08:00
midoks 617a517048 Update index.html 2023-01-28 01:57:54 +08:00
midoks 2e7910a84c 一键迁移初始化 2023-01-28 01:34:48 +08:00
midoks 2ee4c4e3fd 页面更新跟随更新一遍环境 2023-01-27 21:58:10 +08:00
midoks 9ae4d32ed4 卸载时,删除首页显示 2023-01-27 21:05:16 +08:00
midoks 90a7f4ea10 Update .gitignore 2023-01-27 20:09:09 +08:00
midoks d22cb8305d Update soft.html 2023-01-27 20:08:34 +08:00
midoks a7a78f2103 Update .gitignore 2023-01-27 16:20:50 +08:00
Mr Chen db7ec00c07 Merge pull request #352 from midoks/dev
优化在命令行下修改端口命令
2023-01-27 16:09:29 +08:00
midoks dd21d25295 Update tools.py 2023-01-27 16:04:54 +08:00
midoks f7360aa081 Update tools.py 2023-01-27 16:02:21 +08:00
midoks 1d1ba7192a Update tools.py 2023-01-27 16:00:02 +08:00
midoks 0bf540c5e7 Update mw.py 2023-01-27 15:54:55 +08:00
Mr Chen 696797f125 Merge pull request #350 from midoks/dev
0.12.2
2023-01-27 14:41:55 +08:00
midoks fcfad78506 Update .gitignore 2023-01-27 14:40:57 +08:00
midoks 4787d6e7bb 0.12.2
* 开放菜单权限配置。
* 升级SSH终端2.0。
* 增加已安装类型。
* 加入切换linux软件源的命令。
* iptables安装优化。
* 网站统计POST获取数据优化。
* mysql[apt/yum]迁移优化。
* 优化防火墙导入。
* 图标可设置。
* 各种细节优化。
2023-01-27 14:39:46 +08:00
midoks 80ef6dbe4c 描述修改 2023-01-27 13:40:35 +08:00
midoks 78cdad5fb8 Update install.sh 2023-01-27 13:36:06 +08:00
midoks 274f0638ac Update install.sh 2023-01-27 13:35:45 +08:00
midoks 1e97b146db up 2023-01-27 13:33:11 +08:00
midoks fbbc506de9 Update .gitignore 2023-01-27 02:16:32 +08:00
midoks 58ec4c2c30 简单加密 2023-01-27 01:19:26 +08:00
midoks a2298ade04 Update webssh.js 2023-01-27 00:53:34 +08:00
midoks d5fc464603 ssh update 90% 2023-01-27 00:40:06 +08:00
midoks c952be4e45 up 2023-01-26 22:49:55 +08:00
midoks 17a92518b4 Update term-websocketio.js 2023-01-26 19:35:51 +08:00
midoks c7154eea7b Update files.js 2023-01-26 19:35:43 +08:00
midoks 7cab1492c0 Update ssh_terminal.py 2023-01-26 04:21:49 +08:00
midoks 77b6391d9e Update ssh_terminal.py 2023-01-26 04:21:25 +08:00
midoks 08a91ed0f2 ssh终端收尾阶段 2023-01-26 04:11:51 +08:00
midoks b4d090021c up 2023-01-26 03:14:50 +08:00
midoks bf064d89f4 up 2023-01-26 02:06:02 +08:00
midoks 7314071634 ssh优化 2023-01-26 02:05:39 +08:00
midoks 49a606baa2 Update mw.tpl 2023-01-25 19:55:54 +08:00
midoks aaabe5628e Update debian.sh 2023-01-25 19:54:39 +08:00
midoks ea03373363 Update ssh_terminal.py 2023-01-25 19:42:17 +08:00
midoks 515d25648f ssh优化 2023-01-25 17:17:14 +08:00
midoks a30792178a up 2023-01-25 16:50:10 +08:00
midoks 1c74ce3c58 ssh 60% 2023-01-25 16:20:03 +08:00
midoks 832f3b45eb up 2023-01-25 03:20:55 +08:00
midoks 2c3ad7ea99 Update webstats_common.lua 2023-01-23 20:30:01 +08:00
midoks 6b3dae6169 Update webstats_common.lua 2023-01-23 20:21:00 +08:00
midoks d387546986 Update requirements.txt 2023-01-23 04:21:52 +08:00
midoks 6929c5a173 Update requirements.txt 2023-01-23 04:21:02 +08:00
midoks d66e9d7fc6 up 2023-01-23 04:09:25 +08:00
midoks 17470600e9 Update app.py 2023-01-23 04:06:43 +08:00
midoks d1017ddb5c Create term.js 2023-01-23 03:45:55 +08:00
midoks 67161e62ce Update cli.sh 2023-01-23 03:44:44 +08:00
midoks 87ad51f6ad up 2023-01-22 18:34:46 +08:00
midoks 2239d178f4 Update site.css 2023-01-22 15:33:22 +08:00
midoks 4ff95f057c 增加已安装类型 2023-01-22 15:33:14 +08:00
midoks f32ba65596 Update webssh.js 2023-01-22 13:28:42 +08:00
midoks d3db79a4ed webssh 55% 2023-01-22 03:17:09 +08:00
midoks ddc8cde44c Delete cmd.json 2023-01-22 01:32:42 +08:00
midoks 719ad042c7 webssh 50% 2023-01-22 01:31:47 +08:00
midoks 9134dfb1a9 Update index.css 2023-01-20 03:02:45 +08:00
midoks 9611834a9d up 2023-01-20 02:54:55 +08:00
midoks 8d5cc2e926 Update __init__.py 2023-01-20 02:34:45 +08:00
midoks 01b9c51247 Update __init__.py 2023-01-20 02:31:59 +08:00
midoks 21ca9d8874 优化ssh并兼容mac 2023-01-20 02:31:43 +08:00
midoks d2d5a0e12f up 2023-01-20 02:11:07 +08:00
midoks ad2e9958f8 Update mw.tpl 2023-01-19 22:24:15 +08:00
midoks 7cf64673f9 加入切换linux软件源的命令 2023-01-19 22:18:35 +08:00
midoks c49bc6f823 Update README.md 2023-01-19 18:04:00 +08:00
midoks 24e6c57eec Update firewall.js 2023-01-18 17:58:23 +08:00
midoks a6842f2679 Update firewall_api.py 2023-01-18 17:42:56 +08:00
midoks 42ba0ea285 优化iptables判断 2023-01-18 17:27:33 +08:00
midoks 19e6055b7c Update install.sh 2023-01-18 01:38:18 +08:00
midoks 1388c697e0 iptables安装优化 2023-01-18 01:38:06 +08:00
midoks 2a97c0f8b3 Update plugin_menu.html 2023-01-18 01:12:24 +08:00
midoks 8aa4084373 Update plugins_api.py 2023-01-18 01:03:23 +08:00
midoks 4e16773ed8 Update layout.html 2023-01-18 00:57:38 +08:00
midoks b4b9b6eb1f Create plugin_menu.html 2023-01-18 00:57:30 +08:00
midoks b35ae736a3 up 2023-01-18 00:57:25 +08:00
midoks ec78197317 Update plugins_api.py 2023-01-17 15:58:27 +08:00
midoks 1558e93f33 Update .gitignore 2023-01-17 15:37:22 +08:00
midoks 99a15ffc82 Update mw.py 2023-01-17 03:46:04 +08:00
Mr Chen 2b0b697a6a Merge pull request #343 from midoks/dev
修复iptables和firewalld同时存在,导致识别出错2。
2023-01-17 00:17:30 +08:00
midoks a93953a61d Update firewall_api.py 2023-01-17 00:04:56 +08:00
Mr Chen 1ce68581af Merge pull request #342 from midoks/dev
修复iptables和firewalld同时存在,导致识别出错。
2023-01-16 23:53:17 +08:00
midoks df5979a4cf Update firewall_api.py 2023-01-16 23:48:26 +08:00
midoks 9197dcbe03 Update firewall_api.py 2023-01-16 23:48:05 +08:00
midoks 4100824c43 Update firewall_api.py 2023-01-16 23:39:16 +08:00
midoks c22b2b3774 Update firewall_api.py 2023-01-16 23:34:46 +08:00
midoks 0fbd3163e2 up 2023-01-16 23:30:12 +08:00
midoks 500171ed66 Update mw.py 2023-01-16 21:43:38 +08:00
midoks 1c6b6f1090 Update mw.py 2023-01-16 21:43:18 +08:00
midoks fc8a54c4b3 Update app.py 2023-01-16 02:04:12 +08:00
midoks 4ade42380d Update layout.html 2023-01-14 22:43:40 +08:00
midoks 6b2c21ab47 Update install.sh 2023-01-14 20:01:54 +08:00
Mr Chen 6f5ea82a58 Merge pull request #340 from midoks/dev
修复Webhook 插件BUG
2023-01-14 16:53:51 +08:00
midoks 73c849931c #338
已经优化
2023-01-14 16:51:38 +08:00
midoks 39d76f5621 #338 2023-01-14 16:42:22 +08:00
Mr Chen bc435ea848 Merge pull request #339 from midoks/dev
修复网站统计-设置站点配置产生的问题。
2023-01-14 15:31:45 +08:00
midoks ed82dd3c12 Update webstats_common.lua 2023-01-14 15:14:51 +08:00
midoks 0a5a86625f 修复设置错误 2023-01-14 13:46:59 +08:00
midoks 53f56c949b Update sg11.sh 2023-01-13 00:55:49 +08:00
midoks 7e61e37d80 up 2023-01-12 19:49:31 +08:00
midoks c05d0244db Update xhprof.js 2023-01-12 17:50:56 +08:00
midoks 226d0addc5 图标可设置 2023-01-12 14:31:12 +08:00
midoks 35cce35ff3 #334 2023-01-11 17:47:49 +08:00
midoks 9450103c4b 文字优化 2023-01-11 12:01:44 +08:00
Mr Chen 4478f005e7 Merge pull request #333 from midoks/dev
mysql[apt/yum]迁移功能优化
2023-01-10 17:49:27 +08:00
midoks 71be0e3196 Update index.py 2023-01-10 17:47:38 +08:00
midoks e6a79216c4 mysql[apt/yum]迁移优化 2023-01-10 17:45:03 +08:00
midoks 3f12d4b8d6 Update index.py 2023-01-10 17:41:06 +08:00
midoks 683f041b1d Update update_dev.sh 2023-01-10 17:37:33 +08:00
midoks e08cebd48e Update update_dev.sh 2023-01-10 17:32:27 +08:00
midoks d02dea3150 Update update_dev.sh 2023-01-10 17:32:17 +08:00
midoks 18bb92c9e2 Update update_dev.sh 2023-01-10 17:29:08 +08:00
midoks f9b45ee4d9 优化更新脚本 2023-01-10 17:25:38 +08:00
midoks dcbc86ea5c Update index.py 2023-01-10 17:08:26 +08:00
midoks 300728cb17 Update index.py 2023-01-10 17:04:58 +08:00
midoks 51aa028f97 Update index.py 2023-01-10 17:03:36 +08:00
midoks e30ac33cea Update index.py 2023-01-10 16:59:14 +08:00
midoks 1c2f3c4378 Update __init__.py 2023-01-10 01:06:58 +08:00
Mr Chen 17eadfccff Merge pull request #332 from midoks/dev
优化mysql初始化
2023-01-09 23:36:58 +08:00
midoks b55a6d3bd9 Update index.py 2023-01-09 23:31:00 +08:00
Mr Chen 48936e7918 Merge pull request #331 from midoks/dev
php-apt/php-yum安装优化
2023-01-09 15:14:59 +08:00
midoks 1a99c53cb6 composer和php-apt的优化 2023-01-09 15:10:14 +08:00
midoks a8b5df25d0 php-yum优化 2023-01-09 14:46:08 +08:00
midoks 73b4be90f5 Update install.sh 2023-01-09 14:33:15 +08:00
midoks 1ecf12ab5c php-yum update 2023-01-09 14:08:02 +08:00
midoks 758da44141 up 2023-01-09 11:39:52 +08:00
midoks 8aa44408cc Update install.sh 2023-01-09 11:27:34 +08:00
Mr Chen ccf4ab7d81 Merge pull request #330 from midoks/dev
OP防火墙和phpmyadmin轻微优化
2023-01-08 15:21:28 +08:00
midoks 61766a8942 Update init.lua 2023-01-08 13:49:42 +08:00
midoks ce9ac280c7 Update index.py 2023-01-08 13:04:22 +08:00
Mr Chen 94bd8cbe8e Merge pull request #329 from midoks/dev
修复绑定域名问题
2023-01-07 23:31:21 +08:00
midoks 705f2445ee Update site_api.py 2023-01-07 23:24:33 +08:00
Mr Chen bc8d16f2c2 Merge pull request #328 from midoks/dev
OpenResty配置优化
2023-01-07 23:01:09 +08:00
midoks fe9eb7dc38 Update cert_api.py 2023-01-07 19:39:01 +08:00
midoks 94195b5246 优化防火墙导入 2023-01-07 19:21:12 +08:00
midoks 24009267be Update index.py 2023-01-07 16:02:27 +08:00
midoks c28412aea5 Update index.py 2023-01-07 15:55:56 +08:00
midoks 847b9d569b Update index.py 2023-01-07 15:49:37 +08:00
midoks b3c3a4457d op防火墙优化
1.适配openresty配置修改。
2.加入一个测试功能,便于测试。
2023-01-07 15:36:44 +08:00
midoks 89a84f5ca0 Update lua.conf 2023-01-07 15:08:27 +08:00
midoks 4432fa15cc Update lua.conf 2023-01-07 14:17:33 +08:00
midoks c6a600299d Update lua.conf 2023-01-07 12:29:50 +08:00
midoks 082b0ebcc3 openresty 默认开启 2023-01-07 12:23:50 +08:00
midoks 828436b772 Update webstats_common.lua 2023-01-07 03:00:55 +08:00
midoks 658a727fe3 Update index.py 2023-01-07 01:42:52 +08:00
midoks bedd6e5409 Update index.py 2023-01-07 01:02:47 +08:00
midoks ef8bc8bf57 Update index.py 2023-01-07 01:00:52 +08:00
midoks a22f89797a Update index.py 2023-01-07 00:50:20 +08:00
midoks eaf341cda6 Update index.py 2023-01-07 00:37:20 +08:00
midoks 58d69d734d 优化【网站统计】强制获取日志POST信息。 2023-01-07 00:15:54 +08:00
midoks e19f7702db Update tool_task.py 2023-01-06 20:52:07 +08:00
midoks b546a39f29 Update index.py 2023-01-06 20:32:12 +08:00
midoks 81b570214a Update index.py 2023-01-06 20:29:26 +08:00
midoks 44401f5944 Update webstats_common.lua 2023-01-06 20:12:31 +08:00
midoks 4ad190586a 调试请求日志 2023-01-06 20:08:27 +08:00
midoks 25bca124b4 Update index.py 2023-01-06 18:09:06 +08:00
midoks 98165bb449 修复网站统计-监控配置 2023-01-06 18:00:22 +08:00
midoks 6693edb924 Update install.sh 2023-01-06 16:03:02 +08:00
Mr Chen bae1beec3e Merge pull request #327 from midoks/dev
修复网站统计【初始安装】不可用
2023-01-06 12:34:01 +08:00
midoks 0682ca4d4d #326
修复
2023-01-06 12:31:26 +08:00
midoks 5bc10bebd0 Update index.py 2023-01-06 12:17:21 +08:00
Mr Chen 25df8b430b Merge pull request #325 from midoks/dev
修复子域名绑定没有正常删除问题
2023-01-06 00:27:22 +08:00
midoks 4f8fc31ca3 修复子域名绑定没有正常删除问题 2023-01-06 00:25:21 +08:00
Mr Chen a44f54e719 Merge pull request #324 from midoks/dev
修复绑定域名问题
2023-01-05 21:41:09 +08:00
midoks 12068046ec 修复绑定域名问题 2023-01-05 21:31:39 +08:00
midoks d6f57e5ea7 Update install.sh 2023-01-04 21:24:40 +08:00
Mr Chen 869d7795a6 Merge pull request #321 from midoks/dev
0.12.1
2023-01-04 20:58:27 +08:00
midoks 0a3d976c6a 0.12.1
* mysql/mariadb/mysql-apt/mysql-yum增加新的同步方式[同步账户]。
* pureftp安装时自动放行端口。
* 面板ssl,添加【申请Lets证书】功能。
* 站点子域名绑定优化。
* 修复添加数据库名字判断。
* 增加PHP52的隐藏和显示指令。
* 各种细节优化。
2023-01-04 20:57:11 +08:00
midoks c4d39ef9fb Update intl.sh 2023-01-04 20:50:18 +08:00
midoks 8e5b499601 Update install.sh 2023-01-04 20:41:26 +08:00
midoks e6dff6094c Update tools.py 2023-01-04 20:21:52 +08:00
midoks 47dec45a27 Update tools.py 2023-01-04 20:20:47 +08:00
midoks 4706af7423 Update tools.py 2023-01-04 20:18:50 +08:00
midoks 1bc458eb8f 增加PHP52的隐藏和显示指令 2023-01-04 20:17:20 +08:00
midoks dc3f872592 Update config_api.py 2023-01-04 19:13:08 +08:00
midoks 3d48289329 up 2023-01-04 18:52:24 +08:00
midoks 6126539929 Update config_api.py 2023-01-04 18:00:49 +08:00
midoks cff5df6d13 Update requirements.txt 2023-01-04 17:57:55 +08:00
midoks 23a0b74f9d Update config.js 2023-01-04 17:49:58 +08:00
midoks 5927a3abab 面板ssl申请 2023-01-04 17:48:07 +08:00
midoks 4b176ad702 修复子目录绑定ssl申请问题 2023-01-03 23:45:51 +08:00
midoks 577103506d Update site_api.py 2023-01-03 23:39:40 +08:00
midoks 18f8d2b905 Update files.js 2023-01-03 18:56:56 +08:00
midoks 3a6b5339d2 Update config_api.py 2023-01-03 18:50:07 +08:00
midoks c25d4305f7 Update files.js 2023-01-03 18:49:58 +08:00
midoks b6ef87e97b up 2023-01-03 18:43:25 +08:00
midoks 4335668cd4 Update config_api.py 2023-01-03 18:29:20 +08:00
midoks 0fc2ab011d Update files.js 2023-01-03 18:26:20 +08:00
midoks 683035e6b7 up 2023-01-03 18:06:28 +08:00
midoks f2dfa7ad48 Update index.py 2023-01-03 14:55:39 +08:00
midoks 2ae2c188e7 Update index.py 2023-01-03 14:54:57 +08:00
midoks b5cd0564cb 安装,自动放行端口 2023-01-03 14:43:34 +08:00
midoks 2f274ce202 Update pure-ftpd.conf 2023-01-03 14:14:36 +08:00
midoks 0a67932473 域名添加重复bug优化 2023-01-02 22:56:19 +08:00
midoks 7b7cb215c4 Update config_api.py 2023-01-02 20:47:05 +08:00
midoks f6e98ae286 站点配置保持优化 2023-01-02 20:46:09 +08:00
midoks 5958f6e067 Update info.json 2023-01-02 12:01:05 +08:00
midoks 6dadfb1019 Update mysql.sql 2023-01-02 11:58:41 +08:00
midoks f5aeb5664b Update index.py 2023-01-02 11:58:08 +08:00
midoks ca1080937d Update index.py 2023-01-02 11:52:57 +08:00
midoks 1dd934ea3f mysql-yum加入主从同步 2023-01-02 11:47:35 +08:00
midoks a34512d057 Update ubuntu.sh 2023-01-02 01:06:54 +08:00
midoks 575c1e48ae Update info.json 2023-01-02 01:02:13 +08:00
midoks 9f3ee4997b up 2023-01-02 00:59:57 +08:00
midoks cfaa2d9f25 up 2023-01-02 00:30:44 +08:00
midoks aded1fc731 Update index.py 2023-01-02 00:16:12 +08:00
midoks 4a6e847743 Update index.py 2023-01-02 00:13:20 +08:00
midoks 24fac51538 Update index.py 2023-01-02 00:12:49 +08:00
midoks 148b32fed3 Update mariadb.js 2023-01-02 00:11:00 +08:00
midoks fff19d2c4a Update my.cnf 2023-01-02 00:04:43 +08:00
midoks a5e0332179 Update index.py 2023-01-01 23:42:14 +08:00
midoks 1349655570 up 2023-01-01 23:37:43 +08:00
midoks b9e617bf0f Update index.py 2023-01-01 21:58:26 +08:00
midoks f65d10e1e4 up 2023-01-01 21:56:45 +08:00
midoks 6d9ec087ce Update index.py 2023-01-01 21:18:55 +08:00
midoks bfb9da0471 up 2023-01-01 20:55:32 +08:00
midoks 8a95a68c81 Update update_dev.sh 2023-01-01 20:55:09 +08:00
midoks d9d81ded3d mysql-apt添加主从同步功能 2023-01-01 20:43:00 +08:00
midoks 403660c980 Update config_api.py 2023-01-01 20:26:36 +08:00
midoks ac06cfc83c mariadb添加新的同步方式 2023-01-01 20:22:06 +08:00
midoks c50012b9b5 Update index.py 2023-01-01 17:17:05 +08:00
midoks baeb0ddc8f up 2023-01-01 17:00:47 +08:00
midoks 83f8c99275 Update update.sh 2022-12-31 21:06:55 +08:00
midoks 2d2b454a31 Update update_dev.sh 2022-12-31 21:05:36 +08:00
midoks aad41ce14b Update index.py 2022-12-31 20:57:56 +08:00
midoks ebf8c82d33 导入优化 2022-12-31 20:30:02 +08:00
midoks d25100fec3 Update index.py 2022-12-31 20:01:20 +08:00
midoks fe658d9009 Update config_api.py 2022-12-31 17:10:58 +08:00
midoks f37c2c9bc6 Update index.py 2022-12-31 16:46:28 +08:00
midoks a1550765aa up 2022-12-31 14:52:39 +08:00
midoks b020f23161 Update info.json 2022-12-31 13:37:11 +08:00
midoks 4afe2061e2 Update mysql.js 2022-12-31 13:32:30 +08:00
midoks ab64c01981 up 2022-12-31 13:14:30 +08:00
midoks 8a97734700 Update index.py 2022-12-31 13:08:03 +08:00
midoks 16b4c64aa5 Update index.py 2022-12-31 13:07:11 +08:00
midoks df2a5919af Update my5.7.cnf 2022-12-30 20:41:51 +08:00
midoks 40a81b794c Update my5.7.cnf 2022-12-30 20:38:40 +08:00
midoks 8fc7ab3abf up 2022-12-30 20:14:28 +08:00
midoks ca4205c89a up 2022-12-30 20:13:35 +08:00
midoks c89b15a645 Update mysql.js 2022-12-30 20:08:00 +08:00
midoks 7a863c4544 修复添加数据库名字判断 2022-12-30 20:07:31 +08:00
midoks 5d81406fdf 优化mysql主从同步UI 2022-12-30 15:21:33 +08:00
midoks cce59c0521 up 2022-12-30 14:56:31 +08:00
Mr Chen 0a5c5aae12 Merge pull request #320 from midoks/midoks-patch-1
Create requirements.txt
2022-12-30 14:25:03 +08:00
Mr Chen 7478426206 Create requirements.txt 2022-12-30 14:24:27 +08:00
midoks c0ed736edb Update index.py 2022-12-30 12:18:35 +08:00
midoks 313eb25ccd Update mysql.js 2022-12-30 12:10:11 +08:00
midoks f58595755b Update index.py 2022-12-30 12:01:02 +08:00
midoks 18a892a576 Update update_dev.sh 2022-12-30 00:32:51 +08:00
midoks 03d558092f Update update_dev.sh 2022-12-30 00:32:20 +08:00
midoks 3cffa9223a Update my5.7.cnf 2022-12-30 00:28:50 +08:00
midoks c0d5aff2f5 mysql配置优化 2022-12-30 00:06:25 +08:00
midoks 3fa4c7804b Update mysql.sql 2022-12-29 23:32:20 +08:00
midoks 22acce8bc0 mysql主从加入新的同步方式 2022-12-29 23:32:14 +08:00
midoks e53e5cd672 Update mw.py 2022-12-28 23:41:02 +08:00
midoks a7d861de22 up 2022-12-28 23:38:37 +08:00
midoks ddeef3c17e up 2022-12-28 23:21:07 +08:00
midoks af733ac28a Merge branch 'master' into dev 2022-12-28 22:34:24 +08:00
Mr Chen 148e2021dc Merge pull request #318 from midoks/dependabot/pip/wheel-0.38.1
Bump wheel from 0.33.6 to 0.38.1
2022-12-28 22:33:23 +08:00
midoks 50c3fe58f4 Update rhel.sh 2022-12-28 21:54:29 +08:00
midoks 5ed3728d4f Update rhel.sh 2022-12-28 21:47:54 +08:00
midoks 9bf06c9d21 Update rhel.sh 2022-12-28 21:47:41 +08:00
midoks 08b3738cfd up 2022-12-28 21:44:18 +08:00
midoks a04f59a3aa Update rhel.sh 2022-12-28 21:31:55 +08:00
midoks 155b8d2b4c Update rhel.sh 2022-12-28 17:26:56 +08:00
dependabot[bot] 039d2794ab Bump wheel from 0.33.6 to 0.38.1
Bumps [wheel](https://github.com/pypa/wheel) from 0.33.6 to 0.38.1.
- [Release notes](https://github.com/pypa/wheel/releases)
- [Changelog](https://github.com/pypa/wheel/blob/main/docs/news.rst)
- [Commits](https://github.com/pypa/wheel/compare/0.33.6...0.38.1)

---
updated-dependencies:
- dependency-name: wheel
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-12-26 20:58:18 +00:00
midoks 0d2721523c up 2022-12-25 22:43:34 +08:00
midoks f042665c05 Update install.sh 2022-12-25 18:27:27 +08:00
midoks 0118548c24 up 2022-12-24 22:32:14 +08:00
midoks 1ba6625525 Update libsodium.sh 2022-12-24 22:29:33 +08:00
midoks 293f8ab0af up 2022-12-24 22:21:45 +08:00
midoks dc68868883 Update sodium.sh 2022-12-24 21:57:40 +08:00
midoks 30f835b6b8 Update sodium.sh 2022-12-24 21:53:10 +08:00
midoks 4d90fdfe4f Update install.sh 2022-12-21 22:28:40 +08:00
midoks bb4067aa6b Update install.sh 2022-12-21 22:25:36 +08:00
midoks 5dbf89b872 Update README.md 2022-12-21 22:11:54 +08:00
midoks cc244a8ea5 Update README.md 2022-12-21 21:39:57 +08:00
235 changed files with 16245 additions and 1855 deletions
+12
View File
@@ -151,11 +151,20 @@ data/system.db-journal
data/hook_*.json data/hook_*.json
data/letsencrypt.json data/letsencrypt.json
data/basic_auth.json data/basic_auth.json
data/notify.json
data/api.json data/api.json
data/bind_domain.pl data/bind_domain.pl
data/unauthorized_status.pl
plugins/vip_*
plugins/own_*
plugins/my_*
plugins/l2tp plugins/l2tp
plugins/openlitespeed plugins/openlitespeed
plugins/tamper_proof
plugins/tamper_proof_*
plugins/cryptocurrency_trade
plugins/op_load_balance
plugins/gdrive plugins/gdrive
plugins/mtproxy plugins/mtproxy
plugins/zimg plugins/zimg
@@ -164,6 +173,9 @@ plugins/mail
plugins/fastdfs plugins/fastdfs
plugins/v2ray plugins/v2ray
plugins/frp plugins/frp
plugins/file_search
debug.out debug.out
+26 -26
View File
@@ -29,6 +29,13 @@
基本上可以使用,后续会继续优化!欢迎提供意见! 基本上可以使用,后续会继续优化!欢迎提供意见!
- 吹水组 - https://t.me/mdserver_web - 吹水组 - https://t.me/mdserver_web
- 交流论坛 - https://bbs.midoks.me
```
如果出现问题,最好私给我面板信息。不要让我猜。如果不提供,不要提出问题,自行解决。 — 座右铭
Talk is cheap, show me the code. -- linus
```
- [兼容性测试报告](/compatibility.md) - [兼容性测试报告](/compatibility.md)
- [常用命令说明](/cmd.md) - [常用命令说明](/cmd.md)
@@ -38,6 +45,7 @@
* PHP[53-82] - PHP是世界上最好的编程语言。 * PHP[53-82] - PHP是世界上最好的编程语言。
* MySQL - 一种关系数据库管理系统。 * MySQL - 一种关系数据库管理系统。
* MariaDB - 是MySQL的一个重要分支。 * MariaDB - 是MySQL的一个重要分支。
* MySQL[APT/YUM] - 一种关系数据库管理系统。
* MongoDB - 一种非关系NOSQL数据库管理系统。 * MongoDB - 一种非关系NOSQL数据库管理系统。
* phpMyAdmin - 著名Web端MySQL管理工具。 * phpMyAdmin - 著名Web端MySQL管理工具。
* Memcached - 一个高性能的分布式内存对象缓存系统。 * Memcached - 一个高性能的分布式内存对象缓存系统。
@@ -66,13 +74,12 @@ phpMyAdmin[4.4.15]支持MySQL[5.5-5.7]
phpMyAdmin[5.2.0]支持MySQL[8.0] phpMyAdmin[5.2.0]支持MySQL[8.0]
PHP[53-72]支持phpMyAdmin[4.4.15] PHP[53-72]支持phpMyAdmin[4.4.15]
PHP[72-81]支持phpMyAdmin[5.2.0] PHP[72-82]支持phpMyAdmin[5.2.0]
``` ```
# 特别赞助 # 特别赞助
- [找资源 - 阿里云盘资源搜索引擎](https://zhaoziyuan.la/) - [找资源 - 阿里云盘资源搜索引擎](https://zhaoziyuan.la/)
- [奈飞丝 - 奈飞中国 Netflix MOD首发](https://naifeis.com/index.php#/register?code=k7P7V6Ur)
# AD - VPS推荐 - 🙏 # AD - VPS推荐 - 🙏
@@ -85,42 +92,35 @@ PHP[72-81]支持phpMyAdmin[5.2.0]
# Docker # Docker
- 由[DDSRem](https://github.com/DDSRem)开发维护。 - 由[DDSRem](https://github.com/DDSRem)开发维护。
- https://hub.docker.com/r/ddsderek/mw-server - https://hub.docker.com/r/ddsderek/mw
``` ```
docker run -itd --name mw-server --privileged=true -p 7200:7200 -p 80:80 -p 443:443 -p 888:888 ddsderek/mw-server:latest docker run -itd --name mw-server --privileged=true -p 7200:7200 -p 80:80 -p 443:443 -p 888:888 ddsderek/mw-server:latest
``` ```
### 版本更新 0.12.0 ### 版本更新 0.14.0
此次版本:默认面板名为【大圣面板】。
由于#309问题。先注释。提前发版。
* 安装脚本优化 @hiCasper。
* 数据库[pg/apt/yum/mariadb]计划任务备份ok。
* php源码加入sodium扩展。
* 站点分类问题 #300。
* 加入系统加固插件。
### JSDelivr安装地址 ### JSDelivr安装地址
- 初始安装 - 初始安装
``` ```
curl -fsSL https://cdn.jsdelivr.net/gh/midoks/mdserver-web@latest/scripts/install.sh | bash curl --insecure -fsSL https://cdn.jsdelivr.net/gh/midoks/mdserver-web@latest/scripts/install.sh | bash
``` ```
- 直接更新 - 直接更新
``` ```
curl -fsSL https://cdn.jsdelivr.net/gh/midoks/mdserver-web@latest/scripts/update.sh | bash curl --insecure -fsSL https://cdn.jsdelivr.net/gh/midoks/mdserver-web@latest/scripts/update.sh | bash
``` ```
- 卸载脚本 - 卸载脚本
``` ```
wget -O uninstall.sh https://cdn.jsdelivr.net/gh/midoks/mdserver-web@latest/scripts/uninstall.sh && bash uninstall.sh wget --no-check-certificate -O uninstall.sh https://cdn.jsdelivr.net/gh/midoks/mdserver-web@latest/scripts/uninstall.sh && bash uninstall.sh
``` ```
### 备用地址 ### 备用地址
@@ -128,20 +128,20 @@ wget -O uninstall.sh https://cdn.jsdelivr.net/gh/midoks/mdserver-web@latest/scri
- 初始安装 - 初始安装
``` ```
curl -fsSL https://raw.githubusercontent.com/midoks/mdserver-web/master/scripts/install.sh | bash curl --insecure -fsSL https://raw.githubusercontent.com/midoks/mdserver-web/master/scripts/install.sh | bash
curl --insecure -fsSL https://code.midoks.me/midoks/mdserver-web/raw/branch/master/scripts/install.sh | bash
``` ```
- 直接更新 - 直接更新
``` ```
curl -fsSL https://raw.githubusercontent.com/midoks/mdserver-web/master/scripts/update.sh | bash curl --insecure -fsSL https://raw.githubusercontent.com/midoks/mdserver-web/master/scripts/update.sh | bash
``` ```
- 卸载脚本 - 卸载脚本
``` ```
wget -O uninstall.sh https://raw.githubusercontent.com/midoks/mdserver-web/master/scripts/uninstall.sh && bash uninstall.sh wget --no-check-certificate -O uninstall.sh https://raw.githubusercontent.com/midoks/mdserver-web/master/scripts/uninstall.sh && bash uninstall.sh
``` ```
@@ -150,22 +150,22 @@ wget -O uninstall.sh https://raw.githubusercontent.com/midoks/mdserver-web/maste
- 需已经安装mdserver-web - 需已经安装mdserver-web
``` ```
curl -fsSL https://raw.githubusercontent.com/midoks/mdserver-web/dev/scripts/quick/app.sh | bash curl --insecure -fsSL https://raw.githubusercontent.com/midoks/mdserver-web/dev/scripts/quick/app.sh | bash
``` ```
### DEV使用 ### DEV使用
``` ```
curl -fsSL https://raw.githubusercontent.com/midoks/mdserver-web/dev/scripts/install_dev.sh | bash curl --insecure -fsSL https://raw.githubusercontent.com/midoks/mdserver-web/dev/scripts/install_dev.sh | bash
curl -fsSL https://raw.githubusercontent.com/midoks/mdserver-web/dev/scripts/update_dev.sh | bash curl --insecure -fsSL https://raw.githubusercontent.com/midoks/mdserver-web/dev/scripts/update_dev.sh | bash
wget -O uninstall.sh https://raw.githubusercontent.com/midoks/mdserver-web/dev/scripts/uninstall.sh && bash uninstall.sh wget --no-check-certificate -O uninstall.sh https://raw.githubusercontent.com/midoks/mdserver-web/dev/scripts/uninstall.sh && bash uninstall.sh
curl -fsSL https://raw.githubusercontent.com/midoks/mdserver-web/dev/scripts/quick/debug.sh | bash curl --insecure -fsSL https://raw.githubusercontent.com/midoks/mdserver-web/dev/scripts/quick/debug.sh | bash
curl -fsSL https://gitee.com/midoks/mdserver-web/raw/dev/scripts/install_dev.sh | bash curl --insecure -fsSL https://code.midoks.me/midoks/mdserver-web/raw/branch/dev/scripts/install_dev.sh | bash
curl -fsSL https://gitee.com/midoks/mdserver-web/raw/dev/scripts/update_dev.sh | bash curl --insecure -fsSL https://code.midoks.me/midoks/mdserver-web/raw/branch/dev/scripts/update_dev.sh | bash
``` ```
### 捐赠地址 USDT(TRC20) ### 捐赠地址 USDT(TRC20)
+5 -3
View File
@@ -20,6 +20,8 @@
import sys import sys
import io import io
import os import os
sys.dont_write_bytecode = True
from route import app, socketio from route import app, socketio
@@ -38,12 +40,12 @@ try:
PORT = int(f.read()) PORT = int(f.read())
f.close() f.close()
HOST = '0.0.0.0' # HOST = '0.0.0.0'
# app.run(host=HOST, port=PORT)
http_server = WSGIServer( http_server = WSGIServer(
(HOST, PORT), app, handler_class=WebSocketHandler) (HOST, PORT), app, handler_class=WebSocketHandler)
http_server.serve_forever() http_server.serve_forever()
socketio.run(app, host=HOST, port=PORT) socketio.run(app, host=HOST, port=PORT)
except Exception as ex: except Exception as ex:
print(ex) print(ex)
+10 -9
View File
@@ -371,7 +371,8 @@ fi
response = urllib.request.urlopen(req, timeout=timeout) response = urllib.request.urlopen(req, timeout=timeout)
return response return response
except Exception as ex: except Exception as ex:
raise Exception("requestsPost: {}".format(str(ex))) # self.getError()
raise Exception("requestsPost: {}".format(self.getError(str(ex))))
def getRequestJson(self, response): def getRequestJson(self, response):
try: try:
@@ -825,7 +826,7 @@ fi
ret_title, json.dumps(a_auth))) ret_title, json.dumps(a_auth)))
break break
if number_of_checks == self._max_check_num: if number_of_checks == self.__max_check_num:
raise StopIteration( raise StopIteration(
"错误:已尝试验证{0}次. 最大验证次数为{1}. 验证时间间隔为{2}秒.".format( "错误:已尝试验证{0}次. 最大验证次数为{1}. 验证时间间隔为{2}秒.".format(
number_of_checks, number_of_checks,
@@ -1005,7 +1006,7 @@ fi
try: try:
result = {} result = {}
x509 = OpenSSL.crypto.load_certificate( x509 = OpenSSL.crypto.load_certificate(
OpenSSL.crypto.FILETYPE_PEM, public.readFile(pem_file)) OpenSSL.crypto.FILETYPE_PEM, mw.readFile(pem_file))
# 取产品名称 # 取产品名称
issuer = x509.get_issuer() issuer = x509.get_issuer()
result['issuer'] = '' result['issuer'] = ''
@@ -1279,16 +1280,16 @@ fullchain.pem 粘贴到证书输入框
# 获取当前正在使用此证书的网站目录 # 获取当前正在使用此证书的网站目录
def getSslUsedSite(self, save_path): def getSslUsedSite(self, save_path):
pkey_file = '{}/privkey.pem'.format(save_path) pkey_file = '{}/privkey.pem'.format(save_path)
pkey = public.readFile(pkey_file) pkey = mw.readFile(pkey_file)
if not pkey: if not pkey:
return False return False
cert_paths = 'vhost/cert' cert_paths = 'vhost/cert'
import panelSite import panelSite
args = public.dict_obj() args = mw.dict_obj()
args.siteName = '' args.siteName = ''
for c_name in os.listdir(cert_paths): for c_name in os.listdir(cert_paths):
skey_file = '{}/{}/privkey.pem'.format(cert_paths, c_name) skey_file = '{}/{}/privkey.pem'.format(cert_paths, c_name)
skey = public.readFile(skey_file) skey = mw.readFile(skey_file)
if not skey: if not skey:
continue continue
if skey == pkey: if skey == pkey:
@@ -1296,7 +1297,7 @@ fullchain.pem 粘贴到证书输入框
run_path = panelSite.panelSite().GetRunPath(args) run_path = panelSite.panelSite().GetRunPath(args)
if not run_path: if not run_path:
continue continue
sitePath = public.M('sites').where( sitePath = mw.M('sites').where(
'name=?', c_name).getField('path') 'name=?', c_name).getField('path')
if not sitePath: if not sitePath:
continue continue
@@ -1314,7 +1315,7 @@ fullchain.pem 粘贴到证书输入框
self.__config['orders'] = {} self.__config['orders'] = {}
import panelSite import panelSite
siteObj = panelSite.panelSite() siteObj = panelSite.panelSite()
args = public.dict_obj() args = mw.dict_obj()
for siteName in os.listdir(cert_path): for siteName in os.listdir(cert_path):
try: try:
cert_file = '{}/{}/fullchain.pem'.format(cert_path, siteName) cert_file = '{}/{}/fullchain.pem'.format(cert_path, siteName)
@@ -1520,7 +1521,7 @@ fullchain.pem 粘贴到证书输入框
self.__config['orders'][i]['auth_to'] = auth_to self.__config['orders'][i]['auth_to'] = auth_to
# 是否到了允许重试的时间 # 是否到了允许重试的时间
if 'next_retry_time' in self._config['orders'][i]: if 'next_retry_time' in self.__config['orders'][i]:
timeout = self.__config['orders'][i][ timeout = self.__config['orders'][i][
'next_retry_time'] - int(time.time()) 'next_retry_time'] - int(time.time())
if timeout > 0: if timeout > 0:
+7 -7
View File
@@ -736,7 +736,7 @@ class cert_request:
ret_title, json.dumps(a_auth))) ret_title, json.dumps(a_auth)))
break break
if number_of_checks == self._max_check_num: if number_of_checks == self.__max_check_num:
raise StopIteration( raise StopIteration(
"错误:已尝试验证{0}次. 最大验证次数为{1}. 验证时间间隔为{2}秒.".format( "错误:已尝试验证{0}次. 最大验证次数为{1}. 验证时间间隔为{2}秒.".format(
number_of_checks, number_of_checks,
@@ -916,7 +916,7 @@ class cert_request:
try: try:
result = {} result = {}
x509 = OpenSSL.crypto.load_certificate( x509 = OpenSSL.crypto.load_certificate(
OpenSSL.crypto.FILETYPE_PEM, public.readFile(pem_file)) OpenSSL.crypto.FILETYPE_PEM, mw.readFile(pem_file))
# 取产品名称 # 取产品名称
issuer = x509.get_issuer() issuer = x509.get_issuer()
result['issuer'] = '' result['issuer'] = ''
@@ -1186,16 +1186,16 @@ fullchain.pem 粘贴到证书输入框
# 获取当前正在使用此证书的网站目录 # 获取当前正在使用此证书的网站目录
def getSslUsedSite(self, save_path): def getSslUsedSite(self, save_path):
pkey_file = '{}/privkey.pem'.format(save_path) pkey_file = '{}/privkey.pem'.format(save_path)
pkey = public.readFile(pkey_file) pkey = mw.readFile(pkey_file)
if not pkey: if not pkey:
return False return False
cert_paths = 'vhost/cert' cert_paths = 'vhost/cert'
import panelSite import panelSite
args = public.dict_obj() args = mw.dict_obj()
args.siteName = '' args.siteName = ''
for c_name in os.listdir(cert_paths): for c_name in os.listdir(cert_paths):
skey_file = '{}/{}/privkey.pem'.format(cert_paths, c_name) skey_file = '{}/{}/privkey.pem'.format(cert_paths, c_name)
skey = public.readFile(skey_file) skey = mw.readFile(skey_file)
if not skey: if not skey:
continue continue
if skey == pkey: if skey == pkey:
@@ -1203,7 +1203,7 @@ fullchain.pem 粘贴到证书输入框
run_path = panelSite.panelSite().GetRunPath(args) run_path = panelSite.panelSite().GetRunPath(args)
if not run_path: if not run_path:
continue continue
sitePath = public.M('sites').where( sitePath = mw.M('sites').where(
'name=?', c_name).getField('path') 'name=?', c_name).getField('path')
if not sitePath: if not sitePath:
continue continue
@@ -1221,7 +1221,7 @@ fullchain.pem 粘贴到证书输入框
self.__config['orders'] = {} self.__config['orders'] = {}
import panelSite import panelSite
siteObj = panelSite.panelSite() siteObj = panelSite.panelSite()
args = public.dict_obj() args = mw.dict_obj()
for siteName in os.listdir(cert_path): for siteName in os.listdir(cert_path):
try: try:
cert_file = '{}/{}/fullchain.pem'.format(cert_path, siteName) cert_file = '{}/{}/fullchain.pem'.format(cert_path, siteName)
+297 -18
View File
@@ -27,7 +27,7 @@ from flask import request
class config_api: class config_api:
__version = '0.12.0' __version = '0.14.0'
__api_addr = 'data/api.json' __api_addr = 'data/api.json'
def __init__(self): def __init__(self):
@@ -340,19 +340,37 @@ class config_api:
# 获取面板证书 # 获取面板证书
def getPanelSslApi(self): def getPanelSslApi(self):
cert = {} cert = self.getPanelSslData()
return mw.getJson(cert)
def getPanelSslData(self):
cert = {}
keyPath = 'ssl/private.pem' keyPath = 'ssl/private.pem'
certPath = 'ssl/cert.pem' certPath = 'ssl/cert.pem'
if not os.path.exists(certPath): if not os.path.exists(certPath):
mw.createSSL() # 不再自动生成证书
# mw.createSSL()
cert['privateKey'] = ''
cert['is_https'] = ''
cert['certPem'] = ''
cert['rep'] = os.path.exists('ssl/input.pl')
cert['info'] = {'endtime': 0, 'subject': '无',
'notAfter': '无', 'notBefore': '无', 'issuer': '无'}
return cert
panel_ssl = mw.getServerDir() + "/web_conf/nginx/vhost/panel.conf"
if not os.path.exists(panel_ssl):
cert['is_https'] = ''
else:
ssl_data = mw.readFile(panel_ssl)
if ssl_data.find('$server_port !~ 443') != -1:
cert['is_https'] = 'checked'
cert['privateKey'] = mw.readFile(keyPath) cert['privateKey'] = mw.readFile(keyPath)
cert['certPem'] = mw.readFile(certPath) cert['certPem'] = mw.readFile(certPath)
cert['rep'] = os.path.exists('ssl/input.pl') cert['rep'] = os.path.exists('ssl/input.pl')
cert['info'] = mw.getCertName(certPath) cert['info'] = mw.getCertName(certPath)
return mw.getJson(cert) return cert
# 保存面板证书 # 保存面板证书
def savePanelSslApi(self): def savePanelSslApi(self):
@@ -378,6 +396,134 @@ class config_api:
mw.writeFile('ssl/input.pl', 'True') mw.writeFile('ssl/input.pl', 'True')
return mw.returnJson(True, '证书已保存!') return mw.returnJson(True, '证书已保存!')
# 设置面板SSL证书设置
def setPanelHttpToHttpsApi(self):
bind_domain = 'data/bind_domain.pl'
if not os.path.exists(bind_domain):
return mw.returnJson(False, '先要绑定域名!')
keyPath = 'ssl/private.pem'
if not os.path.exists(keyPath):
return mw.returnJson(False, '未申请SSL证书!')
is_https = request.form.get('https', '').strip()
panel_ssl = mw.getServerDir() + "/web_conf/nginx/vhost/panel.conf"
if not os.path.exists(panel_ssl):
return mw.returnJson(False, '未开启面板SSL!')
if is_https == 'false':
conf = mw.readFile(panel_ssl)
if conf:
if conf.find('ssl_certificate') == -1:
return mw.returnJson(False, '当前未开启SSL')
to = "#error_page 404/404.html;\n\
#HTTP_TO_HTTPS_START\n\
if ($server_port !~ 443){\n\
rewrite ^(/.*)$ https://$host$1 permanent;\n\
}\n\
#HTTP_TO_HTTPS_END"
conf = conf.replace('#error_page 404/404.html;', to)
mw.writeFile(panel_ssl, conf)
else:
conf = mw.readFile(panel_ssl)
if conf:
rep = "\n\s*#HTTP_TO_HTTPS_START(.|\n){1,300}#HTTP_TO_HTTPS_END"
conf = re.sub(rep, '', conf)
rep = "\s+if.+server_port.+\n.+\n\s+\s*}"
conf = re.sub(rep, '', conf)
mw.writeFile(panel_ssl, conf)
mw.restartWeb()
action = '开启'
if is_https == 'true':
action = '关闭'
return mw.returnJson(True, action + 'HTTPS跳转成功!')
# 删除面板证书
def delPanelSslApi(self):
bind_domain = 'data/bind_domain.pl'
if not os.path.exists(bind_domain):
return mw.returnJson(False, '未绑定域名!')
siteName = mw.readFile(bind_domain).strip()
src_letpath = mw.getServerDir() + '/web_conf/letsencrypt/' + siteName
dst_letpath = mw.getRunDir() + '/ssl'
dst_csrpath = dst_letpath + '/cert.pem'
dst_keypath = dst_letpath + '/private.pem'
if os.path.exists(src_letpath) or os.path.exists(dst_csrpath):
if os.path.exists(src_letpath):
mw.execShell('rm -rf ' + src_letpath)
if os.path.exists(dst_csrpath):
mw.execShell('rm -rf ' + dst_csrpath)
if os.path.exists(dst_keypath):
mw.execShell('rm -rf ' + dst_keypath)
# mw.restartWeb()
return mw.returnJson(True, '已经删除SSL!')
# mw.restartWeb()
return mw.returnJson(False, '已经不存在SSL!')
# 申请面板let证书
def applyPanelLetSslApi(self):
# check domain is bind?
bind_domain = 'data/bind_domain.pl'
if not os.path.exists(bind_domain):
return mw.returnJson(False, '先要绑定域名!')
siteName = mw.readFile(bind_domain).strip()
auth_to = mw.getRunDir() + "/tmp"
to_args = {
'domains': [siteName],
'auth_type': 'http',
'auth_to': auth_to,
}
src_letpath = mw.getServerDir() + '/web_conf/letsencrypt/' + siteName
src_csrpath = src_letpath + "/fullchain.pem" # 生成证书路径
src_keypath = src_letpath + "/privkey.pem" # 密钥文件路径
dst_letpath = mw.getRunDir() + '/ssl'
dst_csrpath = dst_letpath + '/cert.pem'
dst_keypath = dst_letpath + '/private.pem'
is_already_apply = False
if not os.path.exists(src_letpath):
import cert_api
data = cert_api.cert_api().applyCertApi(to_args)
if not data['status']:
msg = data['msg']
if type(data['msg']) != str:
msg = data['msg'][0]
emsg = data['msg'][1]['challenges'][0]['error']
msg = msg + '<p><span>响应状态:</span>' + str(emsg['status']) + '</p><p><span>错误类型:</span>' + emsg[
'type'] + '</p><p><span>错误代码:</span>' + emsg['detail'] + '</p>'
return mw.returnJson(data['status'], msg, data['msg'])
else:
is_already_apply = True
mw.buildSoftLink(src_csrpath, dst_csrpath, True)
mw.buildSoftLink(src_keypath, dst_keypath, True)
mw.execShell('echo "lets" > "' + dst_letpath + '/README"')
data = self.getPanelSslData()
tmp_well_know = auth_to + '/.well-known'
if os.path.exists(tmp_well_know):
mw.execShell('rm -rf ' + tmp_well_know)
if is_already_apply:
return mw.returnJson(True, '重复申请!', data)
return mw.returnJson(True, '申请成功!', data)
def setPanelDomainApi(self): def setPanelDomainApi(self):
domain = request.form.get('domain', '') domain = request.form.get('domain', '')
@@ -459,8 +605,6 @@ class config_api:
return mw.returnJson(True, 'SSL已关闭,请使用http协议访问面板!') return mw.returnJson(True, 'SSL已关闭,请使用http协议访问面板!')
else: else:
try: try:
if not os.path.exists('ssl/input.ssl'):
mw.createSSL()
mw.writeFile(sslConf, 'True') mw.writeFile(sslConf, 'True')
keyPath = mw.getRunDir() + '/ssl/private.pem' keyPath = mw.getRunDir() + '/ssl/private.pem'
@@ -597,9 +741,83 @@ class config_api:
else: else:
return False, '' return False, ''
def setStatusCodeApi(self):
status_code = request.form.get('status_code', '').strip()
if re.match("^\d+$", status_code):
status_code = int(status_code)
if status_code != 0:
if status_code < 100 or status_code > 999:
return mw.returnJson(False, '状态码范围错误!')
else:
return mw.returnJson(False, '状态码范围错误!')
mw.writeFile('data/unauthorized_status.pl', str(status_code))
mw.writeLog('面板设置', '将未授权响应状态码设置为:{}'.format(status_code))
return mw.returnJson(True, '设置成功!')
def getNotifyApi(self):
# 获取
data = mw.getNotifyData(True)
return mw.returnData(True, 'ok', data)
def setNotifyApi(self):
tag = request.form.get('tag', '').strip()
data = request.form.get('data', '').strip()
cfg = mw.getNotifyData(False)
crypt_data = mw.enDoubleCrypt(tag, data)
if tag in cfg:
cfg[tag]['cfg'] = crypt_data
else:
t = {'cfg': crypt_data}
cfg[tag] = t
mw.writeNotify(cfg)
return mw.returnData(True, '设置成功')
def setNotifyTestApi(self):
# 异步通知验证
tag = request.form.get('tag', '').strip()
tag_data = request.form.get('data', '').strip()
if tag == 'tgbot':
t = json.loads(tag_data)
test_bool = mw.tgbotNotifyTest(t['app_token'], t['chat_id'])
if test_bool:
return mw.returnData(True, '验证成功')
return mw.returnData(False, '验证失败')
if tag == 'email':
t = json.loads(tag_data)
test_bool = mw.emailNotifyTest(t)
if test_bool:
return mw.returnData(True, '验证成功')
return mw.returnData(False, '验证失败')
return mw.returnData(False, '暂时未支持该验证')
def setNotifyEnableApi(self):
# 异步通知验证
tag = request.form.get('tag', '').strip()
tag_enable = request.form.get('enable', '').strip()
data = mw.getNotifyData(False)
op_enable = True
op_action = '开启'
if tag_enable != 'true':
op_enable = False
op_action = '关闭'
if tag in data:
data[tag]['enable'] = op_enable
mw.writeNotify(data)
return mw.returnData(True, op_action + '成功')
def getPanelTokenApi(self): def getPanelTokenApi(self):
api_file = self.__api_addr api_file = self.__api_addr
tmp = mw.readFile(api_file) tmp = mw.readFile(api_file)
if not os.path.exists(api_file): if not os.path.exists(api_file):
ready_data = {"open": False, "token": "", "limit_addr": []} ready_data = {"open": False, "token": "", "limit_addr": []}
@@ -618,7 +836,7 @@ class config_api:
token = mw.getRandomString(32) token = mw.getRandomString(32)
data['token'] = mw.md5(token) data['token'] = mw.md5(token)
data['token_crypt'] = mw.enCrypt( data['token_crypt'] = mw.enCrypt(
data['token'], token).decode('utf-8') data['token'], token)
mw.writeFile(api_file, json.dumps(data)) mw.writeFile(api_file, json.dumps(data))
data['token'] = "***********************************" data['token'] = "***********************************"
@@ -630,10 +848,6 @@ class config_api:
def setPanelTokenApi(self): def setPanelTokenApi(self):
op_type = request.form.get('op_type', '').strip() op_type = request.form.get('op_type', '').strip()
api_file = self.__api_addr
tmp = mw.readFile(api_file)
data = json.loads(tmp)
if op_type == '1': if op_type == '1':
token = mw.getRandomString(32) token = mw.getRandomString(32)
data['token'] = mw.md5(token) data['token'] = mw.md5(token)
@@ -643,7 +857,14 @@ class config_api:
mw.writeFile(api_file, json.dumps(data)) mw.writeFile(api_file, json.dumps(data))
return mw.returnJson(True, 'ok', token) return mw.returnJson(True, 'ok', token)
elif op_type == '2': api_file = self.__api_addr
if not os.path.exists(api_file):
return mw.returnJson(False, "先在API接口配置")
else:
tmp = mw.readFile(api_file)
data = json.loads(tmp)
if op_type == '2':
data['open'] = not data['open'] data['open'] = not data['open']
stats = {True: '开启', False: '关闭'} stats = {True: '开启', False: '关闭'}
if not 'token_crypt' in data: if not 'token_crypt' in data:
@@ -658,12 +879,38 @@ class config_api:
return mw.returnJson(not not data['open'], token) return mw.returnJson(not not data['open'], token)
elif op_type == '3': elif op_type == '3':
limit_addr = request.form.get('limit_addr', '').strip() limit_addr = request.form.get('limit_addr', '').strip()
data['limit_addr'] = limit_addr.split('\n') data['limit_addr'] = limit_addr.split('\n')
mw.writeLog('API配置', '变更IP限制为[%s]' % limit_addr) mw.writeLog('API配置', '变更IP限制为[%s]' % limit_addr)
mw.writeFile(api_file, json.dumps(data)) mw.writeFile(api_file, json.dumps(data))
return mw.returnJson(True, '保存成功!') return mw.returnJson(True, '保存成功!')
def renderUnauthorizedStatus(self, data):
cfg_unauth_status = 'data/unauthorized_status.pl'
if os.path.exists(cfg_unauth_status):
status_code = mw.readFile(cfg_unauth_status)
data['status_code'] = status_code
data['status_code_msg'] = status_code
if status_code == '0':
data['status_code_msg'] = "默认-安全入口错误提示"
elif status_code == '400':
data['status_code_msg'] = "400-客户端请求错误"
elif status_code == '401':
data['status_code_msg'] = "401-未授权访问"
elif status_code == '403':
data['status_code_msg'] = "403-拒绝访问"
elif status_code == '404':
data['status_code_msg'] = "404-页面不存在"
elif status_code == '408':
data['status_code_msg'] = "408-客户端超时"
elif status_code == '416':
data['status_code_msg'] = "416-无效的请求"
else:
data['status_code'] = '0'
data['status_code_msg'] = "默认-安全入口错误提示"
return data
def get(self): def get(self):
data = {} data = {}
@@ -716,6 +963,8 @@ class config_api:
else: else:
data['bind_domain'] = '' data['bind_domain'] = ''
data = self.renderUnauthorizedStatus(data)
api_token = self.__api_addr api_token = self.__api_addr
if os.path.exists(api_token): if os.path.exists(api_token):
bac = mw.readFile(api_token) bac = mw.readFile(api_token)
@@ -730,13 +979,43 @@ class config_api:
data['username'] = mw.M('users').where( data['username'] = mw.M('users').where(
"id=?", (1,)).getField('username') "id=?", (1,)).getField('username')
# databases hook 获取 data['hook_tag'] = request.args.get('tag', '')
database_file = 'data/hook_database.json'
if os.path.exists(database_file): # databases hook
df = mw.readFile(database_file) database_hook_file = 'data/hook_database.json'
if os.path.exists(database_hook_file):
df = mw.readFile(database_hook_file)
df = json.loads(df) df = json.loads(df)
data['hook_database'] = df data['hook_database'] = df
else: else:
data['hook_database'] = [] data['hook_database'] = []
# menu hook
menu_hook_file = 'data/hook_menu.json'
if os.path.exists(menu_hook_file):
df = mw.readFile(menu_hook_file)
df = json.loads(df)
data['hook_menu'] = df
else:
data['hook_menu'] = []
# global_static hook
global_static_hook_file = 'data/hook_global_static.json'
if os.path.exists(global_static_hook_file):
df = mw.readFile(global_static_hook_file)
df = json.loads(df)
data['hook_global_static'] = df
else:
data['hook_global_static'] = []
# notiy config
notify_data = mw.getNotifyData(True)
notify_tag_list = ['tgbot', 'email']
for tag in notify_tag_list:
new_tag = 'notify_' + tag + '_enable'
data[new_tag] = ''
if tag in notify_data and 'enable' in notify_data[tag]:
if notify_data[tag]['enable']:
data[new_tag] = 'checked'
return data return data
+2 -1
View File
@@ -117,6 +117,7 @@ class Sql():
sql = "SELECT " + self.__OPT_FIELD + " FROM " + self.__DB_TABLE + \ sql = "SELECT " + self.__OPT_FIELD + " FROM " + self.__DB_TABLE + \
self.__OPT_WHERE + self.__OPT_GROUP + self.__OPT_ORDER + self.__OPT_LIMIT self.__OPT_WHERE + self.__OPT_GROUP + self.__OPT_ORDER + self.__OPT_LIMIT
# print(sql) # print(sql)
# print(self.__OPT_PARAM)
result = self.__DB_CONN.execute(sql, self.__OPT_PARAM) result = self.__DB_CONN.execute(sql, self.__OPT_PARAM)
data = result.fetchall() data = result.fetchall()
# 构造字曲系列 # 构造字曲系列
@@ -347,7 +348,7 @@ class Sql():
except Exception as ex: except Exception as ex:
return "error: " + str(ex) return "error: " + str(ex)
def query(self, sql, param): def query(self, sql, param=()):
# 执行SQL语句返回数据集 # 执行SQL语句返回数据集
self.__getConn() self.__getConn()
try: try:
+132 -53
View File
@@ -247,8 +247,8 @@ class files_api:
os.remove(task_log) os.remove(task_log)
return mw.returnJson(True, '任务已删除!') return mw.returnJson(True, '任务已删除!')
# 上传文件
def uploadFileApi(self): def uploadFileApi(self):
# 上传文件
from werkzeug.utils import secure_filename from werkzeug.utils import secure_filename
from flask import request from flask import request
@@ -263,6 +263,8 @@ class files_api:
if os.path.exists(filename): if os.path.exists(filename):
s_path = filename s_path = filename
p_stat = os.stat(s_path) p_stat = os.stat(s_path)
# print(filename)
f.save(filename) f.save(filename)
os.chown(filename, p_stat.st_uid, p_stat.st_gid) os.chown(filename, p_stat.st_uid, p_stat.st_gid)
os.chmod(filename, p_stat.st_mode) os.chmod(filename, p_stat.st_mode)
@@ -271,6 +273,86 @@ class files_api:
mw.writeLog('文件管理', msg) mw.writeLog('文件管理', msg)
return mw.returnMsg(True, '上传成功!') return mw.returnMsg(True, '上传成功!')
# 设置文件和目录权限
def setMode(self, path):
s_path = os.path.dirname(path)
p_stat = os.stat(s_path)
os.chown(path, p_stat.st_uid, p_stat.st_gid)
os.chmod(path, p_stat.st_mode)
def uploadSegmentApi(self):
# 分段上传
path = request.form.get('path', '')
name = request.form.get('name', '')
size = request.form.get('size')
start = request.form.get('start')
dir_mode = request.form.get('dir_mode', '')
file_mode = request.form.get('file_mode', '')
if not mw.fileNameCheck(name):
return mw.returnJson(False, '文件名中不能包含特殊字符!')
if path == '/':
return mw.returnJson(False, '不能直接上传文件到系统根目录!')
if name.find('./') != -1 or path.find('./') != -1:
return mw.returnJson(False, '错误的参数')
if not os.path.exists(path):
os.makedirs(path, 493)
if not dir_mode != '' or not file_mode != '':
mw.setMode(path)
save_path = os.path.join(
path, name + '.' + str(int(size)) + '.upload.tmp')
d_size = 0
if os.path.exists(save_path):
d_size = os.path.getsize(save_path)
if d_size != int(start):
return str(d_size)
f = open(save_path, 'ab')
b64_data = request.form.get('b64_data', '0')
if b64_data == '1':
import base64
b64_data = base64.b64decode(args.b64_data)
f.write(b64_data)
else:
upload_files = request.files.getlist("blob")
for tmp_f in upload_files:
f.write(tmp_f.read())
f.close()
f_size = os.path.getsize(save_path)
if f_size != int(size):
return str(f_size)
new_name = os.path.join(path, name)
if os.path.exists(new_name):
if new_name.find('.user.ini') != -1:
mw.execShell("chattr -i " + new_name)
try:
os.remove(new_name)
except:
mw.execShell("rm -f %s" % new_name)
os.renames(save_path, new_name)
if dir_mode != '' and dir_mode != '':
mode_tmp1 = dir_mode.split(',')
mw.setMode(path, mode_tmp1[0])
mw.setOwn(path, mode_tmp1[1])
mode_tmp2 = file_mode.split(',')
mw.setMode(new_name, mode_tmp2[0])
mw.setOwn(new_name, mode_tmp2[1])
else:
self.setMode(new_name)
msg = mw.getInfo('上传文件[{1}] 到 [{2}]成功!', (new_name, path))
mw.writeLog('文件管理', msg)
return mw.returnMsg(True, '上传成功!')
def getRecycleBinApi(self): def getRecycleBinApi(self):
rPath = self.rPath rPath = self.rPath
if not os.path.exists(rPath): if not os.path.exists(rPath):
@@ -626,7 +708,7 @@ class files_api:
return not path in nDirs return not path in nDirs
def getDirSize(self, path): def getDirSize(self, path):
if mw.getOs() == 'darwin': if mw.isAppleSystem():
tmp = mw.execShell('du -sh ' + path) tmp = mw.execShell('du -sh ' + path)
else: else:
tmp = mw.execShell('du -sbh ' + path) tmp = mw.execShell('du -sbh ' + path)
@@ -677,56 +759,29 @@ class files_api:
if os.path.getsize(path) > 2097152: if os.path.getsize(path) > 2097152:
return mw.returnJson(False, '不能在线编辑大于2MB的文件!') return mw.returnJson(False, '不能在线编辑大于2MB的文件!')
if os.path.isdir(path):
return mw.returnJson(False, '这不是一个文件!')
fp = open(path, 'rb') fp = open(path, 'rb')
data = {} data = {}
data['status'] = True data['status'] = True
try: if fp:
if fp: srcBody = fp.read()
from chardet.universaldetector import UniversalDetector fp.close()
detector = UniversalDetector()
srcBody = b""
for line in fp.readlines():
detector.feed(line)
srcBody += line
detector.close()
char = detector.result
data['encoding'] = char['encoding']
if char['encoding'] == 'GB2312' or not char['encoding'] or char[
'encoding'] == 'TIS-620' or char['encoding'] == 'ISO-8859-9':
data['encoding'] = 'GBK'
if char['encoding'] == 'ascii' or char[
'encoding'] == 'ISO-8859-1':
data['encoding'] = 'utf-8'
if char['encoding'] == 'Big5':
data['encoding'] = 'BIG5'
if not data['encoding'] in ['GBK', 'utf-8', 'BIG5']:
data['encoding'] = 'utf-8'
encoding_list = ['utf-8', 'GBK', 'BIG5']
for el in encoding_list:
try: try:
if sys.version_info[0] == 2: data['encoding'] = el
data['data'] = srcBody.decode( data['data'] = srcBody.decode(data['encoding'])
data['encoding']).encode('utf-8', errors='ignore') break
else: except Exception as ex:
data['data'] = srcBody.decode(data['encoding']) if el == 'BIG5':
except: return mw.returnJson(False, '文件编码不被兼容,无法正确读取文件!' + str(ex))
data['encoding'] = char['encoding'] else:
if sys.version_info[0] == 2: return mw.returnJson(False, '文件未正常打开!')
data['data'] = srcBody.decode(
data['encoding']).encode('utf-8', errors='ignore')
else:
data['data'] = srcBody.decode(data['encoding'])
return mw.returnJson(True, 'OK', data)
else:
if sys.version_info[0] == 2:
data['data'] = srcBody.decode('utf-8').encode('utf-8')
else:
data['data'] = srcBody.decode('utf-8')
data['encoding'] = 'utf-8'
return mw.returnJson(True, 'OK', data) return mw.returnJson(True, 'OK', data)
except Exception as ex:
return mw.returnJson(False, '文件编码不被兼容,无法正确读取文件!' + str(ex))
def saveBody(self, path, data, encoding='utf-8'): def saveBody(self, path, data, encoding='utf-8'):
if not os.path.exists(path): if not os.path.exists(path):
@@ -734,13 +789,10 @@ class files_api:
try: try:
if encoding == 'ascii': if encoding == 'ascii':
encoding = 'utf-8' encoding = 'utf-8'
if sys.version_info[0] == 2:
data = data.encode(encoding, errors='ignore') data = data.encode(
fp = open(path, 'w+') encoding, errors='ignore').decode(encoding)
else: fp = open(path, 'w+', encoding=encoding)
data = data.encode(
encoding, errors='ignore').decode(encoding)
fp = open(path, 'w+', encoding=encoding)
fp.write(data) fp.write(data)
fp.close() fp.close()
@@ -935,6 +987,33 @@ class files_api:
return mw.getJson(data) return mw.getJson(data)
def execShellApi(self):
# 执行SHELL命令
shell = request.form.get('shell', '').strip()
path = request.form.get('path', '').strip()
disabled = ['vi', 'vim', 'top', 'passwd', 'su']
tmp = shell.split(' ')
if tmp[0] in disabled:
return mw.returnJson(False, '禁止执行[{}]'.format(tmp[0]))
shellStr = '''#!/bin/bash
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH
cd %s
%s
''' % (path, shell)
mw.writeFile('/tmp/panelShell.sh', shellStr)
mw.execShell(
'nohup bash /tmp/panelShell.sh > /tmp/panelShell.pl 2>&1 &')
return mw.returnJson(True, 'ok')
def getExecShellMsgApi(self):
# 取SHELL执行结果
fileName = '/tmp/panelShell.pl'
if not os.path.exists(fileName):
return ''
status = not mw.processExists('bash', None, '/tmp/panelShell.sh')
return mw.returnJson(status, mw.getNumLines(fileName, 200))
def __get_stats(self, filename, path=None): def __get_stats(self, filename, path=None):
filename = filename.replace('//', '/') filename = filename.replace('//', '/')
try: try:
+85 -63
View File
@@ -28,15 +28,19 @@ from flask import request
class firewall_api: class firewall_api:
__isFirewalld = False __isFirewalld = False
__isIptables = False
__isUfw = False __isUfw = False
__isMac = False __isMac = False
def __init__(self): def __init__(self):
iptables_file = mw.systemdCfgDir() + '/iptables.service'
if os.path.exists('/usr/sbin/firewalld'): if os.path.exists('/usr/sbin/firewalld'):
self.__isFirewalld = True self.__isFirewalld = True
if os.path.exists('/usr/sbin/ufw'): elif os.path.exists('/usr/sbin/ufw'):
self.__isUfw = True self.__isUfw = True
if mw.isAppleSystem(): elif os.path.exists(iptables_file):
self.__isIptables = True
elif mw.isAppleSystem():
self.__isMac = True self.__isMac = True
##### ----- start ----- ### ##### ----- start ----- ###
@@ -55,13 +59,15 @@ class firewall_api:
if self.__isUfw: if self.__isUfw:
mw.execShell('ufw deny from ' + address + ' to any') mw.execShell('ufw deny from ' + address + ' to any')
else: else:
if self.__isFirewalld: if self.__isIptables:
cmd = 'iptables -I INPUT -s ' + address + ' -j DROP'
mw.execShell(cmd)
elif self.__isFirewalld:
cmd = 'firewall-cmd --permanent --add-rich-rule=\'rule family=ipv4 source address="' + \ cmd = 'firewall-cmd --permanent --add-rich-rule=\'rule family=ipv4 source address="' + \
address + '" drop\'' address + '" drop\''
mw.execShell(cmd) mw.execShell(cmd)
else: else:
cmd = 'iptables -I INPUT -s ' + address + ' -j DROP' pass
mw.execShell(cmd)
msg = mw.getInfo('屏蔽IP[{1}]成功!', (address,)) msg = mw.getInfo('屏蔽IP[{1}]成功!', (address,))
mw.writeLog("防火墙管理", msg) mw.writeLog("防火墙管理", msg)
@@ -117,15 +123,14 @@ class firewall_api:
address = port address = port
if self.__isUfw: if self.__isUfw:
mw.execShell('ufw delete deny from ' + address + ' to any') mw.execShell('ufw delete deny from ' + address + ' to any')
elif self.__isFirewalld:
mw.execShell(
'firewall-cmd --permanent --remove-rich-rule=\'rule family=ipv4 source address="' + address + '" drop\'')
elif self.__isIptables:
cmd = 'iptables -D INPUT -s ' + address + ' -j DROP'
mw.execShell(cmd)
else: else:
if self.__isFirewalld: pass
mw.execShell(
'firewall-cmd --permanent --remove-rich-rule=\'rule family=ipv4 source address="' + address + '" drop\'')
elif self.__isMac:
pass
else:
cmd = 'iptables -D INPUT -s ' + address + ' -j DROP'
mw.execShell(cmd)
msg = mw.getInfo('解除IP[{1}]的屏蔽!', (address,)) msg = mw.getInfo('解除IP[{1}]的屏蔽!', (address,))
mw.writeLog("防火墙管理", msg) mw.writeLog("防火墙管理", msg)
@@ -144,15 +149,17 @@ class firewall_api:
return mw.returnJson(False, '失败,不能删除当前面板端口!') return mw.returnJson(False, '失败,不能删除当前面板端口!')
if self.__isUfw: if self.__isUfw:
mw.execShell('ufw delete allow ' + port + '/tcp') mw.execShell('ufw delete allow ' + port + '/tcp')
elif self.__isFirewalld:
port = port.replace(':', '-')
mw.execShell(
'firewall-cmd --permanent --zone=public --remove-port=' + port + '/tcp')
mw.execShell(
'firewall-cmd --permanent --zone=public --remove-port=' + port + '/udp')
elif self.__isIptables:
mw.execShell(
'iptables -D INPUT -p tcp -m state --state NEW -m tcp --dport ' + port + ' -j ACCEPT')
else: else:
if self.__isFirewalld: pass
mw.execShell(
'firewall-cmd --permanent --zone=public --remove-port=' + port + '/tcp')
mw.execShell(
'firewall-cmd --permanent --zone=public --remove-port=' + port + '/udp')
else:
mw.execShell(
'iptables -D INPUT -p tcp -m state --state NEW -m tcp --dport ' + port + ' -j ACCEPT')
msg = mw.getInfo('删除防火墙放行端口[{1}]成功!', (port,)) msg = mw.getInfo('删除防火墙放行端口[{1}]成功!', (port,))
mw.writeLog("防火墙管理", msg) mw.writeLog("防火墙管理", msg)
mw.M('firewall').where("id=?", (sid,)).delete() mw.M('firewall').where("id=?", (sid,)).delete()
@@ -249,24 +256,15 @@ class firewall_api:
conf = re.sub(rep, "Port " + port + "\n", conf) conf = re.sub(rep, "Port " + port + "\n", conf)
mw.writeFile(file, conf) mw.writeFile(file, conf)
if self.__isFirewalld: self.addAcceptPortArgs(port, 'SSH端口修改', 'port')
mw.execShell('setenforce 0') if self.__isUfw:
mw.execShell(
'sed -i "s#SELINUX=enforcing#SELINUX=disabled#" /etc/selinux/config')
mw.execShell("systemctl restart sshd.service")
elif self.__isUfw:
mw.execShell('ufw allow ' + port + '/tcp')
mw.execShell("service ssh restart") mw.execShell("service ssh restart")
else: elif self.__isIptables:
mw.execShell(
'iptables -I INPUT -p tcp -m state --state NEW -m tcp --dport ' + port + ' -j ACCEPT')
mw.execShell("/etc/init.d/sshd restart") mw.execShell("/etc/init.d/sshd restart")
elif self.__isFirewalld:
self.firewallReload() mw.execShell("systemctl restart sshd.service")
# mw.M('firewall').where( else:
# "ps=?", ('SSH远程管理服务',)).setField('port', port) return mw.returnJson(False, '修改失败!')
msg = "改SSH端口为[{}]成功!".format(port)
mw.writeLog("防火墙管理", msg)
return mw.returnJson(True, '修改成功!') return mw.returnJson(True, '修改成功!')
def setSshStatusApi(self): def setSshStatusApi(self):
@@ -342,29 +340,52 @@ class firewall_api:
status = request.form.get('status', '1') status = request.form.get('status', '1')
return mw.getJson(self.setFw(status)) return mw.getJson(self.setFw(status))
def setFwIptables(self, status):
# iptables特殊处理
if status == '1':
mw.execShell('service iptables save')
mw.execShell('service iptables stop')
else:
# 重新导入数据
_list = mw.M('firewall').field('id,port,ps,addtime').limit(
'0,1000').order('id desc').select()
mw.execShell('iptables -P INPUT DROP')
mw.execShell('iptables -P OUTPUT ACCEPT')
for x in _list:
port = x['port']
if mw.isIpAddr(port):
cmd = 'iptables -I INPUT -s ' + port + ' -j DROP'
mw.execShell(cmd)
else:
self.addAcceptPort(port)
mw.execShell('service iptables save')
mw.execShell('service iptables start')
def setFw(self, status): def setFw(self, status):
if self.__isIptables:
self.setFwIptables(status)
return mw.returnData(True, '设置成功!')
if status == '1': if status == '1':
if self.__isUfw: if self.__isUfw:
mw.execShell('/usr/sbin/ufw disable') mw.execShell('/usr/sbin/ufw disable')
if self.__isFirewalld:
elif self.__isFirewalld:
mw.execShell('systemctl stop firewalld.service') mw.execShell('systemctl stop firewalld.service')
mw.execShell('systemctl disable firewalld.service') mw.execShell('systemctl disable firewalld.service')
elif self.__isMac:
pass
else: else:
mw.execShell('service iptables save') pass
mw.execShell('service iptables stop')
else: else:
if self.__isUfw: if self.__isUfw:
mw.execShell("echo 'y'| ufw enable") mw.execShell("echo 'y'| ufw enable")
if self.__isFirewalld: elif self.__isFirewalld:
mw.execShell('systemctl start firewalld.service') mw.execShell('systemctl start firewalld.service')
mw.execShell('systemctl enable firewalld.service') mw.execShell('systemctl enable firewalld.service')
elif self.__isMac:
pass
else: else:
mw.execShell('service iptables save') pass
mw.execShell('service iptables restart')
return mw.returnData(True, '设置成功!') return mw.returnData(True, '设置成功!')
@@ -422,23 +443,24 @@ class firewall_api:
port = port.replace(':', '-') port = port.replace(':', '-')
cmd = 'firewall-cmd --permanent --zone=public --add-port=' + port + '/tcp' cmd = 'firewall-cmd --permanent --zone=public --add-port=' + port + '/tcp'
mw.execShell(cmd) mw.execShell(cmd)
elif self.__isMac: elif self.__isIptables:
pass
else:
cmd = 'iptables -I INPUT -p tcp -m state --state NEW -m tcp --dport ' + port + ' -j ACCEPT' cmd = 'iptables -I INPUT -p tcp -m state --state NEW -m tcp --dport ' + port + ' -j ACCEPT'
mw.execShell(cmd) mw.execShell(cmd)
else:
pass
return True
def firewallReload(self): def firewallReload(self):
if self.__isUfw: if self.__isUfw:
mw.execShell('/usr/sbin/ufw reload') mw.execShell('/usr/sbin/ufw reload')
return return
if self.__isFirewalld: elif self.__isIptables:
mw.execShell('firewall-cmd --reload')
elif self.__isMac:
pass
else:
mw.execShell('service iptables save') mw.execShell('service iptables save')
mw.execShell('service iptables restart') mw.execShell('service iptables restart')
elif self.__isFirewalld:
mw.execShell('firewall-cmd --reload')
else:
pass
def getFwStatus(self): def getFwStatus(self):
if self.__isUfw: if self.__isUfw:
@@ -447,17 +469,17 @@ class firewall_api:
if data[0].strip() == 'inactive': if data[0].strip() == 'inactive':
return False return False
return True return True
if self.__isFirewalld: elif self.__isIptables:
cmd = "systemctl status iptables | grep 'inactive'"
data = mw.execShell(cmd)
if data[0] != '':
return False
return True
elif self.__isFirewalld:
cmd = "ps -ef|grep firewalld |grep -v grep | awk '{print $2}'" cmd = "ps -ef|grep firewalld |grep -v grep | awk '{print $2}'"
data = mw.execShell(cmd) data = mw.execShell(cmd)
if data[0] == '': if data[0] == '':
return False return False
return True return True
elif self.__isMac:
return False
else: else:
cmd = "ps -ef|grep iptables |grep -v grep | awk '{print $2}'" return False
data = mw.execShell(cmd)
if data[0] == '':
return False
return True
+650 -15
View File
@@ -22,10 +22,14 @@ import hashlib
import shlex import shlex
import datetime import datetime
import subprocess import subprocess
import glob
import base64
import re import re
import db
from random import Random from random import Random
import db
def execShell(cmdstring, cwd=None, timeout=None, shell=True): def execShell(cmdstring, cwd=None, timeout=None, shell=True):
@@ -80,6 +84,10 @@ def getPanelDataDir():
return getRunDir() + '/data' return getRunDir() + '/data'
def getPanelTmp():
return getRunDir() + '/tmp'
def getServerDir(): def getServerDir():
return getRootDir() + '/server' return getRootDir() + '/server'
@@ -123,6 +131,23 @@ def getAcmeDir():
return acme return acme
def getAcmeDomainDir(domain):
acme_dir = getAcmeDir()
acme_domain = acme_dir + '/' + domain
acme_domain_ecc = acme_domain + '_ecc'
if os.path.exists(acme_domain_ecc):
acme_domain = acme_domain_ecc
return acme_domain
def fileNameCheck(filename):
f_strs = [';', '&', '<', '>']
for fs in f_strs:
if filename.find(fs) != -1:
return False
return True
def triggerTask(): def triggerTask():
isTask = getRunDir() + '/tmp/panelTask.pl' isTask = getRunDir() + '/tmp/panelTask.pl'
writeFile(isTask, 'True') writeFile(isTask, 'True')
@@ -219,6 +244,23 @@ def isInstalledWeb():
return False return False
def isIpAddr(ip):
check_ip = re.compile(
'^(1\d{2}|2[0-4]\d|25[0-5]|[1-9]\d|[1-9])\.(1\d{2}|2[0-4]\d|25[0-5]|[1-9]\d|\d)\.(1\d{2}|2[0-4]\d|25[0-5]|[1-9]\d|\d)\.(1\d{2}|2[0-4]\d|25[0-5]|[1-9]\d|\d)$')
if check_ip.match(ip):
return True
else:
return False
def getWebStatus():
pid = getServerDir() + '/openresty/nginx/logs/nginx.pid'
if os.path.exists(pid):
return True
return False
# ------------------------------ openresty start -----------------------------
def restartWeb(): def restartWeb():
return opWeb("reload") return opWeb("reload")
@@ -243,6 +285,63 @@ def opWeb(method):
return False return False
def opLuaMake(cmd_name):
path = getServerDir() + '/web_conf/nginx/lua/lua.conf'
root_dir = getServerDir() + '/web_conf/nginx/lua/' + cmd_name
dst_path = getServerDir() + '/web_conf/nginx/lua/' + cmd_name + '.lua'
def_path = getServerDir() + '/web_conf/nginx/lua/empty.lua'
if not os.path.exists(root_dir):
execShell('mkdir -p ' + root_dir)
files = []
for fl in os.listdir(root_dir):
suffix = getFileSuffix(fl)
if suffix != 'lua':
continue
flpath = os.path.join(root_dir, fl)
files.append(flpath)
if len(files) > 0:
def_path = dst_path
content = ''
for f in files:
t = readFile(f)
f_base = os.path.basename(f)
content += '-- ' + '*' * 20 + ' ' + f_base + ' start ' + '*' * 20 + "\n"
content += t
content += "\n" + '-- ' + '*' * 20 + ' ' + f_base + ' end ' + '*' * 20 + "\n"
writeFile(dst_path, content)
else:
if os.path.exists(dst_path):
os.remove(dst_path)
conf = readFile(path)
conf = re.sub(cmd_name + ' (.*);',
cmd_name + " " + def_path + ";", conf)
writeFile(path, conf)
def opLuaInitFile():
opLuaMake('init_by_lua_file')
def opLuaInitWorkerFile():
opLuaMake('init_worker_by_lua_file')
def opLuaInitAccessFile():
opLuaMake('access_by_lua_file')
def opLuaMakeAll():
opLuaInitFile()
opLuaInitWorkerFile()
opLuaInitAccessFile()
# ------------------------------ openresty end -----------------------------
def restartMw(): def restartMw():
import system_api import system_api
system_api.system_api().restartMw() system_api.system_api().restartMw()
@@ -445,16 +544,67 @@ def getDate():
return time.strftime('%Y-%m-%d %X', time.localtime()) return time.strftime('%Y-%m-%d %X', time.localtime())
def getDateFromNow(tf_format="%Y-%m-%d %H:%M:%S", time_zone="Asia/Shanghai"):
# 取格式时间
import time
os.environ['TZ'] = time_zone
time.tzset()
return time.strftime(tf_format, time.localtime())
def getDataFromInt(val):
time_format = '%Y-%m-%d %H:%M:%S'
time_str = time.localtime(val)
return time.strftime(time_format, time_str)
def writeLog(stype, msg, args=()): def writeLog(stype, msg, args=()):
# 写日志 # 写日志
uid = 1
try:
from flask import session
if 'uid' in session:
uid = session['uid']
except Exception as e:
pass
# writeFileLog(getTracebackInfo())
return writeDbLog(stype, msg, args, uid)
def writeFileLog(msg, path=None, limit_size=50 * 1024 * 1024, save_limit=3):
log_file = getServerDir() + '/mdserver-web/logs/debug.log'
if path != None:
log_file = path
if os.path.exists(log_file):
size = os.path.getsize(log_file)
if size > limit_size:
log_file_rename = log_file + "_" + \
time.strftime("%Y-%m-%d_%H%M%S") + '.log'
os.rename(log_file, log_file_rename)
logs = sorted(glob.glob(log_file + "_*"))
count = len(logs)
save_limit = count - save_limit
for i in range(count):
if i > save_limit:
break
os.remove(logs[i])
# print('|---多余日志[' + logs[i] + ']已删除!')
f = open(log_file, 'ab+')
msg += "\n"
if __name__ == '__main__':
print(msg)
f.write(msg.encode('utf-8'))
f.close()
return True
def writeDbLog(stype, msg, args=(), uid=1):
try: try:
import time import time
import db import db
import json import json
from flask import session
uid = 1
if 'uid' in session:
uid = session['uid']
sql = db.Sql() sql = db.Sql()
mdate = time.strftime('%Y-%m-%d %X', time.localtime()) mdate = time.strftime('%Y-%m-%d %X', time.localtime())
wmsg = getInfo(msg, args) wmsg = getInfo(msg, args)
@@ -490,6 +640,18 @@ def backFile(file, act=None):
execShell("cp -p {0} {1}".format(file, file + file_type)) execShell("cp -p {0} {1}".format(file, file + file_type))
def removeBackFile(file, act=None):
"""
@name 删除备份配置文件
@param file 需要删除备份文件
@param act 如果存在,则还原默认配置
"""
file_type = "_bak"
if act:
file_type = "_def"
execShell("rm -rf {0}".format(file + file_type))
def restoreFile(file, act=None): def restoreFile(file, act=None):
""" """
@name 还原配置文件 @name 还原配置文件
@@ -544,31 +706,205 @@ def dePunycode(domain):
def enCrypt(key, strings): def enCrypt(key, strings):
# 加密字符串 # 加密字符串
try: try:
import base64
_key = key.encode('utf-8')
_key = base64.urlsafe_b64encode(_key)
if type(strings) != bytes: if type(strings) != bytes:
strings = strings.encode('utf-8') strings = strings.encode('utf-8')
import cryptography
from cryptography.fernet import Fernet from cryptography.fernet import Fernet
f = Fernet(key) f = Fernet(_key)
result = f.encrypt(strings) result = f.encrypt(strings)
return result.decode('utf-8') return result.decode('utf-8')
except: except:
# print(get_error_info()) writeFileLog(getTracebackInfo())
return strings return strings
def deCrypt(key, strings): def deCrypt(key, strings):
# 解密字符串 # 解密字符串
try: try:
import base64
_key = key.encode('utf-8')
_key = base64.urlsafe_b64encode(_key)
if type(strings) != bytes: if type(strings) != bytes:
strings = strings.decode('utf-8') strings = strings.encode('utf-8')
from cryptography.fernet import Fernet from cryptography.fernet import Fernet
f = Fernet(key) f = Fernet(_key)
result = f.decrypt(strings).decode('utf-8') result = f.decrypt(strings).decode('utf-8')
return result return result
except: except:
# print(get_error_info()) writeFileLog(getTracebackInfo())
return strings return strings
def enDoubleCrypt(key, strings):
# 加密字符串
try:
import base64
_key = md5(key).encode('utf-8')
_key = base64.urlsafe_b64encode(_key)
if type(strings) != bytes:
strings = strings.encode('utf-8')
import cryptography
from cryptography.fernet import Fernet
f = Fernet(_key)
result = f.encrypt(strings)
return result.decode('utf-8')
except:
writeFileLog(getTracebackInfo())
return strings
def deDoubleCrypt(key, strings):
# 解密字符串
try:
import base64
_key = md5(key).encode('utf-8')
_key = base64.urlsafe_b64encode(_key)
if type(strings) != bytes:
strings = strings.encode('utf-8')
from cryptography.fernet import Fernet
f = Fernet(_key)
result = f.decrypt(strings).decode('utf-8')
return result
except:
writeFileLog(getTracebackInfo())
return strings
def aesEncrypt(data, key='ABCDEFGHIJKLMNOP', vi='0102030405060708'):
# aes加密
# @param data 被加密的数据
# @param key 加解密密匙 16位
# @param vi 16位
from cryptography.hazmat.primitives import padding
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
from cryptography.hazmat.backends import default_backend
if not isinstance(data, bytes):
data = data.encode()
# AES_CBC_KEY = os.urandom(32)
# AES_CBC_IV = os.urandom(16)
AES_CBC_KEY = key.encode()
AES_CBC_IV = vi.encode()
# print("AES_CBC_KEY:", AES_CBC_KEY)
# print("AES_CBC_IV:", AES_CBC_IV)
padder = padding.PKCS7(algorithms.AES.block_size).padder()
padded_data = padder.update(data) + padder.finalize()
cipher = Cipher(algorithms.AES(AES_CBC_KEY),
modes.CBC(AES_CBC_IV),
backend=default_backend())
encryptor = cipher.encryptor()
edata = encryptor.update(padded_data)
# print(edata)
# print(str(edata))
# print(edata.decode())
return edata
def aesDecrypt(data, key='ABCDEFGHIJKLMNOP', vi='0102030405060708'):
# aes加密
# @param data 被解密的数据
# @param key 加解密密匙 16位
# @param vi 16位
from cryptography.hazmat.primitives import padding
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import padding
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
from cryptography.hazmat.backends import default_backend
if not isinstance(data, bytes):
data = data.encode()
AES_CBC_KEY = key.encode()
AES_CBC_IV = vi.encode()
cipher = Cipher(algorithms.AES(AES_CBC_KEY),
modes.CBC(AES_CBC_IV),
backend=default_backend())
decryptor = cipher.decryptor()
ddata = decryptor.update(data)
unpadder = padding.PKCS7(algorithms.AES.block_size).unpadder()
data = unpadder.update(ddata)
try:
uppadded_data = data + unpadder.finalize()
except ValueError:
raise Exception('无效的加密信息!')
return uppadded_data
def aesEncrypt_Crypto(data, key, vi):
# 该方法保留,暂时不使用
# aes加密
# @param data 被加密的数据
# @param key 加解密密匙 16位
# @param vi 16位
from Crypto.Cipher import AES
cryptor = AES.new(key.encode('utf8'), AES.MODE_CBC, vi.encode('utf8'))
# 判断是否含有中文
zhmodel = re.compile(u'[\u4e00-\u9fff]')
match = zhmodel.search(data)
if match == None:
# 无中文时
add = 16 - len(data) % 16
pad = lambda s: s + add * chr(add)
data = pad(data)
enctext = cryptor.encrypt(data.encode('utf8'))
else:
# 含有中文时
data = data.encode()
add = 16 - len(data) % 16
data = data + add * (chr(add)).encode()
enctext = cryptor.encrypt(data)
encodestrs = base64.b64encode(enctext).decode('utf8')
return encodestrs
def aesDecrypt_Crypto(data, key, vi):
# 该方法保留,暂时不使用
# aes加密
# @param data 被加密的数据
# @param key 加解密密匙 16位
# @param vi 16位
from crypto.Cipher import AES
data = data.encode('utf8')
encodebytes = base64.urlsafe_b64decode(data)
cipher = AES.new(key.encode('utf8'), AES.MODE_CBC, vi.encode('utf8'))
text_decrypted = cipher.decrypt(encodebytes)
# 判断是否含有中文
zhmodel = re.compile(u'[\u4e00-\u9fff]')
match = zhmodel.search(text_decrypted)
if match == False:
# 无中文时补位
unpad = lambda s: s[0:-s[-1]]
text_decrypted = unpad(text_decrypted)
text_decrypted = text_decrypted.decode('utf8').rstrip() # 去掉补位的右侧空格
return text_decrypted
def buildSoftLink(src, dst, force=False): def buildSoftLink(src, dst, force=False):
''' '''
建立软连接 建立软连接
@@ -841,7 +1177,7 @@ def getLocalIp():
try: try:
ipaddress = readFile(filename) ipaddress = readFile(filename)
if not ipaddress or ipaddress == '127.0.0.1': if not ipaddress or ipaddress == '127.0.0.1':
cmd = "curl -4 -sS --connect-timeout 5 -m 60 https://v6r.ipip.net/?format=text" cmd = "curl --insecure -4 -sS --connect-timeout 5 -m 60 https://v6r.ipip.net/?format=text"
ip = execShell(cmd) ip = execShell(cmd)
result = ip[0].strip() result = ip[0].strip()
if result == '': if result == '':
@@ -850,7 +1186,7 @@ def getLocalIp():
return result return result
return ipaddress return ipaddress
except Exception as e: except Exception as e:
cmd = "curl -6 -sS --connect-timeout 5 -m 60 https://v6r.ipip.net/?format=text" cmd = "curl --insecure -6 -sS --connect-timeout 5 -m 60 https://v6r.ipip.net/?format=text"
ip = execShell(cmd) ip = execShell(cmd)
result = ip[0].strip() result = ip[0].strip()
if result == '': if result == '':
@@ -975,6 +1311,15 @@ def setOwn(filename, user, group=None):
return True return True
def setMode(filename, mode):
# 设置文件权限
if not os.path.exists(filename):
return False
mode = int(str(mode), 8)
os.chmod(filename, mode)
return True
def checkPort(port): def checkPort(port):
# 检查端口是否合法 # 检查端口是否合法
ports = ['21', '443', '888'] ports = ['21', '443', '888']
@@ -1060,7 +1405,7 @@ def makeConf():
file = getRunDir() + '/data/json/config.json' file = getRunDir() + '/data/json/config.json'
if not os.path.exists(file): if not os.path.exists(file):
c = {} c = {}
c['title'] = '大圣面板' c['title'] = '金蝉面板'
c['home'] = 'http://github/midoks/mdserver-web' c['home'] = 'http://github/midoks/mdserver-web'
c['recycle_bin'] = True c['recycle_bin'] = True
c['template'] = 'default' c['template'] = 'default'
@@ -1246,6 +1591,10 @@ def toSize(size):
return str(round(size, 2)) + ' ' + b return str(round(size, 2)) + ' ' + b
def getPathSuffix(path):
return os.path.splitext(path)[-1]
def getMacAddress(): def getMacAddress():
# 获取mac # 获取mac
import uuid import uuid
@@ -1356,7 +1705,7 @@ def getCertName(certPath):
result['notAfter'], "%Y-%m-%d")) - time.time()) / 86400) result['notAfter'], "%Y-%m-%d")) - time.time()) / 86400)
return result return result
except Exception as e: except Exception as e:
# print(getTracebackInfo()) writeFileLog(getTracebackInfo())
return None return None
@@ -1454,8 +1803,290 @@ def getMyORMDb():
o = ormDb.ORM() o = ormDb.ORM()
return o return o
##################### notify start #########################################
def initNotifyConfig():
p = getNotifyPath()
if not os.path.exists(p):
writeFile(p, '{}')
return True
def getNotifyPath():
path = 'data/notify.json'
return path
def getNotifyData(is_parse=False):
initNotifyConfig()
notify_file = getNotifyPath()
notify_data = readFile(notify_file)
data = json.loads(notify_data)
if is_parse:
tag_list = ['tgbot', 'email']
for t in tag_list:
if t in data and 'cfg' in data[t]:
data[t]['data'] = json.loads(deDoubleCrypt(t, data[t]['cfg']))
return data
def writeNotify(data):
p = getNotifyPath()
return writeFile(p, json.dumps(data))
def tgbotNotifyChatID():
data = getNotifyData(True)
if 'tgbot' in data and 'enable' in data['tgbot']:
if data['tgbot']['enable']:
t = data['tgbot']['data']
return t['chat_id']
return ''
def tgbotNotifyObject():
data = getNotifyData(True)
if 'tgbot' in data and 'enable' in data['tgbot']:
if data['tgbot']['enable']:
t = data['tgbot']['data']
import telebot
bot = telebot.TeleBot(app_token)
return True, bot
return False, None
def tgbotNotifyMessage(app_token, chat_id, msg):
import telebot
bot = telebot.TeleBot(app_token)
try:
data = bot.send_message(chat_id, msg)
return True
except Exception as e:
writeFileLog(str(e))
return False
def tgbotNotifyHttpPost(app_token, chat_id, msg):
try:
url = 'https://api.telegram.org/bot' + app_token + '/sendMessage'
post_data = {
'chat_id': chat_id,
'text': msg,
}
rdata = httpPost(url, post_data)
return True
except Exception as e:
writeFileLog(str(e))
return False
def tgbotNotifyTest(app_token, chat_id):
msg = 'MW-通知验证测试OK'
return tgbotNotifyHttpPost(app_token, chat_id, msg)
def emailNotifyMessage(data):
'''
邮件通知
'''
sys.path.append(os.getcwd() + "/class/plugin")
import memail
try:
if data['smtp_ssl'] == 'ssl':
memail.sendSSL(data['smtp_host'], data['smtp_port'],
data['username'], data['password'],
data['to_mail_addr'], data['subject'], data['content'])
else:
memail.send(data['smtp_host'], data['smtp_port'],
data['username'], data['password'],
data['to_mail_addr'], data['subject'], data['content'])
return True
except Exception as e:
print(getTracebackInfo())
return False
def emailNotifyTest(data):
# print(data)
data['subject'] = 'MW通知测试'
data['content'] = data['mail_test']
return emailNotifyMessage(data)
def notifyMessageTry(msg, stype='common', trigger_time=300, is_write_log=True):
lock_file = getPanelTmp() + '/notify_lock.json'
if not os.path.exists(lock_file):
writeFile(lock_file, '{}')
lock_data = json.loads(readFile(lock_file))
if stype in lock_data:
diff_time = time.time() - lock_data[stype]['do_time']
if diff_time >= trigger_time:
lock_data[stype]['do_time'] = time.time()
else:
return False
else:
lock_data[stype] = {'do_time': time.time()}
writeFile(lock_file, json.dumps(lock_data))
if is_write_log:
writeLog("通知管理[" + stype + "]", msg)
data = getNotifyData(True)
# tag_list = ['tgbot', 'email']
# tagbot
do_notify = False
if 'tgbot' in data and 'enable' in data['tgbot']:
if data['tgbot']['enable']:
t = data['tgbot']['data']
i = sys.version_info
# telebot 在python小于3.7无法使用
if i[0] < 3 or i[1] < 7:
do_notify = tgbotNotifyHttpPost(
t['app_token'], t['chat_id'], msg)
else:
do_notify = tgbotNotifyMessage(
t['app_token'], t['chat_id'], msg)
if 'email' in data and 'enable' in data['email']:
if data['email']['enable']:
t = data['email']['data']
t['subject'] = 'MW通知'
t['content'] = msg
do_notify = emailNotifyMessage(t)
return do_notify
def notifyMessage(msg, stype='common', trigger_time=300, is_write_log=True):
try:
return notifyMessageTry(msg, stype, trigger_time, is_write_log)
except Exception as e:
writeFileLog(getTracebackInfo())
return False
##################### notify end #########################################
##################### ssh start #########################################
def getSshDir():
if isAppleSystem():
user = execShell("who | sed -n '2, 1p' |awk '{print $1}'")[0].strip()
return '/Users/' + user + '/.ssh'
return '/root/.ssh'
def processExists(pname, exe=None, cmdline=None):
# 进程是否存在
try:
import psutil
pids = psutil.pids()
for pid in pids:
try:
p = psutil.Process(pid)
if p.name() == pname:
if not exe and not cmdline:
return True
else:
if exe:
if p.exe() == exe:
return True
if cmdline:
if cmdline in p.cmdline():
return True
except:
pass
return False
except:
return True
def createRsa():
# ssh-keygen -t rsa -P "" -C "midoks@163.com"
ssh_dir = getSshDir()
# mw.execShell("rm -f /root/.ssh/*")
if not os.path.exists(ssh_dir + '/authorized_keys'):
execShell('touch ' + ssh_dir + '/authorized_keys')
if not os.path.exists(ssh_dir + '/id_rsa.pub') and os.path.exists(ssh_dir + '/id_rsa'):
execShell('echo y | ssh-keygen -q -t rsa -P "" -f ' +
ssh_dir + '/id_rsa')
else:
execShell('ssh-keygen -q -t rsa -P "" -f ' + ssh_dir + '/id_rsa')
execShell('cat ' + ssh_dir + '/id_rsa.pub >> ' +
ssh_dir + '/authorized_keys')
execShell('chmod 600 ' + ssh_dir + '/authorized_keys')
def createSshInfo():
ssh_dir = getSshDir()
if not os.path.exists(ssh_dir + '/id_rsa') or not os.path.exists(ssh_dir + '/id_rsa.pub'):
createRsa()
# 检查是否写入authorized_keys
data = execShell("cat " + ssh_dir + "/id_rsa.pub | awk '{print $3}'")
if data[0] != "":
cmd = "cat " + ssh_dir + "/authorized_keys | grep " + data[0]
ak_data = execShell(cmd)
if ak_data[0] == "":
cmd = 'cat ' + ssh_dir + '/id_rsa.pub >> ' + ssh_dir + '/authorized_keys'
execShell(cmd)
execShell('chmod 600 ' + ssh_dir + '/authorized_keys')
def connectSsh():
import paramiko
ssh = paramiko.SSHClient()
createSshInfo()
ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
port = 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(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 clearSsh():
# 服务器IP
ip = getHostAddr()
sh = '''
#!/bin/bash
PLIST=`who | grep localhost | awk '{print $2}'`
for i in $PLIST
do
ps -t /dev/$i |grep -v TTY | awk '{print $1}' | xargs kill -9
done
# getHostAddr
PLIST=`who | grep "${ip}" | awk '{print $2}'`
for i in $PLIST
do
ps -t /dev/$i |grep -v TTY | awk '{print $1}' | xargs kill -9
done
'''
if not isAppleSystem():
info = execShell(sh)
print(info[0], info[1])
##################### ssh end #########################################
# --------------------------------------------------------------------------------- # ---------------------------------------------------------------------------------
# 打印相关 # 打印相关 START
# --------------------------------------------------------------------------------- # ---------------------------------------------------------------------------------
@@ -1474,3 +2105,7 @@ def echoEnd(tag):
def echoInfo(msg): def echoInfo(msg):
print("|-{}".format(msg)) print("|-{}".format(msg))
# ---------------------------------------------------------------------------------
# 打印相关 END
# ---------------------------------------------------------------------------------
+88 -19
View File
@@ -27,7 +27,7 @@ import sys
import threading import threading
import multiprocessing import multiprocessing
from flask import render_template
from flask import request from flask import request
@@ -75,12 +75,35 @@ class plugins_api:
data = self.getPluginList(sType, int(sPage)) data = self.getPluginList(sType, int(sPage))
return mw.getJson(data) return mw.getJson(data)
def menuGetAbsPath(self, tag, path):
if path[0:1] == '/':
return path
else:
return mw.getPluginDir() + '/' + tag + '/' + path
def menuApi(self):
import config_api
data = config_api.config_api().get()
tag = request.args.get('tag', '')
menu_file = 'data/hook_menu.json'
content = ''
if os.path.exists(menu_file):
t = mw.readFile(menu_file)
tlist = json.loads(t)
for menu_data in tlist:
if tag == menu_data['name'] and 'path' in menu_data:
tpath = self.menuGetAbsPath(tag, menu_data['path'])
content = mw.readFile(tpath)
data['plugin_content'] = content
return render_template('plugin_menu.html', data=data)
def fileApi(self): def fileApi(self):
name = request.args.get('name', '') name = request.args.get('name', '')
if name.strip() == '': if name.strip() == '':
return '' return ''
f = request.args.get('f', '') f = request.args.get('f', '')
if f.strip() == '': if f.strip() == '':
return '' return ''
@@ -88,8 +111,16 @@ class plugins_api:
if not os.path.exists(file): if not os.path.exists(file):
return '' return ''
c = open(file, 'rb').read() suffix = mw.getPathSuffix(file)
return c if suffix == '.css':
content = mw.readFile(file)
from flask import Response
from flask import make_response
v = Response(content, headers={
'Content-Type': 'text/css; charset="utf-8"'})
return make_response(v)
content = open(file, 'rb').read()
return content
def indexListApi(self): def indexListApi(self):
data = self.getIndexList() data = self.getIndexList()
@@ -214,10 +245,7 @@ class plugins_api:
isNeedAdd = False isNeedAdd = False
if isNeedAdd: if isNeedAdd:
tmp = {} data.append(info)
tmp['title'] = info['title']
tmp['name'] = info['name']
data.append(tmp)
mw.writeFile(hookPath, json.dumps(data)) mw.writeFile(hookPath, json.dumps(data))
def hookUninstallFile(self, hook_name, info): def hookUninstallFile(self, hook_name, info):
@@ -228,26 +256,45 @@ class plugins_api:
data = json.loads(t) data = json.loads(t)
for idx in range(len(data)): for idx in range(len(data)):
if data[idx]['title'] == info['title'] and data[idx]['name'] == info['name']: if data[idx]['name'] == info['name']:
data.remove(data[idx]) data.remove(data[idx])
break
mw.writeFile(hookPath, json.dumps(data)) mw.writeFile(hookPath, json.dumps(data))
def hookInstall(self, info): def hookInstall(self, info):
valid_hook = ['backup', 'database']
valid_list_hook = ['menu', 'global_static', 'site_cb']
if 'hook' in info: if 'hook' in info:
hooks = info['hook'] hooks = info['hook']
for x in hooks: for h in hooks:
if x in ['backup', 'database']: hooks_type = type(h)
self.hookInstallFile(x, info) if hooks_type == dict:
return True tag = h['tag']
if tag in valid_list_hook:
self.hookInstallFile(tag, h[tag])
elif hooks_type == str:
for x in hooks:
if x in valid_hook:
self.hookInstallFile(x, info)
return True
return False return False
def hookUninstall(self, info): def hookUninstall(self, info):
valid_hook = ['backup', 'database']
valid_list_hook = ['menu', 'global_static', 'site_cb']
if 'hook' in info: if 'hook' in info:
hooks = info['hook'] hooks = info['hook']
for x in hooks: for h in hooks:
if x in ['backup', 'database']: hooks_type = type(h)
self.hookUninstallFile(x, info) if hooks_type == dict:
return True tag = h['tag']
if tag in valid_list_hook:
self.hookUninstallFile(tag, h[tag])
elif hooks_type == str:
for x in hooks:
if x in valid_hook:
self.hookUninstallFile(x, info)
return True
return False return False
def uninstallOldApi(self): def uninstallOldApi(self):
@@ -618,6 +665,7 @@ class plugins_api:
"ps": info['ps'], "ps": info['ps'],
"dependnet": "", "dependnet": "",
"mutex": "", "mutex": "",
"icon": "",
"path": path, "path": path,
"install_checks": checks, "install_checks": checks,
"uninsatll_checks": checks, "uninsatll_checks": checks,
@@ -632,6 +680,9 @@ class plugins_api:
"uninstall_pre_inspection": False, "uninstall_pre_inspection": False,
} }
if 'icon' in info:
pInfo['icon'] = info['icon']
if checks.find('VERSION') > -1: if checks.find('VERSION') > -1:
pInfo['install_checks'] = checks.replace( pInfo['install_checks'] = checks.replace(
'VERSION', info['versions']) 'VERSION', info['versions'])
@@ -677,6 +728,7 @@ class plugins_api:
def makeList(self, data, sType='0'): def makeList(self, data, sType='0'):
plugins_info = [] plugins_info = []
# 相应类型
if (data['pid'] == sType): if (data['pid'] == sType):
if type(data['versions']) == list and 'coexist' in data and data['coexist']: if type(data['versions']) == list and 'coexist' in data and data['coexist']:
tmp_data = self.makeCoexist(data) tmp_data = self.makeCoexist(data)
@@ -687,6 +739,7 @@ class plugins_api:
plugins_info.append(pg) plugins_info.append(pg)
return plugins_info return plugins_info
# 全部
if sType == '0': if sType == '0':
if type(data['versions']) == list and 'coexist' in data and data['coexist']: if type(data['versions']) == list and 'coexist' in data and data['coexist']:
tmp_data = self.makeCoexist(data) tmp_data = self.makeCoexist(data)
@@ -696,6 +749,18 @@ class plugins_api:
pg = self.getPluginInfo(data) pg = self.getPluginInfo(data)
plugins_info.append(pg) plugins_info.append(pg)
# 已经安装
if sType == '-1':
if type(data['versions']) == list and 'coexist' in data and data['coexist']:
tmp_data = self.makeCoexist(data)
for index in range(len(tmp_data)):
if tmp_data[index]['setup']:
plugins_info.append(tmp_data[index])
else:
pg = self.getPluginInfo(data)
if pg['setup']:
plugins_info.append(pg)
# print plugins_info, data # print plugins_info, data
return plugins_info return plugins_info
@@ -731,7 +796,8 @@ class plugins_api:
for index in range(len(tmp_data)): for index in range(len(tmp_data)):
plugins_info.append(tmp_data[index]) plugins_info.append(tmp_data[index])
except Exception as e: except Exception as e:
print(e) print(json_file)
print(mw.getTracebackInfo())
start = (page - 1) * pageSize start = (page - 1) * pageSize
end = start + pageSize end = start + pageSize
@@ -950,8 +1016,11 @@ class plugins_api:
# shell 调用 # shell 调用
def run(self, name, func, version='', args='', script='index'): def run(self, name, func, version='', args='', script='index'):
path = self.__plugin_dir + \
'/' + name + '/' + script + '.py' path = self.__plugin_dir + '/' + name + '/' + script + '.py'
if not os.path.exists(path):
path = self.__plugin_dir + '/' + name + '/' + name + '.py'
py = 'python3 ' + path py = 'python3 ' + path
if args == '': if args == '':
+115 -22
View File
@@ -72,6 +72,30 @@ class site_api:
mw.execShell("mkdir -p " + self.sslLetsDir + mw.execShell("mkdir -p " + self.sslLetsDir +
" && chmod -R 755 " + self.sslLetsDir) " && chmod -R 755 " + self.sslLetsDir)
def runHook(self, hook_name, func_name):
# 站点操作Hook
hook_file = 'data/hook_site_cb.json'
hook_cfg = []
if os.path.exists(hook_file):
t = mw.readFile(hook_file)
hook_cfg = json.loads(t)
hook_num = len(hook_cfg)
if hook_num == 0:
return
import plugins_api
pa = plugins_api.plugins_api()
for x in range(hook_num):
hook_data = hook_cfg[x]
if func_name in hook_data:
app_name = hook_data["name"]
run_func = hook_data[func_name]['func']
# print(app_name, run_func)
pa.run(app_name, run_func)
return True
##### ----- start ----- ### ##### ----- start ----- ###
def listApi(self): def listApi(self):
limit = request.form.get('limit', '10') limit = request.form.get('limit', '10')
@@ -550,7 +574,7 @@ class site_api:
return mw.returnJson(False, '使用中,先关闭再删除') return mw.returnJson(False, '使用中,先关闭再删除')
mw.execShell('rm -rf ' + ssl_lets_dir) mw.execShell('rm -rf ' + ssl_lets_dir)
elif ssl_type == 'acme': elif ssl_type == 'acme':
ssl_acme_dir = mw.getAcmeDir() + '/' + site_name ssl_acme_dir = mw.getAcmeDomainDir(site_name)
csr_acme_path = ssl_acme_dir + '/fullchain.cer' # 生成证书路径 csr_acme_path = ssl_acme_dir + '/fullchain.cer' # 生成证书路径
if mw.md5(mw.readFile(csr_acme_path)) == mw.md5(mw.readFile(csr_path)): if mw.md5(mw.readFile(csr_acme_path)) == mw.md5(mw.readFile(csr_path)):
return mw.returnJson(False, '使用中,先关闭再删除') return mw.returnJson(False, '使用中,先关闭再删除')
@@ -587,9 +611,9 @@ class site_api:
csr_path = self.sslLetsDir + '/' + site_name + '/fullchain.pem' # 生成证书路径 csr_path = self.sslLetsDir + '/' + site_name + '/fullchain.pem' # 生成证书路径
key_path = self.sslLetsDir + '/' + site_name + '/privkey.pem' # 密钥文件路径 key_path = self.sslLetsDir + '/' + site_name + '/privkey.pem' # 密钥文件路径
elif ssl_type == 'acme': elif ssl_type == 'acme':
csr_path = mw.getAcmeDir() + '/' + site_name + '/fullchain.cer' # 生成证书路径 acme_dir = mw.getAcmeDomainDir(site_name)
key_path = mw.getAcmeDir() + '/' + site_name + '/' + \ csr_path = acme_dir + '/fullchain.cer' # 生成证书路径
site_name + '.key' # 密钥文件路径 key_path = acme_dir + '/' + site_name + '.key' # 密钥文件路径
key = mw.readFile(key_path) key = mw.readFile(key_path)
csr = mw.readFile(csr_path) csr = mw.readFile(csr_path)
@@ -817,9 +841,9 @@ class site_api:
if not len(domains): if not len(domains):
return mw.returnJson(False, '请选择域名') return mw.returnJson(False, '请选择域名')
file = self.getHostConf(siteName) host_conf_file = self.getHostConf(siteName)
if os.path.exists(file): if os.path.exists(host_conf_file):
siteConf = mw.readFile(file) siteConf = mw.readFile(host_conf_file)
if siteConf.find('301-END') != -1: if siteConf.find('301-END') != -1:
return mw.returnJson(False, '检测到您的站点做了301重定向设置,请先关闭重定向!') return mw.returnJson(False, '检测到您的站点做了301重定向设置,请先关闭重定向!')
@@ -837,7 +861,15 @@ class site_api:
if os.path.exists(proxy_dir_file): if os.path.exists(proxy_dir_file):
return mw.returnJson(False, '检测到您的站点做了反向代理设置,请先关闭反向代理!') return mw.returnJson(False, '检测到您的站点做了反向代理设置,请先关闭反向代理!')
auth_to = self.getSitePath(siteName) # fix binddir domain ssl apply question
mw.backFile(host_conf_file)
auth_to = self.getSitePath(siteName)
rep = "\s*root\s*(.+);"
replace_root = "\n\troot " + auth_to + ";"
siteConf = re.sub(rep, replace_root, siteConf)
mw.writeFile(host_conf_file, siteConf)
mw.restartWeb()
to_args = { to_args = {
'domains': domains, 'domains': domains,
'auth_type': 'http', 'auth_type': 'http',
@@ -855,6 +887,7 @@ class site_api:
if not os.path.exists(src_letpath): if not os.path.exists(src_letpath):
import cert_api import cert_api
data = cert_api.cert_api().applyCertApi(to_args) data = cert_api.cert_api().applyCertApi(to_args)
mw.restoreFile(host_conf_file)
if not data['status']: if not data['status']:
msg = data['msg'] msg = data['msg']
if type(data['msg']) != str: if type(data['msg']) != str:
@@ -876,6 +909,8 @@ class site_api:
result['csr'] = mw.readFile(src_csrpath) result['csr'] = mw.readFile(src_csrpath)
result['key'] = mw.readFile(src_keypath) result['key'] = mw.readFile(src_keypath)
mw.restartWeb()
return mw.returnJson(data['status'], data['msg'], result) return mw.returnJson(data['status'], data['msg'], result)
def getAcmeLogsApi(self): def getAcmeLogsApi(self):
@@ -978,16 +1013,17 @@ class site_api:
# print(cmd) # print(cmd)
result = mw.execShell(cmd) result = mw.execShell(cmd)
src_path = acme_dir + '/' + domains[0] src_path = mw.getAcmeDomainDir(domains[0])
src_cert = src_path + '/fullchain.cer' src_cert = src_path + '/fullchain.cer'
src_key = src_path + '/' + domains[0] + '.key' src_key = src_path + '/' + domains[0] + '.key'
src_cert.replace("\*", "*")
msg = '签发失败,您尝试申请证书的失败次数已达上限!<p>1、检查域名是否绑定到对应站点</p>\ msg = '签发失败,您尝试申请证书的失败次数已达上限!<p>1、检查域名是否绑定到对应站点</p>\
<p>2、检查域名是否正确解析到本服务器,或解析还未完全生效</p>\ <p>2、检查域名是否正确解析到本服务器,或解析还未完全生效</p>\
<p>3、如果您的站点设置了反向代理,或使用了CDN,请先将其关闭</p>\ <p>3、如果您的站点设置了反向代理,或使用了CDN,请先将其关闭</p>\
<p>4、如果您的站点设置了301重定向,请先将其关闭</p>\ <p>4、如果您的站点设置了301重定向,请先将其关闭</p>\
<p>5、如果以上检查都确认没有问题,请尝试更换DNS服务商</p>' <p>5、如果以上检查都确认没有问题,请尝试更换DNS服务商</p>'
if not os.path.exists(src_cert.replace("\*", "*")): if not os.path.exists(src_cert):
data = {} data = {}
data['err'] = result data['err'] = result
data['out'] = result[0] data['out'] = result[0]
@@ -1118,6 +1154,26 @@ class site_api:
host = self.getHostConf(siteName) host = self.getHostConf(siteName)
return mw.getJson({'host': host}) return mw.getJson({'host': host})
def saveHostConfApi(self):
path = request.form.get('path', '')
data = request.form.get('data', '')
encoding = request.form.get('encoding', '')
import files_api
mw.backFile(path)
save_ret_data = files_api.files_api().saveBody(path, data, encoding)
rdata = json.loads(save_ret_data)
if rdata['status']:
isError = mw.checkWebConfig()
if isError != True:
mw.restoreFile(path)
return mw.returnJson(False, 'ERROR: 检测到配置文件有错误,请先排除后再操作<br><br><a style="color:red;">' + isError.replace("\n", '<br>') + '</a>')
mw.restartWeb()
mw.removeBackFile(path)
return save_ret_data
def getRewriteConfApi(self): def getRewriteConfApi(self):
siteName = request.form.get('siteName', '') siteName = request.form.get('siteName', '')
rewrite = self.getRewriteConf(siteName) rewrite = self.getRewriteConf(siteName)
@@ -1211,14 +1267,26 @@ class site_api:
return mw.returnJson(False, '端口范围不合法!') return mw.returnJson(False, '端口范围不合法!')
opid = mw.M('domain').where( opid = mw.M('domain').where(
"name=? AND (port=? OR pid=?)", (domain, domain_port, pid)).getField('pid') "name=? AND (port=? OR pid=?)", (domain_name, domain_port, pid,)).getField('pid')
if opid: # print(opid)
if mw.M('sites').where('id=?', (opid,)).count(): is_bind = False
return mw.returnJson(False, '指定域名已绑定过!') if type(opid) == list and len(opid) > 0:
mw.M('domain').where('pid=?', (opid,)).delete() is_bind = True
if type(opid) == int and opid > 0:
is_bind = True
if type(opid) == str and int(opid) > 0:
is_bind = True
if is_bind:
return mw.returnJson(False, '您添加的域名[{}],已绑定!'.format(domain_name))
# if mw.M('sites').where('id=?', (opid,)).count():
# return mw.returnJson(False, '指定域名已绑定过!')
# mw.M('domain').where('pid=?', (opid,)).delete()
if mw.M('binding').where('domain=?', (domain,)).count(): if mw.M('binding').where('domain=?', (domain,)).count():
return mw.returnJson(False, '您添加的域名已存在!') return mw.returnJson(False, '您添加的域名,子目录已绑定!')
self.nginxAddDomain(webname, domain_name, domain_port) self.nginxAddDomain(webname, domain_name, domain_port)
@@ -1228,6 +1296,7 @@ class site_api:
mw.M('domain').add('pid,name,port,addtime', mw.M('domain').add('pid,name,port,addtime',
(pid, domain_name, domain_port, mw.getDate())) (pid, domain_name, domain_port, mw.getDate()))
self.runHook('site_cb', 'add')
return mw.returnJson(True, '域名添加成功!') return mw.returnJson(True, '域名添加成功!')
def addDirBindApi(self): def addDirBindApi(self):
@@ -1270,17 +1339,18 @@ class site_api:
content = content.replace('{$ROOT_DIR}', webdir) content = content.replace('{$ROOT_DIR}', webdir)
content = content.replace('{$SERVER_MAIN}', siteInfo['name']) content = content.replace('{$SERVER_MAIN}', siteInfo['name'])
content = content.replace('{$OR_REWRITE}', self.rewritePath) content = content.replace('{$OR_REWRITE}', self.rewritePath)
content = content.replace('{$PHP_DIR}', self.setupPath + '/php')
content = content.replace('{$LOGPATH}', mw.getLogsDir()) content = content.replace('{$LOGPATH}', mw.getLogsDir())
conf += "\r\n" + content conf += "\r\n" + content
shutil.copyfile(filename, '/tmp/backup.conf') mw.backFile(filename)
mw.writeFile(filename, conf) mw.writeFile(filename, conf)
conf = mw.readFile(filename) conf = mw.readFile(filename)
# 检查配置是否有误 # 检查配置是否有误
isError = mw.checkWebConfig() isError = mw.checkWebConfig()
if isError != True: if isError != True:
shutil.copyfile('/tmp/backup.conf', filename) mw.restoreFile(filename)
return mw.returnJson(False, 'ERROR: <br><a style="color:red;">' + isError.replace("\n", '<br>') + '</a>') return mw.returnJson(False, 'ERROR: <br><a style="color:red;">' + isError.replace("\n", '<br>') + '</a>')
mw.M('binding').add('pid,domain,port,path,addtime', mw.M('binding').add('pid,domain,port,path,addtime',
@@ -1290,6 +1360,7 @@ class site_api:
(siteInfo['name'], dirName, domain)) (siteInfo['name'], dirName, domain))
mw.writeLog('网站管理', msg) mw.writeLog('网站管理', msg)
mw.restartWeb() mw.restartWeb()
mw.removeBackFile(filename)
return mw.returnJson(True, '添加成功!') return mw.returnJson(True, '添加成功!')
def delDirBindApi(self): def delDirBindApi(self):
@@ -1355,7 +1426,7 @@ class site_api:
data['filename'] = filename data['filename'] = filename
return mw.getJson(data) return mw.getJson(data)
# 修改物理路径 # 修改物理路径
def setPathApi(self): def setPathApi(self):
mid = request.form.get('id', '') mid = request.form.get('id', '')
path = request.form.get('path', '') path = request.form.get('path', '')
@@ -2484,16 +2555,18 @@ location ^~ {from} {\n\
self.createRootDir(self.sitePath) self.createRootDir(self.sitePath)
self.nginxAddConf() self.nginxAddConf()
mw.M('domain').add('pid,name,port,addtime',
(pid, self.siteName, self.sitePort, mw.getDate()))
# 添加更多域名 # 添加更多域名
for domain in siteMenu['domainlist']: for domain in siteMenu['domainlist']:
self.addDomain(domain, self.siteName, pid) self.addDomain(domain, self.siteName, pid)
mw.M('domain').add('pid,name,port,addtime',
(pid, self.siteName, self.sitePort, mw.getDate()))
data = {} data = {}
data['siteStatus'] = False data['siteStatus'] = False
mw.restartWeb() mw.restartWeb()
self.runHook('site_cb', 'add')
return mw.returnJson(True, '添加成功') return mw.returnJson(True, '添加成功')
def deleteWSLogs(self, webname): def deleteWSLogs(self, webname):
@@ -2543,7 +2616,27 @@ location ^~ {from} {\n\
mw.execShell('rm -rf ' + ssl_acme_dir) mw.execShell('rm -rf ' + ssl_acme_dir)
mw.M('sites').where("id=?", (sid,)).delete() mw.M('sites').where("id=?", (sid,)).delete()
mw.M('domain').where("pid=?", (sid,)).delete()
mw.M('domain').where("name=?", (webname,)).delete()
# binding domain delete
binding_list = mw.M('binding').field(
'id,domain').where("pid=?", (sid,)).select()
for x in binding_list:
wlog = mw.getLogsDir() + "/" + webname + "_" + x['domain'] + ".log"
wlog_error = mw.getLogsDir() + "/" + webname + "_" + \
x['domain'] + ".error.log"
if os.path.exists(wlog):
mw.execShell('rm -rf ' + wlog)
if os.path.exists(wlog_error):
mw.execShell('rm -rf ' + wlog_error)
mw.M('binding').where("pid=?", (sid,)).delete()
mw.restartWeb() mw.restartWeb()
self.runHook('site_cb', 'delete')
return mw.returnJson(True, '站点删除成功!') return mw.returnJson(True, '站点删除成功!')
def setEndDate(self, sid, edate): def setEndDate(self, sid, edate):
+456
View File
@@ -0,0 +1,456 @@
# coding: utf-8
# ---------------------------------------------------------------------------------
# MW-Linux面板
# ---------------------------------------------------------------------------------
# copyright (c) 2018-∞(https://github.com/midoks/mdserver-web) All rights reserved.
# ---------------------------------------------------------------------------------
# Author: midoks <midoks@163.com>
# ---------------------------------------------------------------------------------
# ---------------------------------------------------------------------------------
# SSH终端操作
# ---------------------------------------------------------------------------------
import json
import time
import os
import sys
import socket
import threading
import re
from io import BytesIO, StringIO
import mw
import paramiko
from flask_socketio import SocketIO, emit, send
class ssh_terminal:
__debug_file = 'logs/terminal.log'
__log_type = 'SSH终端'
# websocketio 唯一标识
__sid = ''
__host = None
__type = '0'
__port = 22
__user = None
__pass = None
__pkey = None
__key_passwd = None
__rep_ssh_config = False
__rep_ssh_service = False
__sshd_config_backup = None
__ssh = None
__tp = None
__ps = None
__ssh_list = {}
__ssh_last_request_time = {}
def __init__(self):
ht = threading.Thread(target=self.heartbeat)
ht.start()
def debug(self, msg):
msg = "{} - {}:{} => {} \n".format(mw.formatDate(),
self.__host, self.__port, msg)
if not mw.isDebugMode():
return
mw.writeFile(self.__debug_file, msg, 'a+')
def returnMsg(self, status, msg):
return {'status': status, 'msg': msg}
def restartSsh(self, act='reload'):
'''
重启ssh 无参数传递
'''
version = mw.readFile('/etc/redhat-release')
if not os.path.exists('/etc/redhat-release'):
mw.execShell('service ssh ' + act)
elif version.find(' 7.') != -1 or version.find(' 8.') != -1:
mw.execShell("systemctl " + act + " sshd.service")
else:
mw.execShell("/etc/init.d/sshd " + act)
def isRunning(self, rep=False):
try:
if rep and self.__rep_ssh_service:
self.restartSsh('stop')
return True
status = self.getSshStatus()
if not status:
self.restartSsh('start')
self.__rep_ssh_service = True
return True
return False
except:
return False
def setSshdConfig(self, rep=False):
self.isRunning(rep)
if rep and not self.__rep_ssh_config:
return False
try:
sshd_config_file = '/etc/ssh/sshd_config'
if not os.path.exists(sshd_config_file):
return False
sshd_config = mw.readFile(sshd_config_file)
if not sshd_config:
return False
if rep:
if self.__sshd_config_backup:
mw.writeFile(sshd_config_file, self.__sshd_config_backup)
self.restartSsh()
return True
pin = r'^\s*PubkeyAuthentication\s+(yes|no)'
pubkey_status = re.findall(pin, sshd_config, re.I)
if pubkey_status:
if pubkey_status[0] == 'yes':
pubkey_status = True
else:
pubkey_status = False
pin = r'^\s*RSAAuthentication\s+(yes|no)'
rsa_status = re.findall(pin, sshd_config, re.I)
if rsa_status:
if rsa_status[0] == 'yes':
rsa_status = True
else:
rsa_status = False
self._sshd_config_backup = sshd_config
is_write = False
if not pubkey_status:
sshd_config = re.sub(
r'\n#?PubkeyAuthentication\s\w+', '\nPubkeyAuthentication yes', sshd_config)
is_write = True
if not rsa_status:
sshd_config = re.sub(
r'\n#?RSAAuthentication\s\w+', '\nRSAAuthentication yes', sshd_config)
is_write = True
if is_write:
mw.writeFile(sshd_config_file, sshd_config)
self.__rep_ssh_config = True
self.restartSsh()
else:
self.__sshd_config_backup = None
return True
except:
return False
def setSid(self, sid):
self.__sid = sid
def connect(self, sid):
# self.connectBySocket()
if self.__host in ['127.0.0.1', 'localhost']:
return self.connectLocalSsh(sid)
else:
return self.connectBySocket(sid)
def connectLocalSsh(self, sid):
mw.createSshInfo()
self.__ps = paramiko.SSHClient()
self.__ps.set_missing_host_key_policy(paramiko.AutoAddPolicy())
self.__port = mw.getSSHPort()
try:
self.__ps.connect(self.__host, self.__port, timeout=60)
except Exception as e:
self.__ps.connect('127.0.0.1', self.__port)
except Exception as e:
self.__ps.connect('localhost', self.__port)
except Exception as e:
self.setSshdConfig(True)
self.__ps.close()
e = str(e)
if e.find('websocket error!') != -1:
return self.returnMsg(True, '连接成功')
if e.find('Authentication timeout') != -1:
self.debug("认证超时{}".format(e))
return self.returnMsg(False, '认证超时,请按回车重试!{}'.format(e))
if e.find('Connection reset by peer') != -1:
self.debug('目标服务器主动拒绝连接')
return self.returnMsg(False, '目标服务器主动拒绝连接')
if e.find('Error reading SSH protocol banner') != -1:
self.debug('协议头响应超时')
return self.returnMsg(False, '协议头响应超时,与目标服务器之间的网络质量太糟糕:' + e)
if not e:
self.debug('SSH协议握手超时')
return self.returnMsg(False, "SSH协议握手超时,与目标服务器之间的网络质量太糟糕")
err = mw.getTracebackInfo()
self.debug(err)
return self.returnMsg(False, "未知错误: {}".format(err))
self.debug('local-ssh:认证成功,正在构建会话通道')
ssh = self.__ps.invoke_shell(
term='xterm', width=83, height=21)
ssh.setblocking(0)
self.__ssh_list[sid] = ssh
mw.writeLog(self.__log_type, '成功登录到SSH服务器 [{}:{}]'.format(
self.__host, self.__port))
self.debug('local-ssh:通道已构建')
return self.returnMsg(True, '连接成功!')
def connectBySocket(self, sid):
if not self.__host:
return self.returnMsg(False, '错误的连接地址')
if not self.__user:
self.__user = 'root'
if not self.__port:
self.__port = 22
self.setSshdConfig(True)
num = 0
while num < 5:
num += 1
try:
self.debug('正在尝试第{}次连接'.format(num))
if self.__rep_ssh_config:
time.sleep(0.1)
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.settimeout(2 + num)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_SNDBUF, 8192)
sock.connect((self.__host, self.__port))
break
except Exception as e:
if num == 5:
self.setSshdConfig(True)
self.debug('重试连接失败,{}'.format(e))
return self.returnMsg(False, '连接目标服务器失败, {}:{}'.format(self.__host, self.__port))
else:
time.sleep(0.2)
# print(self.__host, sock)
self.__tp = paramiko.Transport(sock)
try:
self.__tp.start_client()
self.__tp.banner_timeout = 60
if not self.__pass and not self.__pkey:
return self.returnMsg(False, '密码或私钥不能都为空: {}:{}'.format(self.__host, self.__port))
if self.__pkey != '' and self.__type != '0':
self.debug('正在认证私钥')
p_file = StringIO(str(self.__pkey.replace('\\n', '\n')))
# p_file = "/tmp/t_ssh_pkey.txt"
# mw.writeFile(p_file, self.__pkey.replace('\\n', '\n'))
# mw.execShell('chmod 600 ' + p_file)
try:
p_file.seek(0)
pkey = paramiko.RSAKey.from_private_key(p_file)
except:
try:
p_file.seek(0) # 重置游标
pkey = paramiko.Ed25519Key.from_private_key(
p_file)
except:
try:
p_file.seek(0)
pkey = paramiko.ECDSAKey.from_private_key(p_file)
except:
p_file.seek(0)
pkey = paramiko.DSSKey.from_private_key(p_file)
self.__tp.auth_publickey(username=self.__user, key=pkey)
else:
try:
self.__tp.auth_none(self.__user)
except Exception as e:
e = str(e)
if e.find('keyboard-interactive') >= 0:
self._auth_interactive()
else:
self.debug('正在认证密码')
self.__tp.auth_password(
username=self.__user, password=self.__pass)
except Exception as e:
self.setSshdConfig(True)
self.__tp.close()
e = str(e)
# print(e)
if e.find('Authentication timeout') != -1:
self.debug("认证超时{}".format(e))
return self.returnMsg(False, '认证超时,请按回车重试!{}'.format(e))
if e.find('Authentication failed') != -1:
self.debug('认证失败{}'.format(e))
return self.returnMsg(False, '帐号或密码错误: {}'.format(e + "," + self.__user + "@" + self.__host + ":" + str(self.__port)))
if e.find('Bad authentication type; allowed types') != -1:
self.debug('认证失败{}'.format(e))
if self.__host in ['127.0.0.1', 'localhost'] and self.__pass == 'none':
return self.returnMsg(False, '帐号或密码错误: {}'.format("Authentication failed ," + self.__user + "@" + self.__host + ":" + str(self.__port)))
return self.returnMsg(False, '不支持的身份验证类型: {}'.format(e))
if e.find('Connection reset by peer') != -1:
self.debug('目标服务器主动拒绝连接')
return self.returnMsg(False, '目标服务器主动拒绝连接')
if e.find('Error reading SSH protocol banner') != -1:
self.debug('协议头响应超时')
return self.returnMsg(False, '协议头响应超时,与目标服务器之间的网络质量太糟糕:' + e)
if not e:
self.debug('SSH协议握手超时')
return self.returnMsg(False, "SSH协议握手超时,与目标服务器之间的网络质量太糟糕")
err = mw.getTracebackInfo()
self.debug(err)
return self.returnMsg(False, "未知错误: {}".format(err))
self.debug('认证成功,正在构建会话通道')
ssh = self.__tp.open_session()
ssh.get_pty(term='xterm', width=100, height=34)
ssh.invoke_shell()
self.__ssh_list[sid] = ssh
mw.writeLog(self.__log_type, '成功登录到SSH服务器 [{}:{}]'.format(
self.__host, self.__port))
self.debug('通道已构建')
return self.returnMsg(True, '连接成功.')
def getSshInfo(self, file):
rdata = mw.readFile(file)
destr = mw.enDoubleCrypt('mdserver-web', rdata)
return json.loads(destr)
def setAttr(self, sid, info):
self.__host = info['host'].strip()
# 外部连接获取
if not self.__host in ['127.0.0.1', 'localhost']:
dst_info = mw.getServerDir() + '/webssh/host/' + self.__host + '/info.json'
if os.path.exists(dst_info):
info = self.getSshInfo(dst_info)
if 'type' in info:
self.__type = info['type']
if 'port' in info:
self.__port = int(info['port'])
if 'username' in info:
self.__user = info['username']
if 'pkey' in info:
self.__pkey = info['pkey']
if 'password' in info:
self.__pass = info['password']
if 'pkey_passwd' in info:
self.__key_passwd = info['pkey_passwd']
# print(self.__host, self.__pass, self.__key_passwd)
try:
result = self.connect(sid)
# print(result)
except Exception as ex:
if str(ex).find("NoneType") == -1:
raise ex
return result
def send(self):
pass
def close(self):
try:
if self.__ssh:
self.__ssh.close()
if self.__tp: # 关闭宿主服务
self.__tp.close()
if self.__ps:
self.__ps.close()
except:
pass
def resize(self, sid, data):
try:
self.__ssh_list[sid].resize_pty(
width=data['cols'], height=data['rows'])
return True
except:
return False
def wsSend(self, recv):
try:
t = recv.decode("utf-8")
return emit('server_response', {'data': t})
except Exception as e:
return emit('server_response', {'data': recv})
def wsSendConnect(self):
return emit('connect', {'data': 'ok'})
def wsSendReConnect(self):
return emit('reconnect', {'data': 'ok'})
def heartbeat(self):
# limit_cos = 10
while True:
time.sleep(3)
cur_time = time.time()
for x in list(self.__ssh_list.keys()):
ssh_last_time = self.__ssh_last_request_time[x]
sid_off_cos = cur_time - ssh_last_time
# print("heartbeat off cos :", x, sid_off_cos)
if sid_off_cos > 3:
cur_ssh = self.__ssh_list[x]
if not cur_ssh:
del(self.__ssh_list[x])
del(self.__ssh_last_request_time[x])
continue
if cur_ssh.exit_status_ready():
del(self.__ssh_list[x])
del(self.__ssh_last_request_time[x])
continue
cur_ssh.send("exit\r\n")
del(self.__ssh_list[x])
del(self.__ssh_last_request_time[x])
def run(self, sid, info):
self.__sid = sid
if not self.__sid:
return self.wsSend('WebSocketIO无效')
self.__ssh_last_request_time[sid] = time.time()
if not sid in self.__ssh_list:
if type(info) == dict and 'host' in info:
result = self.setAttr(sid, info)
if result['status']:
return self.wsSendConnect()
else:
return self.wsSend(result['msg'])
else:
return self.wsSendReConnect()
result = self.returnMsg(False, '')
if sid in self.__ssh_list:
if 'resize' in info:
self.resize(sid, info)
result = self.returnMsg(True, '已连接')
if result['status']:
if type(info) == str:
time.sleep(0.1)
cur_ssh = self.__ssh_list[sid]
if cur_ssh.exit_status_ready():
self.wsSend("logout\r\n")
del(self.__ssh_list[sid])
return
cur_ssh.send(info)
try:
time.sleep(0.005)
recv = cur_ssh.recv(8192)
return self.wsSend(recv)
except Exception as ex:
return self.wsSend('')
else:
return self.wsSend(result['msg'])
+47
View File
@@ -166,6 +166,33 @@ class system_api:
except: except:
return False return False
def getEnvInfoApi(self, get=None):
serverInfo = {}
serverInfo['status'] = True
sdir = mw.getServerDir()
serverInfo['webserver'] = '未安装'
if os.path.exists(sdir + '/openresty/nginx/sbin/nginx'):
serverInfo['webserver'] = 'OpenResty'
serverInfo['php'] = []
phpversions = ['52', '53', '54', '55', '56', '70', '71',
'72', '73', '74', '80', '81', '82', '83', '84']
phpPath = sdir + '/php/'
for pv in phpversions:
if not os.path.exists(phpPath + pv + '/bin/php'):
continue
serverInfo['php'].append(pv)
serverInfo['mysql'] = False
if os.path.exists(sdir + '/mysql/bin/mysql'):
serverInfo['mysql'] = True
import psutil
try:
diskInfo = psutil.disk_usage('/www')
except:
diskInfo = psutil.disk_usage('/')
serverInfo['disk'] = diskInfo[2]
return mw.returnJson(True, 'ok', serverInfo)
def getPanelInfo(self, get=None): def getPanelInfo(self, get=None):
# 取面板配置 # 取面板配置
address = mw.GetLocalIp() address = mw.GetLocalIp()
@@ -724,6 +751,26 @@ class system_api:
mw.execShell('rm -rf ' + toPath + '/mdserver-web-' + version) mw.execShell('rm -rf ' + toPath + '/mdserver-web-' + version)
mw.execShell('rm -rf ' + toPath + '/mw.zip') mw.execShell('rm -rf ' + toPath + '/mw.zip')
update_env = '''
#!/bin/bash
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
if [ ! -f /www/server/mdserver-web/bin/activate ];then
cd /www/server/mdserver-web && python3 -m venv .
cd /www/server/mdserver-web && source /www/server/mdserver-web/bin/activate
else
cd /www/server/mdserver-web && source /www/server/mdserver-web/bin/activate
fi
cn=$(curl -fsSL -m 10 http://ipinfo.io/json | grep "\"country\": \"CN\"")
PIPSRC="https://pypi.python.org/simple"
if [ ! -z "$cn" ];then
PIPSRC="https://pypi.tuna.tsinghua.edu.cn/simple"
fi
cd /www/server/mdserver-web && pip3 install -r /www/server/mdserver-web/requirements.txt -i $PIPSRC
'''
os.system(update_env)
self.restartMw() self.restartMw()
return mw.returnJson(True, '安装更新成功!') return mw.returnJson(True, '安装更新成功!')
+48
View File
@@ -0,0 +1,48 @@
# coding:utf-8
# ---------------------------------------------------------------------------------
# MW-Linux面板
# ---------------------------------------------------------------------------------
# copyright (c) 2018-∞(https://github.com/midoks/mdserver-web) All rights reserved.
# ---------------------------------------------------------------------------------
# Author: midoks <midoks@163.com>
# ---------------------------------------------------------------------------------
# ---------------------------------------------------------------------------------
# VIP用户管理
# ---------------------------------------------------------------------------------
import mw
from flask import request
import requests
class vip_api:
api_url = 'https://wo.midoks.me/api/wp-json/vip'
def __init__(self):
pass
def loginApi(self):
username = request.form.get('username', '')
password = request.form.get('password', '')
password = mw.aesEncrypt(password)
headers = {
"Content-Type": "application/x-www-form-urlencoded",
}
print("name:", str(username))
print("pwd:", str(password))
args = {
'name': username,
'pass': password
}
data = requests.post(self.api_url + '/v1/login',
data=args, headers=headers)
print(data.text)
return mw.returnJson(False, "测试中!")
+43
View File
@@ -0,0 +1,43 @@
# coding: utf-8
import smtplib
from email import encoders
from email.header import Header
from email.mime.text import MIMEText
from email.utils import parseaddr, formataddr
def _format_addr(s):
name, addr = parseaddr(s)
return formataddr((Header(name, 'utf-8').encode(), addr))
def send(smtp_host, smtp_port, username, password, to_mail, subject, content):
smtp = smtplib.SMTP()
smtp.connect(smtp_host, port=smtp_port)
smtp.login(user=username, password=password)
msg = MIMEText(content, 'plain', 'utf-8')
msg['From'] = _format_addr(username)
msg['To'] = _format_addr(to_mail)
msg['Subject'] = Header(subject, 'utf-8').encode()
smtp.sendmail(from_addr=username, to_addrs=to_mail, msg=msg.as_string())
return True
def sendSSL(smtp_host, smtp_port, username, password, to_mail, subject, content):
smtp = smtplib.SMTP_SSL(smtp_host, port=smtp_port)
smtp.login(user=username, password=password)
msg = MIMEText(content, 'plain', 'utf-8')
msg['From'] = _format_addr(username)
msg['To'] = _format_addr(to_mail)
msg['Subject'] = Header(subject, 'utf-8').encode()
smtp.sendmail(from_addr=username, to_addrs=to_mail, msg=msg.as_string())
smtp.quit()
return True
+3
View File
@@ -65,6 +65,9 @@ class ORM:
def setCharset(self, charset): def setCharset(self, charset):
self.__DB_CHARSET = charset self.__DB_CHARSET = charset
def setHost(self, host):
self.__DB_HOST = host
def setPort(self, port): def setPort(self, port):
self.__DB_PORT = port self.__DB_PORT = port
+14 -9
View File
@@ -21,12 +21,12 @@ mw_start_task()
sleep 0.3 sleep 0.3
isStart=$(ps aux |grep 'task.py'|grep -v grep|awk '{print $2}') isStart=$(ps aux |grep 'task.py'|grep -v grep|awk '{print $2}')
if [ "$isStart" == '' ];then if [ "$isStart" == '' ];then
echo -e "\033[31mfailed\033[0m" echo -e "\033[31mfailed\033[0m"
echo '------------------------------------------------------' echo '------------------------------------------------------'
tail -n 20 $DIR/logs/task.log tail -n 20 $DIR/logs/task.log
echo '------------------------------------------------------' echo '------------------------------------------------------'
echo -e "\033[31mError: mw-tasks service startup failed.\033[0m" echo -e "\033[31mError: mw-tasks service startup failed.\033[0m"
return; return;
fi fi
echo -e "\033[32mdone\033[0m" echo -e "\033[32mdone\033[0m"
else else
@@ -43,7 +43,12 @@ mw_start(){
mw_start_debug(){ mw_start_debug(){
python3 task.py >> $DIR/logs/task.log 2>&1 & python3 task.py >> $DIR/logs/task.log 2>&1 &
gunicorn -b :7200 -k gevent -w 1 app:app port=7200
if [ -f /www/server/mdserver-web/data/port.pl ];then
port=$(cat /www/server/mdserver-web/data/port.pl)
fi
# gunicorn -b :${port} -k gevent -w 1 app:app
gunicorn -b :${port} -k geventwebsocket.gunicorn.workers.GeventWebSocketWorker -w 1 app:app
} }
mw_start_debug2(){ mw_start_debug2(){
@@ -57,14 +62,14 @@ mw_stop()
PLIST=`ps -ef|grep app:app |grep -v grep|awk '{print $2}'` PLIST=`ps -ef|grep app:app |grep -v grep|awk '{print $2}'`
for i in $PLIST for i in $PLIST
do do
kill -9 $i kill -9 $i > /dev/null 2>&1
done done
pids=`ps -ef|grep task.py | grep -v grep |awk '{print $2}'` pids=`ps -ef|grep task.py | grep -v grep |awk '{print $2}'`
arr=($pids) arr=($pids)
for p in ${arr[@]} for p in ${arr[@]}
do do
kill -9 $p kill -9 $p > /dev/null 2>&1
done done
} }
+5
View File
@@ -4,6 +4,11 @@
"type":0, "type":0,
"ps":"" "ps":""
}, },
{
"title":"已安装",
"type":-1,
"ps":""
},
{ {
"title":"运行环境", "title":"运行环境",
"type":1, "type":1,
+1 -1
View File
@@ -17,7 +17,7 @@ server
#ERROR-PAGE-END #ERROR-PAGE-END
#PHP-INFO-START #PHP-INFO-START
include enable-php-{$PHPVER}.conf; include {$PHP_DIR}/conf/enable-php-{$PHPVER}.conf;
#PHP-INFO-END #PHP-INFO-END
#REWRITE-START #REWRITE-START
+1
View File
@@ -0,0 +1 @@
存放VIP关键数据
+8 -5
View File
@@ -194,14 +194,17 @@ def backupAllFunc(stype):
'/scripts/backup.py ' + args '/scripts/backup.py ' + args
os.system(cmd) os.system(cmd)
# 开始执行上传信息
bk_prefix = prefix_dict[stype] # 开始执行上传信息.
bk_name = stype
if stype.find('database_') > -1: if stype.find('database_') > -1:
bk_name = 'database' bk_name = 'database'
plugin_name = stype.replace('database_', '') plugin_name = stype.replace('database_', '')
bk_prefix = plugin_name + '/db' bk_prefix = plugin_name + '/db'
stype = 'database'
else:
bk_prefix = prefix_dict[stype]
bk_name = stype
find_path = mw.getBackupDir() + '/' + bk_name + '/' + bk_prefix + '_' + name find_path = mw.getBackupDir() + '/' + bk_name + '/' + bk_prefix + '_' + name
find_new_file = "ls " + find_path + \ find_new_file = "ls " + find_path + \
@@ -212,10 +215,10 @@ def backupAllFunc(stype):
mw.echoInfo("not find upload file!") mw.echoInfo("not find upload file!")
return False return False
print("|-准备上传文件 {}".format(filename))
ftp = FtpPSClient() ftp = FtpPSClient()
ftp.uploadFile(filename, stype) ftp.uploadFile(filename, stype)
return ''
return True
def backupSite(): def backupSite():
+1 -1
View File
@@ -11,7 +11,7 @@
"checks":"server/backup_ftp", "checks":"server/backup_ftp",
"path": "server/backup_ftp", "path": "server/backup_ftp",
"author":"midoks", "author":"midoks",
"home":"", "home":"https://github.com/midoks/mdserver-web",
"date":"2022-10-23", "date":"2022-10-23",
"pid": "4" "pid": "4"
} }
+5 -1
View File
@@ -33,7 +33,11 @@ if [ ! -d $WEB_PATH ];then
mkdir -p $WEB_PATH mkdir -p $WEB_PATH
rsync -vauP --delete --exclude=".*" $GIT_PROJECT_DIR/ $WEB_PATH rsync -vauP --delete --exclude=".*" $GIT_PROJECT_DIR/ $WEB_PATH
else else
rsync -vauP --exclude=".*" $GIT_PROJECT_DIR/ $WEB_PATH if [ -f $GIT_PROJECT_DIR/exclude.list ];then
rsync -vauP --delete --exclude-from="$GIT_PROJECT_DIR/exclude.list" $GIT_PROJECT_DIR/ $WEB_PATH
else
rsync -vauP --exclude=".*" $GIT_PROJECT_DIR/ $WEB_PATH
fi
fi fi
sysName=`uname` sysName=`uname`
+2 -2
View File
@@ -8,8 +8,8 @@
<p onclick="pluginConfig('gitea',null, 'conf');">配置文件</p> <p onclick="pluginConfig('gitea',null, 'conf');">配置文件</p>
<p onclick="gogsSetConfig();">配置修改</p> <p onclick="gogsSetConfig();">配置修改</p>
<p onclick="giteaUserList();">用户列表</p> <p onclick="giteaUserList();">用户列表</p>
<p onclick="pluginLogs('gitea',null,'run_log');">运行日志</p> <!-- <p onclick="pluginLogs('gitea',null,'run_log');">运行日志</p> -->
<p onclick="pluginLogs('gitea',null,'post_receive_log');" title="提交post-receive日志">提交日志</p> <!-- <p onclick="pluginLogs('gitea',null,'post_receive_log');" title="提交post-receive日志">提交日志</p> -->
<p onclick="giteaRead();">使用说明</p> <p onclick="giteaRead();">使用说明</p>
</div> </div>
<div class="bt-w-con pd15"> <div class="bt-w-con pd15">
+1 -1
View File
@@ -560,7 +560,7 @@ def userProjectList():
data['data'] = ret_data data['data'] = ret_data
data['args'] = args data['args'] = args
data['list'] = mw.getPage( data['list'] = mw.getPage(
{'count': dlist_sum, 'p': page, 'row': page_size, 'tojs': 'userProjectList'}) {'count': dlist_sum, 'p': page, 'row': page_size, 'tojs': 'userProjectListPost'})
return mw.returnJson(True, 'OK', data) return mw.returnJson(True, 'OK', data)
+2 -2
View File
@@ -1,8 +1,8 @@
{ {
"ps": "Gitea 是一个开源社区驱动的轻量级代码托管解决方案。", "ps": "Gitea是一个开源社区驱动的轻量级代码托管解决方案。",
"name": "gitea", "name": "gitea",
"title": "Gitea", "title": "Gitea",
"versions": ["1.17.2"], "versions": ["1.17.2","1.18.5"],
"tip": "soft", "tip": "soft",
"install_pre_inspection":false, "install_pre_inspection":false,
"uninstall_pre_inspection":true, "uninstall_pre_inspection":true,
+1 -1
View File
@@ -14,7 +14,7 @@ if [ -f ${rootPath}/bin/activate ];then
fi fi
URL_DOWNLOAD=https://dl.gitea.io/ URL_DOWNLOAD=https://dl.gitea.com
bash ${rootPath}/scripts/getos.sh bash ${rootPath}/scripts/getos.sh
+3 -2
View File
@@ -187,7 +187,7 @@ function userProjectList(user, search){
<thead><tr><th>项目</th><th>操作</th></tr></thead>\ <thead><tr><th>项目</th><th>操作</th></tr></thead>\
<tbody></tbody>\ <tbody></tbody>\
</table>\ </table>\
<div class='dataTables_paginate paging_bootstrap pagination' style='margin-top:0px;'><ul id='gitea_page' class='page'></ul></div>\ <div class='dataTables_paginate paging_bootstrap pagination' style='margin-top:0px;'><ul class='page'><div class='gitea_page'></div></ul></div>\
</div>\ </div>\
</div>\ </div>\
</div>", </div>",
@@ -230,7 +230,8 @@ function userProjectListPost(user, search){
var project_list = rdata['data']['data']; var project_list = rdata['data']['data'];
for (i in project_list) { for (i in project_list) {
var name = project_list[i]['name']; var name = project_list[i]['name'];
list += '<tr><td>'+name+'</td>\ list += '<tr>\
<td>'+name+'</td>\
<td>\ <td>\
<a class="btlink" target="_blank" href="'+rdata['data']['root_url']+user+'/'+name+'">源码</a> | \ <a class="btlink" target="_blank" href="'+rdata['data']['root_url']+user+'/'+name+'">源码</a> | \
<a class="btlink" onclick="projectScript(\''+user+'\',\''+name+'\','+project_list[i]['has_hook']+');">脚本</a>\ <a class="btlink" onclick="projectScript(\''+user+'\',\''+name+'\','+project_list[i]['has_hook']+');">脚本</a>\
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"sort": 7, "sort": 7,
"ps": "简单邮件服务[DEV]", "ps": "[<b style='color:red;'>DEV</b>]简单邮件服务",
"name": "imail", "name": "imail",
"title": "邮件服务", "title": "邮件服务",
"shell": "install.sh", "shell": "install.sh",
+3 -3
View File
@@ -10,8 +10,8 @@ serverPath=$(dirname "$rootPath")
install_tmp=${rootPath}/tmp/mw_install.pl install_tmp=${rootPath}/tmp/mw_install.pl
VERSION=0.0.19 VERSION=0.0.19
# bash install.sh install 0.0.16 # bash install.sh install 0.0.19
## cd /www/server/mdserver-web/plugins/imail && bash install.sh install 0.0.16 ## cd /www/server/mdserver-web/plugins/imail && bash install.sh install 0.0.19
bash ${rootPath}/scripts/getos.sh bash ${rootPath}/scripts/getos.sh
OSNAME=`cat ${rootPath}/data/osname.pl` OSNAME=`cat ${rootPath}/data/osname.pl`
@@ -24,7 +24,7 @@ get_arch() {
TMP_ARCH=`arch` TMP_ARCH=`arch`
if [ "$TMP_ARCH" == "x86_64" ];then if [ "$TMP_ARCH" == "x86_64" ];then
ARCH="amd64" ARCH="amd64"
else if [ "$TMP_ARCH" == "aarch64" ];then elif [ "$TMP_ARCH" == "aarch64" ];then
ARCH="arm64" ARCH="arm64"
else else
echo $ARCH echo $ARCH
+14
View File
@@ -40,4 +40,18 @@ CREATE TABLE IF NOT EXISTS `slave_id_rsa` (
`addtime` TEXT `addtime` TEXT
); );
-- 从库配置主库的[user]
-- drop table `slave_user`;
CREATE TABLE IF NOT EXISTS `slave_sync_user` (
`id` INTEGER PRIMARY KEY AUTOINCREMENT,
`ip` TEXT,
`port` TEXT,
`user` TEXT,
`pass` TEXT,
`mode` TEXT,
`cmd` TEXT,
`addtime` TEXT
);
+1
View File
@@ -70,6 +70,7 @@ replicate-ignore-db = information_schema
replicate-ignore-db = performance_schema replicate-ignore-db = performance_schema
replicate-ignore-db = mysql replicate-ignore-db = mysql
replicate-ignore-db = test replicate-ignore-db = test
replicate-wild-ignore-table=mysql.%
innodb_data_home_dir = {$SERVER_APP_PATH}/data innodb_data_home_dir = {$SERVER_APP_PATH}/data
+316 -16
View File
@@ -140,20 +140,27 @@ def contentReplace(content):
def pSqliteDb(dbname='databases'): def pSqliteDb(dbname='databases'):
file = getServerDir() + '/mariadb.db' file = getServerDir() + '/mariadb.db'
name = 'mysql' name = 'mysql'
if not os.path.exists(file):
import_sql = mw.readFile(getPluginDir() + '/conf/mariadb.sql')
md5_sql = mw.md5(import_sql)
import_sign = False
save_md5_file = getServerDir() + '/import_sql.md5'
if os.path.exists(save_md5_file):
save_md5_sql = mw.readFile(save_md5_file)
if save_md5_sql != md5_sql:
import_sign = True
mw.writeFile(save_md5_file, md5_sql)
else:
mw.writeFile(save_md5_file, md5_sql)
if not os.path.exists(file) or import_sql:
conn = mw.M(dbname).dbPos(getServerDir(), name) conn = mw.M(dbname).dbPos(getServerDir(), name)
csql = mw.readFile(getPluginDir() + '/conf/mariadb.sql') csql_list = import_sql.split(';')
csql_list = csql.split(';')
for index in range(len(csql_list)): for index in range(len(csql_list)):
conn.execute(csql_list[index], ()) conn.execute(csql_list[index], ())
else:
# 现有run conn = mw.M(dbname).dbPos(getServerDir(), name)
# conn = mw.M(dbname).dbPos(getServerDir(), name)
# csql = mw.readFile(getPluginDir() + '/conf/mysql.sql')
# csql_list = csql.split(';')
# for index in range(len(csql_list)):
# conn.execute(csql_list[index], ())
conn = mw.M(dbname).dbPos(getServerDir(), name)
return conn return conn
@@ -280,6 +287,13 @@ def binLog():
return mw.returnJson(True, '设置成功!') return mw.returnJson(True, '设置成功!')
def cleanBinLog():
db = pMysqlDb()
cleanTime = time.strftime('%Y-%m-%d %H:%i:%s', time.localtime())
db.execute("PURGE MASTER LOGS BEFORE '" + cleanTime + "';")
return mw.returnJson(True, '清理BINLOG成功!')
def setSkipGrantTables(v): def setSkipGrantTables(v):
''' '''
设置是否密码验证 设置是否密码验证
@@ -1785,7 +1799,7 @@ def addMasterRepSlaveUser(version=''):
# address = args['address'].strip() # address = args['address'].strip()
# dataAccess = args['dataAccess'].strip() # dataAccess = args['dataAccess'].strip()
reg = "^[\w\.-]+$" reg = "^[\w-]+$"
if not re.match(reg, username): if not re.match(reg, username):
return mw.returnJson(False, '用户名不能带有特殊符号!') return mw.returnJson(False, '用户名不能带有特殊符号!')
checks = ['root', 'mysql', 'test', 'sys', 'panel_logs'] checks = ['root', 'mysql', 'test', 'sys', 'panel_logs']
@@ -1806,11 +1820,15 @@ def addMasterRepSlaveUser(version=''):
sql = "GRANT REPLICATION SLAVE ON *.* TO '" + username + \ sql = "GRANT REPLICATION SLAVE ON *.* TO '" + username + \
"'@'%' identified by '" + password + "';" "'@'%' identified by '" + password + "';"
result = pdb.execute(sql) result = pdb.execute(sql)
result = pdb.execute('FLUSH PRIVILEGES;')
isError = isSqlError(result) isError = isSqlError(result)
if isError != None: if isError != None:
return isError return isError
sql_select = "grant select,lock tables,PROCESS on *.* to " + username + "@'%';"
pdb.execute(sql_select)
pdb.execute('FLUSH PRIVILEGES;')
addTime = time.strftime('%Y-%m-%d %X', time.localtime()) addTime = time.strftime('%Y-%m-%d %X', time.localtime())
psdb.add('username,password,accept,ps,addtime', psdb.add('username,password,accept,ps,addtime',
(username, password, '%', '', addTime)) (username, password, '%', '', addTime))
@@ -1847,10 +1865,16 @@ def getMasterRepSlaveUserCmd(version):
mode = recognizeDbMode() mode = recognizeDbMode()
# 查找同步点
# SELECT BINLOG_GTID_POS('master1-bin.000002', 561866201);
if mode == "gtid": if mode == "gtid":
sql = "CHANGE MASTER TO MASTER_HOST='" + ip + "', MASTER_PORT=" + port + ", MASTER_USER='" + \ sql = "CHANGE MASTER TO MASTER_HOST='" + ip + "', MASTER_PORT=" + port + ", MASTER_USER='" + \
clist[0]['username'] + "', MASTER_PASSWORD='" + \ clist[0]['username'] + "', MASTER_PASSWORD='" + \
clist[0]['password'] + "', MASTER_AUTO_POSITION=1" clist[0]['password'] + "', " + "MASTER_LOG_FILE='" + mstatus[0]["File"] + \
"',MASTER_LOG_POS=" + \
str(mstatus[0]["Position"]) + \
",MASTER_USE_GTID=slave_pos,MASTER_CONNECT_RETRY=10;"
else: else:
sql = "CHANGE MASTER TO MASTER_HOST='" + ip + "', MASTER_PORT=" + port + ", MASTER_USER='" + \ sql = "CHANGE MASTER TO MASTER_HOST='" + ip + "', MASTER_PORT=" + port + ", MASTER_USER='" + \
clist[0]['username'] + "', MASTER_PASSWORD='" + \ clist[0]['username'] + "', MASTER_PASSWORD='" + \
@@ -1936,6 +1960,123 @@ def getSlaveSSHList(version=''):
return mw.getJson(data) return mw.getJson(data)
def getSlaveSyncUserByIp(version=''):
args = getArgs()
data = checkArgs(args, ['ip'])
if not data[0]:
return data[1]
ip = args['ip']
conn = pSqliteDb('slave_sync_user')
data = conn.field('ip,port,user,pass,mode,cmd').where(
"ip=?", (ip,)).select()
return mw.returnJson(True, 'ok', data)
def addSlaveSyncUser(version=''):
import base64
args = getArgs()
data = checkArgs(args, ['ip'])
if not data[0]:
return data[1]
ip = args['ip']
if ip == "":
return mw.returnJson(True, 'ok')
data = checkArgs(args, ['port', 'user', 'pass', 'mode'])
if not data[0]:
return data[1]
cmd = args['cmd']
port = args['port']
user = args['user']
apass = args['pass']
mode = args['mode']
addTime = time.strftime('%Y-%m-%d %X', time.localtime())
conn = pSqliteDb('slave_sync_user')
data = conn.field('ip').where("ip=?", (ip,)).select()
if len(data) > 0:
res = conn.where("ip=?", (ip,)).save(
'port,user,pass,mode,cmd', (port, user, apass, mode, cmd))
else:
conn.add('ip,port,user,cmd,user,pass,mode,addtime',
(ip, port, user, cmd, user, apass, mode, addTime))
return mw.returnJson(True, '设置成功!')
def delSlaveSyncUser(version=''):
args = getArgs()
data = checkArgs(args, ['ip'])
if not data[0]:
return data[1]
ip = args['ip']
conn = pSqliteDb('slave_sync_user')
conn.where("ip=?", (ip,)).delete()
return mw.returnJson(True, '删除成功!')
def getSlaveSyncUserList(version=''):
args = getArgs()
data = checkArgs(args, ['page', 'page_size'])
if not data[0]:
return data[1]
page = int(args['page'])
page_size = int(args['page_size'])
conn = pSqliteDb('slave_sync_user')
limit = str((page - 1) * page_size) + ',' + str(page_size)
field = 'id,ip,port,user,pass,cmd,addtime'
clist = conn.field(field).limit(limit).order('id desc').select()
count = conn.count()
data = {}
_page = {}
_page['count'] = count
_page['p'] = page
_page['row'] = page_size
_page['tojs'] = args['tojs']
data['page'] = mw.getPage(_page)
data['data'] = clist
return mw.getJson(data)
def getSyncModeFile():
return getServerDir() + "/sync.mode"
def getSlaveSyncMode(version):
sync_mode = getSyncModeFile()
if os.path.exists(sync_mode):
mode = mw.readFile(sync_mode).strip()
return mw.returnJson(True, 'ok', mode)
return mw.returnJson(False, 'fail')
def setSlaveSyncMode(version):
args = getArgs()
data = checkArgs(args, ['mode'])
if not data[0]:
return data[1]
mode = args['mode']
sync_mode = getSyncModeFile()
if mode == 'none':
os.remove(sync_mode)
else:
mw.writeFile(sync_mode, mode)
return mw.returnJson(True, '设置成功', mode)
def getSlaveSSHByIp(version=''): def getSlaveSSHByIp(version=''):
args = getArgs() args = getArgs()
data = checkArgs(args, ['ip']) data = checkArgs(args, ['ip'])
@@ -2013,8 +2154,6 @@ def getSlaveList(version=''):
db = pMysqlDb() db = pMysqlDb()
dlist = db.query('show slave status') dlist = db.query('show slave status')
# print(dlist)
ret = [] ret = []
for x in range(0, len(dlist)): for x in range(0, len(dlist)):
tmp = {} tmp = {}
@@ -2024,6 +2163,24 @@ def getSlaveList(version=''):
tmp['Master_Log_File'] = dlist[x]["Master_Log_File"] tmp['Master_Log_File'] = dlist[x]["Master_Log_File"]
tmp['Slave_IO_Running'] = dlist[x]["Slave_IO_Running"] tmp['Slave_IO_Running'] = dlist[x]["Slave_IO_Running"]
tmp['Slave_SQL_Running'] = dlist[x]["Slave_SQL_Running"] tmp['Slave_SQL_Running'] = dlist[x]["Slave_SQL_Running"]
tmp['Last_Error'] = dlist[x]["Last_Error"]
tmp['Last_IO_Error'] = dlist[x]["Last_IO_Error"]
tmp['Last_SQL_Error'] = dlist[x]["Last_SQL_Error"]
tmp['Slave_SQL_Running_State'] = dlist[x]["Slave_SQL_Running_State"]
tmp['Error'] = ''
if tmp['Last_Error'] != '':
tmp['Error'] = tmp['Last_Error']
if tmp['Last_IO_Error'] != '':
tmp['Error'] = tmp['Last_IO_Error']
if tmp['Last_SQL_Error'] != '':
tmp['Error'] = tmp['Last_SQL_Error']
if tmp['Error'] == '':
tmp['Error'] = tmp['Slave_SQL_Running_State']
ret.append(tmp) ret.append(tmp)
data = {} data = {}
data['data'] = ret data['data'] = ret
@@ -2040,6 +2197,47 @@ def getSlaveSyncCmd(version=''):
def initSlaveStatus(version=''): def initSlaveStatus(version=''):
mode_file = getSyncModeFile()
if not os.path.exists(mode_file):
return mw.returnJson(False, '需要先设置同步配置')
mode = mw.readFile(mode_file)
if mode == 'ssh':
return initSlaveStatusSSH(version)
if mode == 'sync-user':
return initSlaveStatusSyncUser(version)
def initSlaveStatusSyncUser(version=''):
conn = pSqliteDb('slave_sync_user')
data = conn.field('ip,port,user,pass,mode,cmd').find()
if len(data) < 1:
return mw.returnJson(False, '需要先添加同步用户配置!')
# print(data)
db = pMysqlDb()
dlist = db.query('show slave status')
if len(dlist) > 0:
return mw.returnJson(False, '已经初始化好了zz...')
u = data
mode_name = 'classic'
if u['mode'] == '1':
mode_name = 'gtid'
local_mode = recognizeDbMode()
if local_mode != mode_name:
return mw.returnJson(False, '同步模式不一致!')
# print(u['cmd'])
t = db.query(u['cmd'])
# print(t)
db.query("start slave")
return mw.returnJson(True, '初始化成功!')
def initSlaveStatusSSH(version=''):
db = pMysqlDb() db = pMysqlDb()
dlist = db.query('show slave status') dlist = db.query('show slave status')
if len(dlist) > 0: if len(dlist) > 0:
@@ -2110,6 +2308,41 @@ def initSlaveStatus(version=''):
def setSlaveStatus(version=''): def setSlaveStatus(version=''):
mode_file = getSyncModeFile()
if not os.path.exists(mode_file):
return mw.returnJson(False, '需要先设置同步配置')
mode = mw.readFile(mode_file)
if mode == 'ssh':
return setSlaveStatusSSH(version)
if mode == 'sync-user':
return setSlaveStatusSyncUser(version)
def setSlaveStatusSyncUser(version=''):
db = pMysqlDb()
dlist = db.query('show slave status')
if len(dlist) == 0:
return mw.returnJson(False, '需要手动添加同步账户或者执行初始化!')
if len(dlist) > 0 and (dlist[0]["Slave_IO_Running"] == 'Yes' or dlist[0]["Slave_SQL_Running"] == 'Yes'):
db.query('stop slave')
else:
ip = dlist[0]['Master_Host']
conn = pSqliteDb('slave_sync_user')
data = conn.field('ip,port,user,pass,mode,cmd').find()
if len(data) == 0:
return mw.returnJson(False, '没有数据无法重启!')
user = data['user']
apass = data['pass']
db.query("start slave")
# db.query("start slave user='{}' password='{}';".format(user, apass))
return mw.returnJson(True, '设置成功!')
def setSlaveStatusSSH(version=''):
db = pMysqlDb() db = pMysqlDb()
dlist = db.query('show slave status') dlist = db.query('show slave status')
@@ -2178,6 +2411,61 @@ def writeDbSyncStatus(data):
def doFullSync(version=''): def doFullSync(version=''):
mode_file = getSyncModeFile()
if not os.path.exists(mode_file):
return mw.returnJson(False, '需要先设置同步配置')
mode = mw.readFile(mode_file)
if mode == 'ssh':
return doFullSyncSSH(version)
if mode == 'sync-user':
return doFullSyncUser(version)
def doFullSyncUser(version=''):
args = getArgs()
data = checkArgs(args, ['db'])
if not data[0]:
return data[1]
sync_db = args['db']
db = pMysqlDb()
conn = pSqliteDb('slave_sync_user')
data = conn.field('ip,port,user,pass,mode,cmd').find()
user = data['user']
apass = data['pass']
port = data['port']
ip = data['ip']
bak_file = '/tmp/tmp.sql'
writeDbSyncStatus({'code': 0, 'msg': '开始同步...', 'progress': 0})
writeDbSyncStatus({'code': 1, 'msg': '远程导出数据...', 'progress': 20})
if not os.path.exists(bak_file):
dump_sql_data = getServerDir() + "/bin/mysqldump --force --opt --default-character-set=utf8 --single-transaction -h" + ip + " -P" + \
port + " -u" + user + " -p" + apass + " " + sync_db + " > " + bak_file
mw.execShell(dump_sql_data)
writeDbSyncStatus({'code': 2, 'msg': '本地导入数据...', 'progress': 40})
if os.path.exists(bak_file):
pwd = pSqliteDb('config').where('id=?', (1,)).getField('mysql_root')
sock = getSocketFile()
my_import_cmd = getServerDir() + '/bin/mysql -S ' + sock + ' -uroot -p' + pwd + \
' ' + sync_db + ' < ' + bak_file
mw.execShell(my_import_cmd)
db.query("start slave")
writeDbSyncStatus({'code': 6, 'msg': '从库重启完成...', 'progress': 100})
os.system("rm -rf " + bak_file)
return True
def doFullSyncSSH(version=''):
args = getArgs() args = getArgs()
data = checkArgs(args, ['db']) data = checkArgs(args, ['db'])
@@ -2483,6 +2771,18 @@ if __name__ == "__main__":
print(delSlaveSSH(version)) print(delSlaveSSH(version))
elif func == 'update_slave_ssh': elif func == 'update_slave_ssh':
print(updateSlaveSSH(version)) print(updateSlaveSSH(version))
elif func == 'get_slave_sync_user_list':
print(getSlaveSyncUserList(version))
elif func == 'get_slave_sync_user_by_ip':
print(getSlaveSyncUserByIp(version))
elif func == 'add_slave_sync_user':
print(addSlaveSyncUser(version))
elif func == 'del_slave_sync_user':
print(delSlaveSyncUser(version))
elif func == 'get_slave_sync_mode':
print(getSlaveSyncMode(version))
elif func == 'set_slave_sync_mode':
print(setSlaveSyncMode(version))
elif func == 'init_slave_status': elif func == 'init_slave_status':
print(initSlaveStatus(version)) print(initSlaveStatus(version))
elif func == 'set_slave_status': elif func == 'set_slave_status':
+1 -1
View File
@@ -8,7 +8,7 @@
"uninstall_pre_inspection":true, "uninstall_pre_inspection":true,
"checks": "server/mariadb", "checks": "server/mariadb",
"path": "server/mariadb", "path": "server/mariadb",
"versions":["10.6","10.7","10.8","10.9"], "versions":["10.6","10.7","10.8","10.9","10.11","11.0"],
"shell":"install.sh", "shell":"install.sh",
"checks":"server/mariadb", "checks":"server/mariadb",
"path":"server/mariadb", "path":"server/mariadb",
+275 -11
View File
@@ -39,7 +39,7 @@ function myPostN(method,args,callback, title){
if (typeof(title) != 'undefined'){ if (typeof(title) != 'undefined'){
_title = title; _title = title;
} }
$.post('/plugins/run', {name:'mysql', func:method, args:_args}, function(data) { $.post('/plugins/run', {name:'mariadb', func:method, args:_args}, function(data) {
if(typeof(callback) == 'function'){ if(typeof(callback) == 'function'){
callback(data); callback(data);
} }
@@ -655,8 +655,8 @@ function addDatabase(type){
<span class='tname'>数据库名</span>\ <span class='tname'>数据库名</span>\
<div class='info-r'><input name='name' class='bt-input-text mr5' placeholder='新的数据库名称' type='text' style='width:65%' value=''>\ <div class='info-r'><input name='name' class='bt-input-text mr5' placeholder='新的数据库名称' type='text' style='width:65%' value=''>\
<select class='bt-input-text mr5 codeing_a5nGsm' name='codeing' style='width:27%'>\ <select class='bt-input-text mr5 codeing_a5nGsm' name='codeing' style='width:27%'>\
<option value='utf8'>utf-8</option>\
<option value='utf8mb4'>utf8mb4</option>\ <option value='utf8mb4'>utf8mb4</option>\
<option value='utf8'>utf-8</option>\
<option value='gbk'>gbk</option>\ <option value='gbk'>gbk</option>\
<option value='big5'>big5</option>\ <option value='big5'>big5</option>\
</select>\ </select>\
@@ -797,7 +797,7 @@ function openPhpmyadmin(name,username,password){
data = syncPost('/plugins/run',{'name':'phpmyadmin','func':'get_cfg'}); data = syncPost('/plugins/run',{'name':'phpmyadmin','func':'get_cfg'});
var rdata = $.parseJSON(data.data); var rdata = $.parseJSON(data.data);
if (rdata.choose != 'mariadb'){ if (rdata.choose != 'mariadb'){
layer.msg('当前为['+rdata.choose+']模式,若要使用请切换模式.',{icon:2,shade: [0.3, '#000']}); layer.msg('当前为['+rdata.choose+']模式,若要使用请修改phpMyAdmin访问切换.',{icon:2,shade: [0.3, '#000']});
return; return;
} }
var phpmyadmin_cfg = rdata; var phpmyadmin_cfg = rdata;
@@ -1185,7 +1185,7 @@ function myLogs(){
<span class="f14 c6 mr20">二进制日志 </span><span class="f14 c6 mr20">' + toSize(rdata.msg) + '</span>\ <span class="f14 c6 mr20">二进制日志 </span><span class="f14 c6 mr20">' + toSize(rdata.msg) + '</span>\
'+line_status+'\ '+line_status+'\
<p class="f14 c6 mtb10" style="border-top:#ddd 1px solid; padding:10px 0">错误日志<button class="btn btn-default btn-clear btn-xs" style="float:right;" >清理日志</button></p>\ <p class="f14 c6 mtb10" style="border-top:#ddd 1px solid; padding:10px 0">错误日志<button class="btn btn-default btn-clear btn-xs" style="float:right;" >清理日志</button></p>\
<textarea readonly style="margin: 0px;width: 100%;height: 440px;background-color: #333;color:#fff; padding:0 5px" id="error_log"></textarea>\ <textarea readonly style="margin: 0px;width: 100%;height: 438px;background-color: #333;color:#fff; padding:0 5px" id="error_log"></textarea>\
</p>'; </p>';
$(".soft-man-con").html(limitCon); $(".soft-man-con").html(limitCon);
@@ -1504,7 +1504,7 @@ function getMasterRepSlaveUserCmd(username, db=''){
title: '同步命令', title: '同步命令',
area: '500px', area: '500px',
content:"<form class='bt-form pd20 pb70' id='add_master'>\ content:"<form class='bt-form pd20 pb70' id='add_master'>\
<div class='line'>"+cmd+"</div>\ <div class='line' style='word-wrap: break-word;word-break: normal;'>"+cmd+"</div>\
<div class='bt-form-submit-btn'>\ <div class='bt-form-submit-btn'>\
<button type='button' class='btn btn-success btn-sm btn-title class-copy-cmd'>复制</button>\ <button type='button' class='btn btn-success btn-sm btn-title class-copy-cmd'>复制</button>\
</div>\ </div>\
@@ -1795,11 +1795,27 @@ function addSlaveSSH(ip=''){
} }
function delSlaveSSH(ip){ function delSlaveSSH(ip){
myPost('del_slave_ssh', {ip:ip}, function(rdata){ myPost('del_slave_ssh', {ip:ip}, function(rdata){
var rdata = $.parseJSON(rdata.data); var rdata = $.parseJSON(rdata.data);
layer.msg(rdata.msg, {icon: rdata.status ? 1 : 2}); showMsg(rdata.msg,function(){
getSlaveSSHPage(); if (rdata.status){
getSlaveSSHPage();
}
},{icon: rdata.status ? 1 : 2}, 600);
});
}
function delSlaveSyncUser(ip){
myPost('del_slave_sync_user', {ip:ip}, function(rdata){
var rdata = $.parseJSON(rdata.data);
showMsg(rdata.msg,function(){
if (rdata.status){
getSlaveSyncUserPage();
}
},{icon: rdata.status ? 1 : 2}, 600);
}); });
} }
@@ -1848,6 +1864,234 @@ function getSlaveSSHPage(page=1){
}); });
} }
function addSlaveSyncUser(ip=''){
myPost('get_slave_sync_user_by_ip', {ip:ip}, function(rdata){
var rdata = $.parseJSON(rdata.data);
var ip = '127.0.0.1';
var port = "22";
var cmd = '';
var user = 'input_sync_user';
var pass = 'input_sync_pwd';
var mode = '0';
if (rdata.data.length>0){
ip = rdata.data[0]['ip'];
port = rdata.data[0]['port'];
cmd = rdata.data[0]['cmd'];
user = rdata.data[0]['user'];
pass = rdata.data[0]['pass'];
mode = rdata.data[0]['mode'];
}
var index = layer.open({
type: 1,
area: ['500px','470px'],
title: '同步账户',
closeBtn: 1,
shift: 5,
shadeClose: true,
btn:["确认","取消"],
content: "<form class='bt-form pd20'>\
<div class='line'><span class='tname'>IP</span><div class='info-r'><input name='ip' class='bt-input-text mr5' type='text' style='width:330px;' value='"+ip+"'></div></div>\
<div class='line'><span class='tname'>端口</span><div class='info-r'><input name='port' class='bt-input-text mr5' type='number' style='width:330px;' value='"+port+"'></div></div>\
<div class='line'><span class='tname'>同步账户</span><div class='info-r'><input name='user' class='bt-input-text mr5' type='text' style='width:330px;' value='"+user+"'></div></div>\
<div class='line'><span class='tname'>同步密码</span><div class='info-r'><input name='pass' class='bt-input-text mr5' type='text' style='width:330px;' value='"+pass+"'></div></div>\
<div class='line'>\
<span class='tname'>CMD[必填]</span>\
<div class='info-r'><textarea class='bt-input-text mr5' row='20' cols='30' name='cmd' style='width:330px;height:150px;'></textarea></div>\
</div>\
<input type='hidden' name='mode' value='"+mode+"' />\
</form>",
success:function(){
$('textarea[name="cmd"]').html(cmd);
$('textarea[name="cmd"]').change(function(){
var val = $(this).val();
var vlist = val.split(',');
var a = {};
for (var i in vlist) {
var tmp = toTrim(vlist[i]);
var tmp_a = tmp.split(" ");
var real_tmp = tmp_a[tmp_a.length-1];
var kv = real_tmp.split("=");
a[kv[0]] = kv[1].replace("'",'').replace("'",'');
}
$('input[name="ip"]').val(a['MASTER_HOST']);
$('input[name="port"]').val(a['MASTER_PORT']);
$('input[name="user"]').val(a['MASTER_USER']);
$('input[name="pass"]').val(a['MASTER_PASSWORD']);
console.log(a['MASTER_USE_GTID'],typeof(a['MASTER_USE_GTID']));
if (typeof(a['MASTER_USE_GTID']) != 'undefined' ){
$('input[name="mode"]').val('1');
}
});
},
yes:function(index){
var ip = $('input[name="ip"]').val();
var port = $('input[name="port"]').val();
var user = $('input[name="user"]').val();
var pass = $('input[name="pass"]').val();
var cmd = $('textarea[name="cmd"]').val();
var mode = $('input[name="mode"]').val();
var data = {ip:ip,port:port,cmd:cmd,user:user,pass:pass,mode:mode};
myPost('add_slave_sync_user', data, function(ret_data){
layer.close(index);
var rdata = $.parseJSON(ret_data.data);
showMsg(rdata.msg,function(){
if (rdata.status){
getSlaveSyncUserPage();
}
},{icon: rdata.status ? 1 : 2},600);
});
}
});
});
}
function getSlaveSyncUserPage(page=1){
var _data = {};
_data['page'] = page;
_data['page_size'] = 5;
_data['tojs'] ='getSlaveSyncUserPage';
myPost('get_slave_sync_user_list', _data, function(data){
var layerId = null;
var rdata = [];
try {
rdata = $.parseJSON(data.data);
} catch(e) {
console.log(e);
}
var list = '';
var user_list = rdata['data'];
for (i in user_list) {
var ip = user_list[i]['ip'];
var port = user_list[i]['port'];
var user = user_list[i]['user'];
var apass = user_list[i]['pass'];
var cmd = '未设置';
if (user_list[i]['cmd']!=''){
cmd = '已设置';
}
list += '<tr><td>'+ip+'</td>\
<td>'+port+'</td>\
<td>'+user+'</td>\
<td>'+apass+'</td>\
<td>'+cmd+'</td>\
<td>\
<a class="btlink" onclick="addSlaveSyncUser(\''+ip+'\');">修改</a> | \
<a class="btlink" onclick="delSlaveSyncUser(\''+ip+'\');">删除</a>\
</td>\
</tr>';
}
$('.get-slave-ssh-list tbody').html(list);
$('.dataTables_paginate_4').html(rdata['page']);
});
}
function getSlaveCfg(){
myPost('get_slave_sync_mode', '', function(data){
var rdata = $.parseJSON(data.data);
var mode_none = 'success';
var mode_ssh = 'danger';
var mode_sync_user = 'danger';
if(rdata.status){
var mode_none = 'danger';
if (rdata.data == 'ssh'){
var mode_ssh = 'success';
var mode_sync_user = 'danger';
} else {
var mode_ssh = 'danger';
var mode_sync_user = 'success';
}
}
layerId = layer.open({
type: 1,
title: '同步配置',
area: ['400px','180px'],
content:"<div class='bt-form pd20 c6'>\
<p class='conf_p'>\
<span class='f14 c6 mr20'>当前从库同步模式</span>\
<b class='f14 c6 mr20'></b>\
<button class='btn btn-"+mode_none+" btn-xs slave-db-mode btn-none'>无</button>\
<button class='btn btn-"+mode_ssh+" btn-xs slave-db-mode btn-ssh'>SSH</button>\
<button class='btn btn-"+mode_sync_user+" btn-xs slave-db-mode btn-sync-user'>同步账户</button>\
</p>\
<hr />\
<p class='conf_p'>\
<span class='f14 c6 mr20'>配置设置</span>\
<b class='f14 c6 mr20'></b>\
<button class='btn btn-success btn-xs btn-slave-ssh'>SSH</button>\
<button class='btn btn-success btn-xs btn-slave-user'>同步账户</button>\
</p>\
</div>",
success:function(){
$('.btn-slave-ssh').click(function(){
getSlaveSSHList();
});
$('.btn-slave-user').click(function(){
getSlaveUserList();
});
$('.slave-db-mode').click(function(){
var _this = this;
var mode = 'none';
if ($(this).hasClass('btn-ssh')){
mode = 'ssh';
}
if ($(this).hasClass('btn-sync-user')){
mode = 'sync-user';
}
myPost('set_slave_sync_mode', {mode:mode}, function(data){
var rdata = $.parseJSON(data.data);
showMsg(rdata.msg,function(){
$('.slave-db-mode').remove('btn-success').addClass('btn-danger');
$(_this).removeClass('btn-danger').addClass('btn-success');
},{icon:rdata.status?1:2},2000);
});
});
}
});
});
}
function getSlaveUserList(){
var page = '<div class="dataTables_paginate_4 dataTables_paginate paging_bootstrap page" style="margin-top:0px;"></div>';
page += '<div class="table_toolbar" style="left:0px;"><span class="sync btn btn-default btn-sm" onclick="addSlaveSyncUser()" title="">添加同步账户</span></div>';
layerId = layer.open({
type: 1,
title: '同步账户列表',
area: '500px',
content:"<div class='bt-form pd20 c6'>\
<div class='divtable mtb10'>\
<div><table class='table table-hover get-slave-ssh-list'>\
<thead><tr><th>IP</th><th>PORT</th><th>同步账户</th><th>同步密码</th><th>CMD</th><th>操作</th></tr></thead>\
<tbody></tbody>\
</table></div>\
"+page +"\
</div>\
</div>",
success:function(){
getSlaveSyncUserPage(1);
}
});
}
function getSlaveSSHList(page=1){ function getSlaveSSHList(page=1){
@@ -1968,7 +2212,7 @@ function masterOrSlaveConf(version=''){
for(i in rdata.data){ for(i in rdata.data){
var v = rdata.data[i]; var v = rdata.data[i];
var status = "异常"; var status = "<a class='btlink db_error'>异常</>";
if (v['Slave_SQL_Running'] == 'Yes' && v['Slave_IO_Running'] == 'Yes'){ if (v['Slave_SQL_Running'] == 'Yes' && v['Slave_IO_Running'] == 'Yes'){
status = "正常"; status = "正常";
} }
@@ -2010,6 +2254,26 @@ function masterOrSlaveConf(version=''){
// <span class="sync btn btn-default btn-sm" onclick="getMasterRepSlaveList()" title="">添加</span>\ // <span class="sync btn btn-default btn-sm" onclick="getMasterRepSlaveList()" title="">添加</span>\
// </div> // </div>
$(".table_slave_status_list").html(con); $(".table_slave_status_list").html(con);
$('.db_error').click(function(){
layer.open({
type: 1,
title: '同步异常信息',
area: '500px',
content:"<form class='bt-form pd20 pb70'>\
<div class='line'>"+v['Error']+"</div>\
<div class='bt-form-submit-btn'>\
<button type='button' class='btn btn-success btn-sm btn-title class-copy-db-err'>复制</button>\
</div>\
</form>",
success:function(){
copyText(v['Error']);
$('.class-copy-db-err').click(function(){
copyText(v['Error']);
});
}
});
});
}); });
} }
@@ -2087,7 +2351,7 @@ function masterOrSlaveConf(version=''){
<p class="conf_p">\ <p class="conf_p">\
<span class="f14 c6 mr20">Slave[从]配置</span><span class="f14 c6 mr20"></span>\ <span class="f14 c6 mr20">Slave[从]配置</span><span class="f14 c6 mr20"></span>\
<button class="btn '+(!rdata.slave_status ? 'btn-danger' : 'btn-success')+' btn-xs btn-slave">'+(!rdata.slave_status ? '未启动' : '已启动') +'</button>\ <button class="btn '+(!rdata.slave_status ? 'btn-danger' : 'btn-success')+' btn-xs btn-slave">'+(!rdata.slave_status ? '未启动' : '已启动') +'</button>\
<button class="btn btn-success btn-xs" onclick="getSlaveSSHList()" >[主]SSH配置</button>\ <button class="btn btn-success btn-xs" onclick="getSlaveCfg()" >同步配置</button>\
<button class="btn btn-success btn-xs" onclick="initSlaveStatus()" >初始化</button>\ <button class="btn btn-success btn-xs" onclick="initSlaveStatus()" >初始化</button>\
</p>\ </p>\
<hr/>\ <hr/>\
@@ -2161,10 +2425,10 @@ function masterOrSlaveConf(version=''){
getMasterDbList(); getMasterDbList();
} }
if (rdata.slave_status){ // if (rdata.slave_status){
getAsyncMasterDbList(); getAsyncMasterDbList();
getAsyncDataList() getAsyncDataList()
} // }
}); });
} }
getMasterStatus(); getMasterStatus();
+119
View File
@@ -0,0 +1,119 @@
#!/bin/bash
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH
#https://dev.mysql.com/downloads/mysql/5.5.html#downloads
#https://dev.mysql.com/downloads/file/?id=480541
curPath=`pwd`
rootPath=$(dirname "$curPath")
rootPath=$(dirname "$rootPath")
serverPath=$(dirname "$rootPath")
sysName=`uname`
install_tmp=${rootPath}/tmp/mw_install.pl
mariadbDir=${serverPath}/source/mariadb
MY_VER=10.11.2
Install_app()
{
mkdir -p ${mariadbDir}
echo '正在安装脚本文件...' > $install_tmp
if id mysql &> /dev/null ;then
echo "mysql uid is `id -u www`"
echo "mysql shell is `grep "^www:" /etc/passwd |cut -d':' -f7 `"
else
groupadd mysql
useradd -g mysql mysql
fi
if [ "$sysName" != "Darwin" ];then
mkdir -p /var/log/mariadb
touch /var/log/mariadb/mariadb.log
fi
# ----- cpu start ------
if [ -z "${cpuCore}" ]; then
cpuCore="1"
fi
if [ -f /proc/cpuinfo ];then
cpuCore=`cat /proc/cpuinfo | grep "processor" | wc -l`
fi
MEM_INFO=$(free -m|grep Mem|awk '{printf("%.f",($2)/1024)}')
if [ "${cpuCore}" != "1" ] && [ "${MEM_INFO}" != "0" ];then
if [ "${cpuCore}" -gt "${MEM_INFO}" ];then
cpuCore="${MEM_INFO}"
fi
else
cpuCore="1"
fi
if [ "$cpuCore" -gt "2" ];then
cpuCore=`echo "$cpuCore" | awk '{printf("%.f",($1)*0.8)}'`
else
cpuCore="1"
fi
# ----- cpu end ------
# if [ ! -f ${mariadbDir}/mariadb-${MY_VER}.tar.gz ];then
# wget --no-check-certificate -O ${mariadbDir}/mariadb-${MY_VER}.tar.gz --tries=3 https://mirrors.aliyun.com/mariadb/mariadb-${MY_VER}/source/mariadb-${MY_VER}.tar.gz
# fi
# https://downloads.mariadb.org/interstitial/mariadb-10.9.1/source/mariadb-10.9.1.tar.gz
if [ ! -f ${mariadbDir}/mariadb-${MY_VER}.tar.gz ];then
wget --no-check-certificate -O ${mariadbDir}/mariadb-${MY_VER}.tar.gz --tries=3 https://archive.mariadb.org/mariadb-${MY_VER}/source/mariadb-${MY_VER}.tar.gz
fi
if [ ! -d ${mariadbDir}/mariadb-${MY_VER} ];then
cd ${mariadbDir} && tar -zxvf ${mariadbDir}/mariadb-${MY_VER}.tar.gz
fi
if [ ! -d $serverPath/mariadb ];then
cd ${mariadbDir}/mariadb-${MY_VER} && cmake \
-DCMAKE_INSTALL_PREFIX=$serverPath/mariadb \
-DMYSQL_DATADIR=$serverPath/mariadb/data/ \
-DMYSQL_USER=mysql \
-DMYSQL_UNIX_ADDR=$serverPath/mariadb/mysql.sock \
-DWITH_MYISAM_STORAGE_ENGINE=1 \
-DWITH_INNOBASE_STORAGE_ENGINE=1 \
-DWITH_MEMORY_STORAGE_ENGINE=1 \
-DENABLED_LOCAL_INFILE=1 \
-DWITH_PARTITION_STORAGE_ENGINE=1 \
-DEXTRA_CHARSETS=all \
-DDEFAULT_CHARSET=utf8mb4 \
-DDEFAULT_COLLATION=utf8mb4_general_ci \
-DCMAKE_C_COMPILER=/usr/bin/gcc \
-DCMAKE_CXX_COMPILER=/usr/bin/g++
make -j${cpuCore} && make install && make clean
if [ -d $serverPath/mariadb ];then
echo '10.11' > $serverPath/mariadb/version.pl
echo '安装完成' > $install_tmp
else
echo '安装失败' > $install_tmp
echo 'install fail'>&2
exit 1
fi
fi
rm -rf ${mariadbDir}/mariadb-${MY_VER}
rm -rf ${mariadbDir}/mariadb-${MY_VER}.tar.gz
}
Uninstall_app()
{
rm -rf $serverPath/mariadb
echo '卸载完成' > $install_tmp
}
action=$1
if [ "${1}" == 'install' ];then
Install_app
else
Uninstall_app
fi
+1 -1
View File
@@ -13,7 +13,7 @@ sysName=`uname`
install_tmp=${rootPath}/tmp/mw_install.pl install_tmp=${rootPath}/tmp/mw_install.pl
mariadbDir=${serverPath}/source/mariadb mariadbDir=${serverPath}/source/mariadb
MY_VER=10.6.9 MY_VER=10.6.12
Install_app() Install_app()
{ {
+1 -1
View File
@@ -14,7 +14,7 @@ sysName=`uname`
install_tmp=${rootPath}/tmp/mw_install.pl install_tmp=${rootPath}/tmp/mw_install.pl
mariadbDir=${serverPath}/source/mariadb mariadbDir=${serverPath}/source/mariadb
MY_VER=10.7.4 MY_VER=10.7.8
Install_app() Install_app()
{ {
+1 -1
View File
@@ -14,7 +14,7 @@ sysName=`uname`
install_tmp=${rootPath}/tmp/mw_install.pl install_tmp=${rootPath}/tmp/mw_install.pl
mariadbDir=${serverPath}/source/mariadb mariadbDir=${serverPath}/source/mariadb
MY_VER=10.8.5 MY_VER=10.8.7
Install_app() Install_app()
{ {
+1 -1
View File
@@ -14,7 +14,7 @@ sysName=`uname`
install_tmp=${rootPath}/tmp/mw_install.pl install_tmp=${rootPath}/tmp/mw_install.pl
mariadbDir=${serverPath}/source/mariadb mariadbDir=${serverPath}/source/mariadb
MY_VER=10.9.2 MY_VER=10.9.5
Install_app() Install_app()
{ {
+119
View File
@@ -0,0 +1,119 @@
#!/bin/bash
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH
#https://dev.mysql.com/downloads/mysql/5.5.html#downloads
#https://dev.mysql.com/downloads/file/?id=480541
curPath=`pwd`
rootPath=$(dirname "$curPath")
rootPath=$(dirname "$rootPath")
serverPath=$(dirname "$rootPath")
sysName=`uname`
install_tmp=${rootPath}/tmp/mw_install.pl
mariadbDir=${serverPath}/source/mariadb
MY_VER=11.0.1
Install_app()
{
mkdir -p ${mariadbDir}
echo '正在安装脚本文件...' > $install_tmp
if id mysql &> /dev/null ;then
echo "mysql uid is `id -u www`"
echo "mysql shell is `grep "^www:" /etc/passwd |cut -d':' -f7 `"
else
groupadd mysql
useradd -g mysql mysql
fi
if [ "$sysName" != "Darwin" ];then
mkdir -p /var/log/mariadb
touch /var/log/mariadb/mariadb.log
fi
# ----- cpu start ------
if [ -z "${cpuCore}" ]; then
cpuCore="1"
fi
if [ -f /proc/cpuinfo ];then
cpuCore=`cat /proc/cpuinfo | grep "processor" | wc -l`
fi
MEM_INFO=$(free -m|grep Mem|awk '{printf("%.f",($2)/1024)}')
if [ "${cpuCore}" != "1" ] && [ "${MEM_INFO}" != "0" ];then
if [ "${cpuCore}" -gt "${MEM_INFO}" ];then
cpuCore="${MEM_INFO}"
fi
else
cpuCore="1"
fi
if [ "$cpuCore" -gt "2" ];then
cpuCore=`echo "$cpuCore" | awk '{printf("%.f",($1)*0.8)}'`
else
cpuCore="1"
fi
# ----- cpu end ------
# if [ ! -f ${mariadbDir}/mariadb-${MY_VER}.tar.gz ];then
# wget --no-check-certificate -O ${mariadbDir}/mariadb-${MY_VER}.tar.gz --tries=3 https://mirrors.aliyun.com/mariadb/mariadb-${MY_VER}/source/mariadb-${MY_VER}.tar.gz
# fi
# https://downloads.mariadb.org/interstitial/mariadb-10.9.1/source/mariadb-10.9.1.tar.gz
if [ ! -f ${mariadbDir}/mariadb-${MY_VER}.tar.gz ];then
wget --no-check-certificate -O ${mariadbDir}/mariadb-${MY_VER}.tar.gz --tries=3 https://archive.mariadb.org/mariadb-${MY_VER}/source/mariadb-${MY_VER}.tar.gz
fi
if [ ! -d ${mariadbDir}/mariadb-${MY_VER} ];then
cd ${mariadbDir} && tar -zxvf ${mariadbDir}/mariadb-${MY_VER}.tar.gz
fi
if [ ! -d $serverPath/mariadb ];then
cd ${mariadbDir}/mariadb-${MY_VER} && cmake \
-DCMAKE_INSTALL_PREFIX=$serverPath/mariadb \
-DMYSQL_DATADIR=$serverPath/mariadb/data/ \
-DMYSQL_USER=mysql \
-DMYSQL_UNIX_ADDR=$serverPath/mariadb/mysql.sock \
-DWITH_MYISAM_STORAGE_ENGINE=1 \
-DWITH_INNOBASE_STORAGE_ENGINE=1 \
-DWITH_MEMORY_STORAGE_ENGINE=1 \
-DENABLED_LOCAL_INFILE=1 \
-DWITH_PARTITION_STORAGE_ENGINE=1 \
-DEXTRA_CHARSETS=all \
-DDEFAULT_CHARSET=utf8mb4 \
-DDEFAULT_COLLATION=utf8mb4_general_ci \
-DCMAKE_C_COMPILER=/usr/bin/gcc \
-DCMAKE_CXX_COMPILER=/usr/bin/g++
make -j${cpuCore} && make install && make clean
if [ -d $serverPath/mariadb ];then
echo '10.11' > $serverPath/mariadb/version.pl
echo '安装完成' > $install_tmp
else
echo '安装失败' > $install_tmp
echo 'install fail'>&2
exit 1
fi
fi
rm -rf ${mariadbDir}/mariadb-${MY_VER}
rm -rf ${mariadbDir}/mariadb-${MY_VER}.tar.gz
}
Uninstall_app()
{
rm -rf $serverPath/mariadb
echo '卸载完成' > $install_tmp
}
action=$1
if [ "${1}" == 'install' ];then
Install_app
else
Uninstall_app
fi
+5 -2
View File
@@ -62,13 +62,16 @@ def getArgs():
if args_len == 1: if args_len == 1:
t = args[0].strip('{').strip('}') t = args[0].strip('{').strip('}')
t = t.split(':') if t.strip() == '':
tmp = []
else:
t = t.split(':')
tmp[t[0]] = t[1]
tmp[t[0]] = t[1] tmp[t[0]] = t[1]
elif args_len > 1: elif args_len > 1:
for i in range(len(args)): for i in range(len(args)):
t = args[i].split(':') t = args[i].split(':')
tmp[t[0]] = t[1] tmp[t[0]] = t[1]
return tmp return tmp
Binary file not shown.

After

Width:  |  Height:  |  Size: 478 B

+292
View File
@@ -0,0 +1,292 @@
<style>
.migration_content {
position: relative;
height: 450px;
}
.step_head::after {
background-color: #ddd;
border-top: 2px solid #ccc;
content: "";
display: block;
height: 3px;
left: 84px;
position: absolute;
top: 14px;
width: 500px;
z-index: -1;
}
.step_head {
margin: 20px 0;
}
.step_head ul li {
width: 24.5%;
display: inline-block;
}
.step_head ul li span {
width: 30px;
height: 30px;
line-height: 30px;
display: block;
border-radius: 15px;
background-color: #ddd;
color: #878787;
margin: 0 auto;
text-align: center;
font-size: 16px;
font-weight: 600;
}
.step_head ul li p {
text-align: center;
margin-top: 15px;
}
.step_head ul li.active span {
background-color: #20A53A;
color: #fff;
}
.step_content {
text-align: center;
}
.boxHide {
display: none
}
.step_content .psync_info input {
width: 300px;
}
.step_content .psync_info .panel_setp_span {
height: 32px;
line-height: 32px;
overflow: hidden;
padding-right: 20px;
text-align: right;
text-overflow: ellipsis;
white-space: nowrap;
width: 130px;
display: inline-block;
float: left;
}
.step_content .psync_info .mtb20{
padding-left: 40px;
text-align: left;
}
.psync_path .table {
text-align: left;
}
.psync_path .table > tbody > tr > td:nth-child(2n),
.psync_path .table > thead > tr > th:nth-child(2n),
.terlist .table > thead > tr > th:nth-child(2n),
.terlist .table > tbody > tr > td:nth-child(2n),
.terlist .table > tbody > tr > td:nth-child(1),
.terlist .table > thead > tr > th:nth-child(1) {
border-right: #ddd 1px solid;
}
.checkbox_conten {
background-color: #f3f3f3;
border: #ddd 1px solid;
border-radius: 4px;
padding: 10px;
margin: 0px 50px;
}
.checkbox_item span {
display: inline-block;
height: 15px;
overflow: hidden;
text-align: left;
display: block;
padding-left: 20px;
height: 20px;
line-height: 20px;
width: 152px;
text-overflow: ellipsis;
}
label.checkbox_label {
margin-left: 6px;
margin-bottom: 0;
}
label.checkbox_label span {
color: #000;
}
.psync_data {
display: none;
}
.psync_data .checkbox_item input[type="checkbox"] {
width: 15px;
height: 15px;
position: absolute;
top: 50%;
margin-top: -6px;
}
.checkbox_item label {
font-weight: normal;
white-space: nowrap;
position: relative;
}
.psync_data .checkbox_data {
margin: 0 5px 5px;
display: inline-block;
width: 166px;
vertical-align: text-top;
}
.psync_data .checkbox_data:last-child {
margin-right: 0;
}
.checkbox_item ul {
background-color: #fff;
max-height: 174px;
overflow: auto;
width: 100%;
display: block;
margin-top: 10px;
}
.checkbox_item ul li {
padding: 0 6px;
}
.psync_data .checkbox_con {
display: block
}
.progress {
background-color: #e2e2e2;
border-radius: 8px;
height: 16px;
line-height: 16px;
position: relative;
}
.progress-bar {
background-color: #5ab76c;
border-radius: 8px;
height: 16px;
max-width: 100%;
position: absolute;
text-align: right;
transition: all 0.3s ease 0s;
width: 0;
}
.progress-text {
font-size: 12px;
color: #fff;
padding: 0 10px;
position: static;
}
.qystatus {
color: #666;
margin-bottom: 10px;
margin-left: 5px;
}
.success {
padding: 50px 0 60px;
margin-left: -60px;
}
.success p {
margin-top: 20px;
font-size: 16px;
color: #666;
}
.psync_tips{
color: red;
font-size: 15px;
background-color: #fbfbfb;
border: 1px solid #eee;
line-height: 46px;
margin-bottom: 15px;
padding-left: 10px;
}
.psync_tips span{
font-size: 12px;
}
</style>
<div class="migration_api pd15">
<div class="migration_content">
<div class="step_head">
<ul>
<li class="active"><span>1</span><p>填写信息</p></li>
<li><span>2</span><p>检测环境</p></li>
<li><span>3</span><p>选择数据</p></li>
<li><span>4</span><p>一键迁移</p></li>
</ul>
</div>
<div class="step_content">
<div class="pd15 psync_info">
<div class="psync_tips">
<span class="glyphicon glyphicon-alert" style="color: #f39c12; margin-right: 10px;"></span>只需在发送数据服务器安装本软件,请填写<span>『 接收数据服务器 』</span>资料。
<a href="#" target="_blank" class="bt-ico-ask" style="cursor: pointer;">?</a>
</div>
<div class="mtb20">
<span class="panel_setp_span">接收数据的面板地址</span>
<input type="text" class="bt-input-text" name="sync_url" value="" placeholder="接收数据面板地址,如: http://127.0.0.1:8888">
</div>
<div class="mtb20">
<span class="panel_setp_span">接收数据的面板API</span>
<input type="text" class="bt-input-text" name="sync_token" value="" placeholder="接收数据面板API密钥" />
<a href="#" target="_blank" class="btlink ml5">获取API秘钥</a>
</div>
<div class="mtb20">
<span class="panel_setp_span">IP白名单</span>
<span style="height: 32px;line-height: 32px;">必须将本机器IP加入接收数据服务器API的IP白名单,<a href="#" href="javascript:;" target="_blank" class="btlink">如何添加白名单</a></span>
</div>
<div class="mtb20" style="text-align: left;margin-left: 130px;">
<button class="btn btn-success infoNext">下一步</button>
</div>
</div>
<div class="pa15 psync_path" style="margin:0 50px;"></div>
<div class="pa15 psync_data" style="text-align: left;">
<div class="checkbox_conten">
<div class="checkbox_data">
<div class="checkbox_item">
<label class="checkbox_label">
<input type="checkbox" id="sites_All" checked>
<span>网站</span>
</label>
<ul id="sites_li"></ul>
</div>
</div>
<div class="checkbox_data">
<div class="checkbox_item">
<label class="checkbox_label">
<input type="checkbox" id="db_All" checked>
<span>数据库</span>
</label>
<ul id="db_li"></ul>
</div>
</div>
</div>
<div class="line mtb20" style="margin:20px 0 0 50px">
<button class="btn btn-default btn-sm mr20 dataBack">上一步</button>
<button class="btn btn-success btn-sm dataMigrate">一键迁移</button>
</div>
</div>
<div class="pa15 psync_migrate"></div>
</div>
<hr style="margin-top: 5px;" />
<div class="step_footer" style="margin: 5px 0 0 20px;text-align: left;">
<ul class="help-info-text c7 mlr20" style="margin-top: 0px;">
<li>一键迁移过程中是后台执行可以关闭当前窗口</li>
<li>一键迁移迁移数据不涉及原来数据的增删(是将原来数据打包发送)</li>
</ul>
</div>
</div>
</div>
<script type="text/javascript">
resetPluginWinWidth(700);
resetPluginWinWidth(700);
$.getScript( "/plugins/file?name=migration_api&f=js/app.js", function(){
initStep();
});
</script>
+1090
View File
File diff suppressed because it is too large Load Diff
+18
View File
@@ -0,0 +1,18 @@
{
"sort": 7,
"ps": "[<span style='color:red;'>潜龙勿用</span>]一键迁移,仅网站数据和MySQL数据(仅支持源码安装软件)",
"name": "migration_api",
"title": "一键迁移API",
"shell": "install.sh",
"versions":["1.0"],
"updates":["1.0"],
"tip": "soft",
"checks": "server/migration_api",
"path":"server/migration_api",
"display": 1,
"author": "midoks",
"date": "2022-01-17",
"home": "https://github.com/midoks/mdserver-web",
"type": 0,
"pid": "4"
}
+31
View File
@@ -0,0 +1,31 @@
#!/bin/bash
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH
curPath=`pwd`
rootPath=$(dirname "$curPath")
rootPath=$(dirname "$rootPath")
serverPath=$(dirname "$rootPath")
install_tmp=${rootPath}/tmp/mw_install.pl
VERSION=1.0
Install_App(){
mkdir -p $serverPath/migration_api
echo "${VERSION}" > $serverPath/migration_api/version.pl
echo '正在安装脚本文件...' > $install_tmp
}
Uninstall_App()
{
rm -rf $serverPath/migration_api
}
action=$1
if [ "${1}" == 'install' ];then
Install_App
else
Uninstall_App
fi
+333
View File
@@ -0,0 +1,333 @@
function maPostNoMsg(method,args,callback){
var _args = null;
if (typeof(args) == 'string'){
_args = JSON.stringify(toArrayObject(args));
} else {
_args = JSON.stringify(args);
}
$.post('/plugins/run', {name:'migration_api', func:method, args:_args}, function(data) {
if (!data.status){
layer.msg(data.msg,{icon:0,time:2000,shade: [0.3, '#000']});
return;
}
if(typeof(callback) == 'function'){
callback(data);
}
},'json');
}
function maPost(method,args,callback, msg = '正在获取...'){
var _args = null;
if (typeof(args) == 'string'){
_args = JSON.stringify(toArrayObject(args));
} else {
_args = JSON.stringify(args);
}
var loadT = layer.msg(msg, { icon: 16, time: 0, shade: 0.3 });
$.post('/plugins/run', {name:'migration_api', func:method, args:_args}, function(data) {
layer.close(loadT);
if (!data.status){
layer.msg(data.msg,{icon:0,time:2000,shade: [0.3, '#000']});
return;
}
if(typeof(callback) == 'function'){
callback(data);
}
},'json');
}
function maAsyncPost(method,args){
var _args = null;
if (typeof(args) == 'string'){
_args = JSON.stringify(toArrayObject(args));
} else {
_args = JSON.stringify(args);
}
return syncPost('/plugins/run', {name:'migration_api', func:method, args:_args});
}
function maPostCallbak(method, args, callback){
var loadT = layer.msg('正在获取...', { icon: 16, time: 0, shade: 0.3 });
var req_data = {};
req_data['name'] = 'migration_api';
req_data['func'] = method;
args['version'] = '1.0';
if (typeof(args) == 'string'){
req_data['args'] = JSON.stringify(toArrayObject(args));
} else {
req_data['args'] = JSON.stringify(args);
}
$.post('/plugins/callback', req_data, function(data) {
layer.close(loadT);
if (!data.status){
layer.msg(data.msg,{icon:0,time:2000,shade: [0.3, '#000']});
return;
}
if(typeof(callback) == 'function'){
callback(data);
}
},'json');
}
function selectProgress(val){
$('.step_head li').removeClass('active');
$('.step_head li').each(function(){
var v = $(this).find('span').text();
if (val == v){
$(this).addClass('active');
}
});
}
function initStep1(){
var url = $('input[name="sync_url"]').val();
var token = $('input[name="sync_token"]').val();
maPost('step_one',{url:url,token:token}, function(rdata){
var rdata = $.parseJSON(rdata.data);
showMsg(rdata.msg,function(){
if (rdata.status){
initStep2();
}
},{ icon: rdata.status ? 1 : 2 });
},'API校验中...');
}
function initStep2(){
maPost('step_two',{}, function(rdata){
var rdata = $.parseJSON(rdata.data);
showMsg(rdata.msg,function(){
if (rdata.status){
selectProgress(2);
$('.psync_info').hide();
var info = rdata.data;
var body = '<div class="divtable">\
<table class="table table-hover">\
<thead>\
<tr><th style="border-right:1px solid #ddd">服务</th><th>当前服务器</th><th>远程服务器</th></tr>\
</thead>\
<tbody>\
<tr>\
<td style="border-right:1px solid #ddd">网站服务</td>\
<td>'+info['local']['webserver']+'</td>\
<td>'+info['remote']['webserver']+'</td>\
</tr>\
<tr>\
<td style="border-right:1px solid #ddd">安装MySQL</td>\
<td>'+(info['local']['mysql']?'是':'否')+'</td>\
<td>'+(info['remote']['mysql']?'是':'否')+'</td>\
</tr>\
<tr>\
<td style="border-right:1px solid #ddd">安装PHP</td>\
<td>'+(info['local']['php'].join('/'))+'</td>\
<td>'+(info['remote']['php'].join('/')) +'</td>\
</tr>\
<tr>\
<td style="border-right:1px solid #ddd">可用磁盘</td>\
<td>'+info['local']['disk']+'</td>\
<td>'+info['remote']['disk'][0]['size'][0]+'</td>\
</tr>\
</tbody>\
</table>\
</div>';
body += '<div class="line mtb20" style="text-align: left;">\
<button class="btn btn-default btn-sm mr20 pathTestting">重新检测</button>\
<button class="btn btn-default btn-sm mr20 pathBcak">上一步</button>\
<button class="btn btn-success btn-sm psync-next pathNext">下一步</button>\
</div>';
$('.psync_path').html(body);
$('.psync_path').show();
}
},{ icon: rdata.status ? 1 : 2 });
},'检测环境中...');
}
function initStep3(){
maPost('step_three',{}, function(rdata){
var rdata = $.parseJSON(rdata.data);
showMsg(rdata.msg,function(){
if (rdata.status){
selectProgress(3);
var pdata = rdata.data;
var site_li = '';
for (var i = 0; i < pdata.sites.length; i++) {
site_li+='<li>\
<label>\
<input type="checkbox" data-id="'+i+'" id="sites_'+pdata.sites[i]['name']+'" value="'+pdata.sites[i]['name']+'" name="sites" checked="">\
<span title="'+pdata.sites[i]['name']+'">'+pdata.sites[i]['name']+'</span>\
</label>\
</li>';
}
$('#sites_li').html(site_li);
var db_li = '';
for (var i = 0; i < pdata.databases.length; i++) {
db_li+='<li>\
<label>\
<input type="checkbox" data-id="'+i+'" id="sites_'+pdata.databases[i]['name']+'" value="'+pdata.databases[i]['name']+'" name="databases" checked="">\
<span title="'+pdata.databases[i]['name']+'">'+pdata.databases[i]['name']+'</span>\
</label>\
</li>';
}
$('#db_li').html(db_li);
$('.psync_path').hide();
$('.psync_data').show();
}
},{ icon: rdata.status ? 1 : 2 });
});
}
function renderMigrationProgress(){
maPostNoMsg('get_speed',{}, function(rdata){
var rdata = $.parseJSON(rdata.data);
// console.log('speed:',rdata.data);
if (rdata.status){
if (rdata['data']['action'] == 'True'){
var end = '<div class="line">\
<div class="success text-center" style="padding: 10px 0 15px;">\
<p>数据迁移完成,请务必检查数据完整性!</p>\
<p style="font-size: 14px;margin-top:2px;color: #939292;">传输大小: '+toSize(rdata['data']['total_size'])+',耗时: '+rdata['data']['total_time']+',平均速度: '+toSize(rdata['data']['speed'])+'/s</p>\
<p class="mtb15">\
<button class="btn btn-success btn-sm mr5 okBtn">确定完成</button>\
<a class="btn btn-default btn-sm" style="margin-left: 10px;" target="_blank" href="/files/download?filename='+rdata['data']['log_file']+'">迁移日志</a>\
</p>\
</div>\
</div>';
$('.psync_migrate').html(end);
} else{
$('.psync_migrate .action').text(rdata['data']['action']);
$('.psync_migrate .done').text(rdata['data']['done']);
$('.psync_migrate pre').text(rdata['data']['log']);
var p = (rdata['data']['all_speed']/rdata['data']['all_total'])*100;
if (p>100){
p = 100;
}
$('.psync_migrate .progress_info_bar').width(p+'%');
$('.psync_migrate .progress_info').text(p+'%');
renderMigrationProgress();
}
} else{
layer.msg(rdata.msg,{icon:1});
}
});
}
function initStep4(){
var site_checked = '';
$('input[name="sites"]:checked').each(function(){
site_checked += $(this).val()+',';
});
var databases_checked = '';
$('input[name="databases"]:checked').each(function(){
databases_checked+=$(this).val()+',';
});
maPost('step_four',{sites:site_checked,databases:databases_checked}, function(rdata){
var rdata = $.parseJSON(rdata.data);
selectProgress(4);
var progress = '<div style="margin: 0 40px;">\
<div class="line">\
<div style="text-align:left"><span class="action">--</span>\
<span style="margin-left: 20px;" class="done">当前: --</span><img src="/static/img/ing.gif"><a style="position: absolute;right: 40px;" class="btlink psync_close" onclick="migrate.close();">[取消]</a></div>\
<div class="bt-progress" style="border-radius:0;height:20px;line-height:19px">\
<div class="bt-progress-bar progress_info_bar" style="border-radius: 0px; height: 20px; width: 0%;">\
<span class="bt-progress-text progress_info"></span></div>\
</div>\
</div>\
</div>\
<pre style="height: 222px;text-align: left;margin:5px 38px 0;font-size: 12px;line-height: 20px;padding: 10px;background-color: #333;color: #fff;"></pre>\
</div>';
$('.psync_data').hide();
$('.psync_migrate').html(progress);
$('.psync_migrate').show();
renderMigrationProgress();
});
}
function initStep(){
maPost('get_conf',{}, function(rdata){
var rdata = $.parseJSON(rdata.data);
$('input[name="sync_url"]').val(rdata.data['url']);
$('input[name="sync_token"]').val(rdata.data['token']);
});
$('.infoNext').click(function(){
initStep1();
});
// 重新检测按钮
$('.psync_path').on('click', '.pathTestting', function () {
initStep2();
});
$('.psync_path').on('click', '.pathBcak', function(){
$('.psync_path').hide();
$('.psync_info').show();
selectProgress(1);
});
$('.psync_path').on('click', '.pathNext', function(){
initStep3();
});
$('.psync_data').on('click', '.dataBack', function(){
$('.psync_data').hide();
$('.psync_path').show();
selectProgress(2);
});
$('.psync_data').on('click', '.dataMigrate', function(){
initStep4();
});
$('#sites_All').on('click',function(){
var ch = $(this).prop('checked');
$('#sites_li input').prop('checked',ch);
});
$('#db_All').on('click',function(){
var ch = $(this).prop('checked');
$('#db_li input').prop('checked',ch);
});
$('.psync_migrate').on('click', '.okBtn', function(){
$('.psync_migrate').hide();
$('.psync_info').show();
selectProgress(1);
});
}
+2 -2
View File
@@ -104,14 +104,14 @@ rm -r /var/lib/mongodb
Install_Linux_Debian() Install_Linux_Debian()
{ {
##################### debian start ##################### ##################### debian start #####################
if [ "$SYS_VERSION_ID" -ge "11" ]; then if [ "$SYS_VERSION_ID" -eq "11" ] && [ "$VERSION" -eq "4.4" ]; then
echo "Not yet supported" echo "Not yet supported"
exit 1 exit 1
fi fi
if [ -f /usr/lib/systemd/system/mongod.service ];then if [ -f /usr/lib/systemd/system/mongod.service ];then
echo 'alreay exist!' echo 'alreay install exist!'
exit 0 exit 0
fi fi
+6 -4
View File
@@ -36,11 +36,12 @@ max_connect_errors = 100
open_files_limit = 2560 open_files_limit = 2560
max_allowed_packet = 128M max_allowed_packet = 128M
#skip-external-locking skip_name_resolve = 1
#skip-grant-tables skip-ssl
#loose-skip-innodb
#skip-networking #skip-networking
#skip-name-resolve #skip-external-locking
#loose-skip-innodb
#skip-grant-tables
log-bin=mysql-bin log-bin=mysql-bin
binlog_format=mixed binlog_format=mixed
@@ -64,6 +65,7 @@ binlog-ignore-db = performance_schema
#slave #slave
log-slave-updates log-slave-updates
#replicate-do-db #replicate-do-db
slave_skip_errors=1062
replicate-ignore-db = information_schema replicate-ignore-db = information_schema
replicate-ignore-db = performance_schema replicate-ignore-db = performance_schema
replicate-ignore-db = mysql replicate-ignore-db = mysql
+1
View File
@@ -62,6 +62,7 @@ binlog-ignore-db = performance_schema
#slave #slave
log_replica_updates log_replica_updates
#replicate-do-db #replicate-do-db
slave_skip_errors=1062
replicate-ignore-db = information_schema replicate-ignore-db = information_schema
replicate-ignore-db = performance_schema replicate-ignore-db = performance_schema
replicate-ignore-db = mysql replicate-ignore-db = mysql
+13
View File
@@ -40,4 +40,17 @@ CREATE TABLE IF NOT EXISTS `slave_id_rsa` (
`addtime` TEXT `addtime` TEXT
); );
-- 从库配置主库的[user]
-- drop table `slave_user`;
CREATE TABLE IF NOT EXISTS `slave_sync_user` (
`id` INTEGER PRIMARY KEY AUTOINCREMENT,
`ip` TEXT,
`port` TEXT,
`user` TEXT,
`pass` TEXT,
`mode` TEXT,
`cmd` TEXT,
`addtime` TEXT
);
+1
View File
@@ -13,6 +13,7 @@
<p onclick="myLogs();">日志</p> <p onclick="myLogs();">日志</p>
<p onclick="pluginLogs('mysql-apt',$('.plugin_version').attr('version'),'show_log');">慢日志</p> <p onclick="pluginLogs('mysql-apt',$('.plugin_version').attr('version'),'show_log');">慢日志</p>
<p onclick="dbList()">管理列表</p> <p onclick="dbList()">管理列表</p>
<p onclick="masterOrSlaveConf($('.plugin_version').attr('version'))">主从配置</p>
</div> </div>
<div class="bt-w-con pd15"> <div class="bt-w-con pd15">
<div class="soft-man-con"></div> <div class="soft-man-con"></div>
+357 -30
View File
@@ -122,20 +122,27 @@ def contentReplace(content):
def pSqliteDb(dbname='databases'): def pSqliteDb(dbname='databases'):
file = getServerDir() + '/mysql.db' file = getServerDir() + '/mysql.db'
name = 'mysql' name = 'mysql'
if not os.path.exists(file):
import_sql = mw.readFile(getPluginDir() + '/conf/mysql.sql')
md5_sql = mw.md5(import_sql)
import_sign = False
save_md5_file = getServerDir() + '/import_sql.md5'
if os.path.exists(save_md5_file):
save_md5_sql = mw.readFile(save_md5_file)
if save_md5_sql != md5_sql:
import_sign = True
mw.writeFile(save_md5_file, md5_sql)
else:
mw.writeFile(save_md5_file, md5_sql)
if not os.path.exists(file) or import_sql:
conn = mw.M(dbname).dbPos(getServerDir(), name) conn = mw.M(dbname).dbPos(getServerDir(), name)
csql = mw.readFile(getPluginDir() + '/conf/mysql.sql') csql_list = import_sql.split(';')
csql_list = csql.split(';')
for index in range(len(csql_list)): for index in range(len(csql_list)):
conn.execute(csql_list[index], ()) conn.execute(csql_list[index], ())
else:
# 现有run conn = mw.M(dbname).dbPos(getServerDir(), name)
# conn = mw.M(dbname).dbPos(getServerDir(), name)
# csql = mw.readFile(getPluginDir() + '/conf/mysql.sql')
# csql_list = csql.split(';')
# for index in range(len(csql_list)):
# conn.execute(csql_list[index], ())
conn = mw.M(dbname).dbPos(getServerDir(), name)
return conn return conn
@@ -154,9 +161,16 @@ def pMysqlDb():
def initDreplace(version=''): def initDreplace(version=''):
my_dir = getServerDir() + '/etc' conf_dir = getServerDir() + '/etc'
if not os.path.exists(my_dir): mode_dir = conf_dir + '/mode'
os.mkdir(my_dir)
conf_list = [
conf_dir,
mode_dir,
]
for conf in conf_list:
if not os.path.exists(conf):
os.mkdir(conf)
tmp_dir = getServerDir() + '/tmp' tmp_dir = getServerDir() + '/tmp'
if not os.path.exists(tmp_dir): if not os.path.exists(tmp_dir):
@@ -164,12 +178,26 @@ def initDreplace(version=''):
mw.execShell("chown -R mysql:mysql " + tmp_dir) mw.execShell("chown -R mysql:mysql " + tmp_dir)
mw.execShell("chmod 750 " + tmp_dir) mw.execShell("chmod 750 " + tmp_dir)
mysql_conf = my_dir + '/my.cnf' my_conf = conf_dir + '/my.cnf'
if not os.path.exists(mysql_conf): if not os.path.exists(my_conf):
mysql_conf_tpl = getPluginDir() + '/conf/my' + version + '.cnf' tpl = getPluginDir() + '/conf/my' + version + '.cnf'
content = mw.readFile(mysql_conf_tpl) content = mw.readFile(tpl)
content = contentReplace(content) content = contentReplace(content)
mw.writeFile(mysql_conf, content) mw.writeFile(my_conf, content)
classic_conf = mode_dir + '/classic.cnf'
if not os.path.exists(classic_conf):
tpl = getPluginDir() + '/conf/classic.cnf'
content = mw.readFile(tpl)
content = contentReplace(content)
mw.writeFile(classic_conf, content)
gtid_conf = mode_dir + '/gtid.cnf'
if not os.path.exists(gtid_conf):
tpl = getPluginDir() + '/conf/gtid.cnf'
content = mw.readFile(tpl)
content = contentReplace(content)
mw.writeFile(gtid_conf, content)
# systemd # systemd
systemDir = mw.systemdCfgDir() systemDir = mw.systemdCfgDir()
@@ -383,6 +411,7 @@ def my8cmd(version, method):
initDreplace(version) initDreplace(version)
# mysql 8.0 and 5.7 # mysql 8.0 and 5.7
try: try:
isInited = True
if version == '5.7': if version == '5.7':
isInited = initMysql57Data() isInited = initMysql57Data()
elif version == '8.0': elif version == '8.0':
@@ -455,7 +484,7 @@ def getMyDbPos():
return tmp.groups()[0].strip() return tmp.groups()[0].strip()
def setMyDbPos(): def setMyDbPos(version=''):
args = getArgs() args = getArgs()
data = checkArgs(args, ['datadir']) data = checkArgs(args, ['datadir'])
if not data[0]: if not data[0]:
@@ -470,7 +499,7 @@ def setMyDbPos():
mw.execShell('mkdir -p ' + t_datadir) mw.execShell('mkdir -p ' + t_datadir)
# mw.execShell('/etc/init.d/mysqld stop') # mw.execShell('/etc/init.d/mysqld stop')
stop() stop(version)
mw.execShell('cp -rf ' + s_datadir + '/* ' + t_datadir + '/') mw.execShell('cp -rf ' + s_datadir + '/* ' + t_datadir + '/')
mw.execShell('chown -R mysql mysql ' + t_datadir) mw.execShell('chown -R mysql mysql ' + t_datadir)
mw.execShell('chmod -R 755 ' + t_datadir) mw.execShell('chmod -R 755 ' + t_datadir)
@@ -484,7 +513,7 @@ def setMyDbPos():
mycnf = mycnf.replace(s_datadir, t_datadir) mycnf = mycnf.replace(s_datadir, t_datadir)
mw.writeFile(myfile, mycnf) mw.writeFile(myfile, mycnf)
start() restart(version)
result = mw.execShell( result = mw.execShell(
'ps aux|grep mysqld| grep -v grep|grep -v python') 'ps aux|grep mysqld| grep -v grep|grep -v python')
@@ -745,10 +774,13 @@ def importDbExternal():
pwd + ' ' + name + ' < ' + import_sql pwd + ' ' + name + ' < ' + import_sql
# print(mysql_cmd) # print(mysql_cmd)
os.system(mysql_cmd) rdata = mw.execShell(mysql_cmd)
if ext != 'sql': if ext != 'sql':
os.remove(import_sql) os.remove(import_sql)
if rdata[1].lower().find('error') > -1:
return mw.returnJson(False, rdata[1])
return mw.returnJson(True, 'ok') return mw.returnJson(True, 'ok')
@@ -774,7 +806,12 @@ def importDbBackup():
' ' + name + ' < ' + file_path_sql ' ' + name + ' < ' + file_path_sql
# print(mysql_cmd) # print(mysql_cmd)
os.system(mysql_cmd) # os.system(mysql_cmd)
rdata = mw.execShell(mysql_cmd)
if rdata[1].lower().find('error') > -1:
return mw.returnJson(False, rdata[1])
return mw.returnJson(True, 'ok') return mw.returnJson(True, 'ok')
@@ -1104,7 +1141,7 @@ def addDb():
dataAccess = args['dataAccess'].strip() dataAccess = args['dataAccess'].strip()
ps = args['ps'].strip() ps = args['ps'].strip()
reg = "^[\w\.-]+$" reg = "^[\w-]+$"
if not re.match(reg, args['name']): if not re.match(reg, args['name']):
return mw.returnJson(False, '数据库名称不能带有特殊符号!') return mw.returnJson(False, '数据库名称不能带有特殊符号!')
checks = ['root', 'mysql', 'test', 'sys', 'panel_logs'] checks = ['root', 'mysql', 'test', 'sys', 'panel_logs']
@@ -1772,7 +1809,7 @@ def addMasterRepSlaveUser(version=''):
# address = args['address'].strip() # address = args['address'].strip()
# dataAccess = args['dataAccess'].strip() # dataAccess = args['dataAccess'].strip()
reg = "^[\w\.-]+$" reg = "^[\w-]+$"
if not re.match(reg, username): if not re.match(reg, username):
return mw.returnJson(False, '用户名不能带有特殊符号!') return mw.returnJson(False, '用户名不能带有特殊符号!')
checks = ['root', 'mysql', 'test', 'sys', 'panel_logs'] checks = ['root', 'mysql', 'test', 'sys', 'panel_logs']
@@ -1800,17 +1837,18 @@ def addMasterRepSlaveUser(version=''):
if isError != None: if isError != None:
return isError return isError
sql = "FLUSH PRIVILEGES;"
pdb.execute(sql)
else: else:
sql = "GRANT REPLICATION SLAVE ON *.* TO '" + username + \ sql = "GRANT REPLICATION SLAVE ON *.* TO '" + username + \
"'@'%' identified by '" + password + "';" "'@'%' identified by '" + password + "';"
result = pdb.execute(sql) result = pdb.execute(sql)
result = pdb.execute('FLUSH PRIVILEGES;')
isError = isSqlError(result) isError = isSqlError(result)
if isError != None: if isError != None:
return isError return isError
sql_select = "grant select,lock tables,PROCESS on *.* to " + username + "@'%';"
pdb.execute(sql_select)
pdb.execute('FLUSH PRIVILEGES;')
addTime = time.strftime('%Y-%m-%d %X', time.localtime()) addTime = time.strftime('%Y-%m-%d %X', time.localtime())
psdb.add('username,password,accept,ps,addtime', psdb.add('username,password,accept,ps,addtime',
(username, password, '%', '', addTime)) (username, password, '%', '', addTime))
@@ -1947,6 +1985,123 @@ def getSlaveSSHList(version=''):
return mw.getJson(data) return mw.getJson(data)
def getSlaveSyncUserByIp(version=''):
args = getArgs()
data = checkArgs(args, ['ip'])
if not data[0]:
return data[1]
ip = args['ip']
conn = pSqliteDb('slave_sync_user')
data = conn.field('ip,port,user,pass,mode,cmd').where(
"ip=?", (ip,)).select()
return mw.returnJson(True, 'ok', data)
def addSlaveSyncUser(version=''):
import base64
args = getArgs()
data = checkArgs(args, ['ip'])
if not data[0]:
return data[1]
ip = args['ip']
if ip == "":
return mw.returnJson(True, 'ok')
data = checkArgs(args, ['port', 'user', 'pass', 'mode'])
if not data[0]:
return data[1]
cmd = args['cmd']
port = args['port']
user = args['user']
apass = args['pass']
mode = args['mode']
addTime = time.strftime('%Y-%m-%d %X', time.localtime())
conn = pSqliteDb('slave_sync_user')
data = conn.field('ip').where("ip=?", (ip,)).select()
if len(data) > 0:
res = conn.where("ip=?", (ip,)).save(
'port,user,pass,mode,cmd', (port, user, apass, mode, cmd))
else:
conn.add('ip,port,user,cmd,user,pass,mode,addtime',
(ip, port, user, cmd, user, apass, mode, addTime))
return mw.returnJson(True, '设置成功!')
def delSlaveSyncUser(version=''):
args = getArgs()
data = checkArgs(args, ['ip'])
if not data[0]:
return data[1]
ip = args['ip']
conn = pSqliteDb('slave_sync_user')
conn.where("ip=?", (ip,)).delete()
return mw.returnJson(True, '删除成功!')
def getSlaveSyncUserList(version=''):
args = getArgs()
data = checkArgs(args, ['page', 'page_size'])
if not data[0]:
return data[1]
page = int(args['page'])
page_size = int(args['page_size'])
conn = pSqliteDb('slave_sync_user')
limit = str((page - 1) * page_size) + ',' + str(page_size)
field = 'id,ip,port,user,pass,cmd,addtime'
clist = conn.field(field).limit(limit).order('id desc').select()
count = conn.count()
data = {}
_page = {}
_page['count'] = count
_page['p'] = page
_page['row'] = page_size
_page['tojs'] = args['tojs']
data['page'] = mw.getPage(_page)
data['data'] = clist
return mw.getJson(data)
def getSyncModeFile():
return getServerDir() + "/sync.mode"
def getSlaveSyncMode(version):
sync_mode = getSyncModeFile()
if os.path.exists(sync_mode):
mode = mw.readFile(sync_mode).strip()
return mw.returnJson(True, 'ok', mode)
return mw.returnJson(False, 'fail')
def setSlaveSyncMode(version):
args = getArgs()
data = checkArgs(args, ['mode'])
if not data[0]:
return data[1]
mode = args['mode']
sync_mode = getSyncModeFile()
if mode == 'none':
os.remove(sync_mode)
else:
mw.writeFile(sync_mode, mode)
return mw.returnJson(True, '设置成功', mode)
def getSlaveSSHByIp(version=''): def getSlaveSSHByIp(version=''):
args = getArgs() args = getArgs()
data = checkArgs(args, ['ip']) data = checkArgs(args, ['ip'])
@@ -2033,6 +2188,24 @@ def getSlaveList(version=''):
tmp['Master_Log_File'] = dlist[x]["Master_Log_File"] tmp['Master_Log_File'] = dlist[x]["Master_Log_File"]
tmp['Slave_IO_Running'] = dlist[x]["Slave_IO_Running"] tmp['Slave_IO_Running'] = dlist[x]["Slave_IO_Running"]
tmp['Slave_SQL_Running'] = dlist[x]["Slave_SQL_Running"] tmp['Slave_SQL_Running'] = dlist[x]["Slave_SQL_Running"]
tmp['Last_Error'] = dlist[x]["Last_Error"]
tmp['Last_IO_Error'] = dlist[x]["Last_IO_Error"]
tmp['Last_SQL_Error'] = dlist[x]["Last_SQL_Error"]
tmp['Slave_SQL_Running_State'] = dlist[x]["Slave_SQL_Running_State"]
tmp['Error'] = ''
if tmp['Last_Error'] != '':
tmp['Error'] = tmp['Last_Error']
if tmp['Last_IO_Error'] != '':
tmp['Error'] = tmp['Last_IO_Error']
if tmp['Last_SQL_Error'] != '':
tmp['Error'] = tmp['Last_SQL_Error']
if tmp['Error'] == '':
tmp['Error'] = tmp['Slave_SQL_Running_State']
ret.append(tmp) ret.append(tmp)
data = {} data = {}
data['data'] = ret data['data'] = ret
@@ -2049,6 +2222,47 @@ def getSlaveSyncCmd(version=''):
def initSlaveStatus(version=''): def initSlaveStatus(version=''):
mode_file = getSyncModeFile()
if not os.path.exists(mode_file):
return mw.returnJson(False, '需要先设置同步配置')
mode = mw.readFile(mode_file)
if mode == 'ssh':
return initSlaveStatusSSH(version)
if mode == 'sync-user':
return initSlaveStatusSyncUser(version)
def initSlaveStatusSyncUser(version=''):
conn = pSqliteDb('slave_sync_user')
data = conn.field('ip,port,user,pass,mode,cmd').find()
if len(data) < 1:
return mw.returnJson(False, '需要先添加同步用户配置!')
# print(data)
db = pMysqlDb()
dlist = db.query('show slave status')
if len(dlist) > 0:
return mw.returnJson(False, '已经初始化好了zz...')
u = data
mode_name = 'classic'
if u['mode'] == '1':
mode_name = 'gtid'
local_mode = recognizeDbMode()
if local_mode != mode_name:
return mw.returnJson(False, '同步模式不一致!')
t = db.query(u['cmd'])
# print(t)
db.query("start slave user='{}' password='{}';".format(
u['user'], u['pass']))
return mw.returnJson(True, '初始化成功!')
def initSlaveStatusSSH(version=''):
db = pMysqlDb() db = pMysqlDb()
dlist = db.query('show slave status') dlist = db.query('show slave status')
if len(dlist) > 0: if len(dlist) > 0:
@@ -2117,6 +2331,41 @@ def initSlaveStatus(version=''):
def setSlaveStatus(version=''): def setSlaveStatus(version=''):
mode_file = getSyncModeFile()
if not os.path.exists(mode_file):
return mw.returnJson(False, '需要先设置同步配置')
mode = mw.readFile(mode_file)
if mode == 'ssh':
return setSlaveStatusSSH(version)
if mode == 'sync-user':
return setSlaveStatusSyncUser(version)
def setSlaveStatusSyncUser(version=''):
db = pMysqlDb()
dlist = db.query('show slave status')
if len(dlist) == 0:
return mw.returnJson(False, '需要手动添加同步账户或者执行初始化!')
if len(dlist) > 0 and (dlist[0]["Slave_IO_Running"] == 'Yes' or dlist[0]["Slave_SQL_Running"] == 'Yes'):
db.query('stop slave')
else:
ip = dlist[0]['Master_Host']
conn = pSqliteDb('slave_sync_user')
data = conn.field('ip,port,user,pass,mode,cmd').find()
if len(data) == 0:
return mw.returnJson(False, '没有数据无法重启!')
user = data['user']
apass = data['pass']
db.query("start slave")
# db.query("start slave user='{}' password='{}';".format(user, apass))
return mw.returnJson(True, '设置成功!')
def setSlaveStatusSSH(version=''):
db = pMysqlDb() db = pMysqlDb()
dlist = db.query('show slave status') dlist = db.query('show slave status')
@@ -2185,6 +2434,69 @@ def writeDbSyncStatus(data):
def doFullSync(version=''): def doFullSync(version=''):
mode_file = getSyncModeFile()
if not os.path.exists(mode_file):
return mw.returnJson(False, '需要先设置同步配置')
mode = mw.readFile(mode_file)
if mode == 'ssh':
return doFullSyncSSH(version)
if mode == 'sync-user':
return doFullSyncUser(version)
def doFullSyncUser(version=''):
args = getArgs()
data = checkArgs(args, ['db'])
if not data[0]:
return data[1]
sync_db = args['db']
db = pMysqlDb()
conn = pSqliteDb('slave_sync_user')
data = conn.field('ip,port,user,pass,mode,cmd').find()
user = data['user']
apass = data['pass']
port = data['port']
ip = data['ip']
bak_file = '/tmp/tmp.sql'
writeDbSyncStatus({'code': 0, 'msg': '开始同步...', 'progress': 0})
dmp_option = ''
mode = recognizeDbMode()
if mode == 'gtid':
dmp_option = ' --set-gtid-purged=off '
writeDbSyncStatus({'code': 1, 'msg': '远程导出数据...', 'progress': 20})
if not os.path.exists(bak_file):
dump_sql_data = getServerDir() + "/bin/mysqldump " + dmp_option + " --force --opt --default-character-set=utf8 --single-transaction -h" + ip + " -P" + \
port + " -u" + user + " -p" + apass + \
" --ssl-mode=DISABLED " + sync_db + " > " + bak_file
mw.execShell(dump_sql_data)
writeDbSyncStatus({'code': 2, 'msg': '本地导入数据...', 'progress': 40})
if os.path.exists(bak_file):
pwd = pSqliteDb('config').where('id=?', (1,)).getField('mysql_root')
sock = getSocketFile()
my_import_cmd = getServerDir() + '/bin/mysql -S ' + sock + ' -uroot -p' + pwd + \
' ' + sync_db + ' < ' + bak_file
mw.execShell(my_import_cmd)
if version == '8.0':
db.query("start slave user='{}' password='{}';".format(user, apass))
else:
db.query("start slave")
writeDbSyncStatus({'code': 6, 'msg': '从库重启完成...', 'progress': 100})
os.system("rm -rf " + bak_file)
return True
def doFullSyncSSH(version=''):
args = getArgs() args = getArgs()
data = checkArgs(args, ['db']) data = checkArgs(args, ['db'])
@@ -2371,6 +2683,9 @@ def uninstallPreInspection(version):
if mw.isDebugMode(): if mw.isDebugMode():
return 'ok' return 'ok'
import plugins_api
plugins_api.plugins_api().removeIndex(getPluginName(), version)
return "请手动删除MySQL[{}]<br/> rm -rf {}".format(version, getServerDir()) return "请手动删除MySQL[{}]<br/> rm -rf {}".format(version, getServerDir())
if __name__ == "__main__": if __name__ == "__main__":
@@ -2423,7 +2738,7 @@ if __name__ == "__main__":
elif func == 'my_db_pos': elif func == 'my_db_pos':
print(getMyDbPos()) print(getMyDbPos())
elif func == 'set_db_pos': elif func == 'set_db_pos':
print(setMyDbPos()) print(setMyDbPos(version))
elif func == 'my_port': elif func == 'my_port':
print(getMyPort()) print(getMyPort())
elif func == 'set_my_port': elif func == 'set_my_port':
@@ -2516,6 +2831,18 @@ if __name__ == "__main__":
print(delSlaveSSH(version)) print(delSlaveSSH(version))
elif func == 'update_slave_ssh': elif func == 'update_slave_ssh':
print(updateSlaveSSH(version)) print(updateSlaveSSH(version))
elif func == 'get_slave_sync_user_list':
print(getSlaveSyncUserList(version))
elif func == 'get_slave_sync_user_by_ip':
print(getSlaveSyncUserByIp(version))
elif func == 'add_slave_sync_user':
print(addSlaveSyncUser(version))
elif func == 'del_slave_sync_user':
print(delSlaveSyncUser(version))
elif func == 'get_slave_sync_mode':
print(getSlaveSyncMode(version))
elif func == 'set_slave_sync_mode':
print(setSlaveSyncMode(version))
elif func == 'init_slave_status': elif func == 'init_slave_status':
print(initSlaveStatus(version)) print(initSlaveStatus(version))
elif func == 'set_slave_status': elif func == 'set_slave_status':
+1 -1
View File
@@ -4,7 +4,7 @@
"tip":"soft", "tip":"soft",
"name":"mysql-apt", "name":"mysql-apt",
"type":"运行环境", "type":"运行环境",
"ps":"[<b style='color:red;'>潜龙勿用</b>]一种关系数据库管理系统[debian,ubuntu](极速安装)", "ps":"一种关系数据库管理系统[debian,ubuntu](极速安装)",
"todo_versions":["5.7","8.0"], "todo_versions":["5.7","8.0"],
"versions":["5.7","8.0"], "versions":["5.7","8.0"],
"shell":"install.sh", "shell":"install.sh",
+277 -16
View File
@@ -731,7 +731,7 @@ function addDatabase(type){
layer.close(index); layer.close(index);
dbList(); dbList();
} }
},{icon: rdata.status ? 1 : 2},600); },{icon: rdata.status ? 1 : 2}, 2000);
}); });
} }
}); });
@@ -819,7 +819,7 @@ function openPhpmyadmin(name,username,password){
data = syncPost('/plugins/run',{'name':'phpmyadmin','func':'get_cfg'}); data = syncPost('/plugins/run',{'name':'phpmyadmin','func':'get_cfg'});
var rdata = $.parseJSON(data.data); var rdata = $.parseJSON(data.data);
if (rdata.choose != 'mysql-apt'){ if (rdata.choose != 'mysql-apt'){
layer.msg('当前为['+rdata.choose+']模式,若要使用请切换模式.',{icon:2,shade: [0.3, '#000']}); layer.msg('当前为['+rdata.choose+']模式,若要使用请修改phpMyAdmin访问切换.',{icon:2,shade: [0.3, '#000']});
return; return;
} }
@@ -1208,7 +1208,7 @@ function myLogs(){
<span class="f14 c6 mr20">二进制日志 </span><span class="f14 c6 mr20">' + toSize(rdata.msg) + '</span>\ <span class="f14 c6 mr20">二进制日志 </span><span class="f14 c6 mr20">' + toSize(rdata.msg) + '</span>\
'+line_status+'\ '+line_status+'\
<p class="f14 c6 mtb10" style="border-top:#ddd 1px solid; padding:10px 0">错误日志<button class="btn btn-default btn-clear btn-xs" style="float:right;" >清理日志</button></p>\ <p class="f14 c6 mtb10" style="border-top:#ddd 1px solid; padding:10px 0">错误日志<button class="btn btn-default btn-clear btn-xs" style="float:right;" >清理日志</button></p>\
<textarea readonly style="margin: 0px;width: 100%;height: 440px;background-color: #333;color:#fff; padding:0 5px" id="error_log"></textarea>\ <textarea readonly style="margin: 0px;width: 100%;height: 438px;background-color: #333;color:#fff; padding:0 5px" id="error_log"></textarea>\
</p>'; </p>';
$(".soft-man-con").html(limitCon); $(".soft-man-con").html(limitCon);
@@ -1545,13 +1545,9 @@ function getMasterRepSlaveUserCmd(username, db=''){
function delMasterRepSlaveUser(username){ function delMasterRepSlaveUser(username){
myPost('del_master_rep_slave_user', {username:username}, function(data){ myPost('del_master_rep_slave_user', {username:username}, function(data){
var rdata = $.parseJSON(data.data); var rdata = $.parseJSON(data.data);
layer.msg(rdata.msg); showMsg(rdata.msg, function(){
$('.layui-layer-close1').click();
setTimeout(function(){
getMasterRepSlaveList(); getMasterRepSlaveList();
},1000); },{icon: rdata.status ? 1 : 2},1000)
}); });
} }
@@ -1821,8 +1817,23 @@ function addSlaveSSH(ip=''){
function delSlaveSSH(ip){ function delSlaveSSH(ip){
myPost('del_slave_ssh', {ip:ip}, function(rdata){ myPost('del_slave_ssh', {ip:ip}, function(rdata){
var rdata = $.parseJSON(rdata.data); var rdata = $.parseJSON(rdata.data);
layer.msg(rdata.msg, {icon: rdata.status ? 1 : 2}); showMsg(rdata.msg,function(){
getSlaveSSHPage(); if (rdata.status){
getSlaveSSHPage();
}
},{icon: rdata.status ? 1 : 2}, 600);
});
}
function delSlaveSyncUser(ip){
myPost('del_slave_sync_user', {ip:ip}, function(rdata){
var rdata = $.parseJSON(rdata.data);
showMsg(rdata.msg,function(){
if (rdata.status){
getSlaveSyncUserPage();
}
},{icon: rdata.status ? 1 : 2}, 600);
}); });
} }
@@ -1872,6 +1883,237 @@ function getSlaveSSHPage(page=1){
} }
function addSlaveSyncUser(ip=''){
myPost('get_slave_sync_user_by_ip', {ip:ip}, function(rdata){
var rdata = $.parseJSON(rdata.data);
var ip = '127.0.0.1';
var port = "22";
var cmd = '';
var user = 'input_sync_user';
var pass = 'input_sync_pwd';
var mode = '0';
if (rdata.data.length>0){
ip = rdata.data[0]['ip'];
port = rdata.data[0]['port'];
cmd = rdata.data[0]['cmd'];
user = rdata.data[0]['user'];
pass = rdata.data[0]['pass'];
mode = rdata.data[0]['mode'];
}
var index = layer.open({
type: 1,
area: ['500px','470px'],
title: '同步账户',
closeBtn: 1,
shift: 5,
shadeClose: true,
btn:["确认","取消"],
content: "<form class='bt-form pd20'>\
<div class='line'><span class='tname'>IP</span><div class='info-r'><input name='ip' class='bt-input-text mr5' type='text' style='width:330px;' value='"+ip+"'></div></div>\
<div class='line'><span class='tname'>端口</span><div class='info-r'><input name='port' class='bt-input-text mr5' type='number' style='width:330px;' value='"+port+"'></div></div>\
<div class='line'><span class='tname'>同步账户</span><div class='info-r'><input name='user' class='bt-input-text mr5' type='text' style='width:330px;' value='"+user+"'></div></div>\
<div class='line'><span class='tname'>同步密码</span><div class='info-r'><input name='pass' class='bt-input-text mr5' type='text' style='width:330px;' value='"+pass+"'></div></div>\
<div class='line'>\
<span class='tname'>CMD[最好填好]</span>\
<div class='info-r'><textarea class='bt-input-text mr5' row='20' cols='30' name='cmd' style='width:330px;height:150px;'></textarea></div>\
</div>\
<input type='hidden' name='mode' value='"+mode+"' />\
</form>",
success:function(){
$('textarea[name="cmd"]').html(cmd);
$('textarea[name="cmd"]').change(function(){
var val = $(this).val();
var vlist = val.split(',');
var a = {};
for (var i in vlist) {
var tmp = toTrim(vlist[i]);
var tmp_a = tmp.split(" ");
var real_tmp = tmp_a[tmp_a.length-1];
var kv = real_tmp.split("=");
a[kv[0]] = kv[1].replace("'",'').replace("'",'');
}
$('input[name="ip"]').val(a['MASTER_HOST']);
$('input[name="port"]').val(a['MASTER_PORT']);
$('input[name="user"]').val(a['MASTER_USER']);
$('input[name="pass"]').val(a['MASTER_PASSWORD']);
console.log(a['MASTER_AUTO_POSITION'],typeof(a['MASTER_AUTO_POSITION']));
if (typeof(a['MASTER_AUTO_POSITION']) != 'undefined' ){
$('input[name="mode"]').val('1');
}
});
},
yes:function(index){
var ip = $('input[name="ip"]').val();
var port = $('input[name="port"]').val();
var user = $('input[name="user"]').val();
var pass = $('input[name="pass"]').val();
var cmd = $('textarea[name="cmd"]').val();
var mode = $('input[name="mode"]').val();
var data = {ip:ip,port:port,cmd:cmd,user:user,pass:pass,mode:mode};
myPost('add_slave_sync_user', data, function(ret_data){
layer.close(index);
var rdata = $.parseJSON(ret_data.data);
showMsg(rdata.msg,function(){
if (rdata.status){
getSlaveSyncUserPage();
}
},{icon: rdata.status ? 1 : 2},600);
});
}
});
});
}
function getSlaveSyncUserPage(page=1){
var _data = {};
_data['page'] = page;
_data['page_size'] = 5;
_data['tojs'] ='getSlaveSyncUserPage';
myPost('get_slave_sync_user_list', _data, function(data){
var layerId = null;
var rdata = [];
try {
rdata = $.parseJSON(data.data);
} catch(e) {
console.log(e);
}
var list = '';
var user_list = rdata['data'];
for (i in user_list) {
var ip = user_list[i]['ip'];
var port = user_list[i]['port'];
var user = user_list[i]['user'];
var apass = user_list[i]['pass'];
var cmd = '未设置';
if (user_list[i]['cmd']!=''){
cmd = '已设置';
}
list += '<tr><td>'+ip+'</td>\
<td>'+port+'</td>\
<td>'+user+'</td>\
<td>'+apass+'</td>\
<td>'+cmd+'</td>\
<td>\
<a class="btlink" onclick="addSlaveSyncUser(\''+ip+'\');">修改</a> | \
<a class="btlink" onclick="delSlaveSyncUser(\''+ip+'\');">删除</a>\
</td>\
</tr>';
}
$('.get-slave-ssh-list tbody').html(list);
$('.dataTables_paginate_4').html(rdata['page']);
});
}
function getSlaveCfg(){
myPost('get_slave_sync_mode', '', function(data){
var rdata = $.parseJSON(data.data);
var mode_none = 'success';
var mode_ssh = 'danger';
var mode_sync_user = 'danger';
if(rdata.status){
var mode_none = 'danger';
if (rdata.data == 'ssh'){
var mode_ssh = 'success';
var mode_sync_user = 'danger';
} else {
var mode_ssh = 'danger';
var mode_sync_user = 'success';
}
}
layerId = layer.open({
type: 1,
title: '同步配置',
area: ['400px','180px'],
content:"<div class='bt-form pd20 c6'>\
<p class='conf_p'>\
<span class='f14 c6 mr20'>当前从库同步模式</span>\
<b class='f14 c6 mr20'></b>\
<button class='btn btn-"+mode_none+" btn-xs slave-db-mode btn-none'>无</button>\
<button class='btn btn-"+mode_ssh+" btn-xs slave-db-mode btn-ssh'>SSH</button>\
<button class='btn btn-"+mode_sync_user+" btn-xs slave-db-mode btn-sync-user'>同步账户</button>\
</p>\
<hr />\
<p class='conf_p'>\
<span class='f14 c6 mr20'>配置设置</span>\
<b class='f14 c6 mr20'></b>\
<button class='btn btn-success btn-xs btn-slave-ssh'>SSH</button>\
<button class='btn btn-success btn-xs btn-slave-user'>同步账户</button>\
</p>\
</div>",
success:function(){
$('.btn-slave-ssh').click(function(){
getSlaveSSHList();
});
$('.btn-slave-user').click(function(){
getSlaveUserList();
});
$('.slave-db-mode').click(function(){
var _this = this;
var mode = 'none';
if ($(this).hasClass('btn-ssh')){
mode = 'ssh';
}
if ($(this).hasClass('btn-sync-user')){
mode = 'sync-user';
}
myPost('set_slave_sync_mode', {mode:mode}, function(data){
var rdata = $.parseJSON(data.data);
showMsg(rdata.msg,function(){
$('.slave-db-mode').remove('btn-success').addClass('btn-danger');
$(_this).removeClass('btn-danger').addClass('btn-success');
},{icon:rdata.status?1:2},2000);
});
});
}
});
});
}
function getSlaveUserList(){
var page = '<div class="dataTables_paginate_4 dataTables_paginate paging_bootstrap page" style="margin-top:0px;"></div>';
page += '<div class="table_toolbar" style="left:0px;"><span class="sync btn btn-default btn-sm" onclick="addSlaveSyncUser()" title="">添加同步账户</span></div>';
layerId = layer.open({
type: 1,
title: '同步账户列表',
area: '500px',
content:"<div class='bt-form pd20 c6'>\
<div class='divtable mtb10'>\
<div><table class='table table-hover get-slave-ssh-list'>\
<thead><tr><th>IP</th><th>PORT</th><th>同步账户</th><th>同步密码</th><th>CMD</th><th>操作</th></tr></thead>\
<tbody></tbody>\
</table></div>\
"+page +"\
</div>\
</div>",
success:function(){
getSlaveSyncUserPage(1);
}
});
}
function getSlaveSSHList(page=1){ function getSlaveSSHList(page=1){
var page = '<div class="dataTables_paginate_4 dataTables_paginate paging_bootstrap page" style="margin-top:0px;"></div>'; var page = '<div class="dataTables_paginate_4 dataTables_paginate paging_bootstrap page" style="margin-top:0px;"></div>';
@@ -1991,7 +2233,7 @@ function masterOrSlaveConf(version=''){
for(i in rdata.data){ for(i in rdata.data){
var v = rdata.data[i]; var v = rdata.data[i];
var status = "异常"; var status = "<a class='btlink db_error'>异常</>";
if (v['Slave_SQL_Running'] == 'Yes' && v['Slave_IO_Running'] == 'Yes'){ if (v['Slave_SQL_Running'] == 'Yes' && v['Slave_IO_Running'] == 'Yes'){
status = "正常"; status = "正常";
} }
@@ -2033,6 +2275,26 @@ function masterOrSlaveConf(version=''){
// <span class="sync btn btn-default btn-sm" onclick="getMasterRepSlaveList()" title="">添加</span>\ // <span class="sync btn btn-default btn-sm" onclick="getMasterRepSlaveList()" title="">添加</span>\
// </div> // </div>
$(".table_slave_status_list").html(con); $(".table_slave_status_list").html(con);
$('.db_error').click(function(){
layer.open({
type: 1,
title: '同步异常信息',
area: '500px',
content:"<form class='bt-form pd20 pb70'>\
<div class='line'>"+v['Error']+"</div>\
<div class='bt-form-submit-btn'>\
<button type='button' class='btn btn-success btn-sm btn-title class-copy-db-err'>复制</button>\
</div>\
</form>",
success:function(){
copyText(v['Error']);
$('.class-copy-db-err').click(function(){
copyText(v['Error']);
});
}
});
});
}); });
} }
@@ -2079,7 +2341,6 @@ function masterOrSlaveConf(version=''){
}); });
} }
function getMasterStatus(){ function getMasterStatus(){
myPost('get_master_status', '', function(rdata){ myPost('get_master_status', '', function(rdata){
@@ -2110,7 +2371,7 @@ function masterOrSlaveConf(version=''){
<p class="conf_p">\ <p class="conf_p">\
<span class="f14 c6 mr20">Slave[从]配置</span><span class="f14 c6 mr20"></span>\ <span class="f14 c6 mr20">Slave[从]配置</span><span class="f14 c6 mr20"></span>\
<button class="btn '+(!rdata.slave_status ? 'btn-danger' : 'btn-success')+' btn-xs btn-slave">'+(!rdata.slave_status ? '未启动' : '已启动') +'</button>\ <button class="btn '+(!rdata.slave_status ? 'btn-danger' : 'btn-success')+' btn-xs btn-slave">'+(!rdata.slave_status ? '未启动' : '已启动') +'</button>\
<button class="btn btn-success btn-xs" onclick="getSlaveSSHList()" >[主]SSH配置</button>\ <button class="btn btn-success btn-xs" onclick="getSlaveCfg()" >同步配置</button>\
<button class="btn btn-success btn-xs" onclick="initSlaveStatus()" >初始化</button>\ <button class="btn btn-success btn-xs" onclick="initSlaveStatus()" >初始化</button>\
</p>\ </p>\
<hr/>\ <hr/>\
@@ -2184,10 +2445,10 @@ function masterOrSlaveConf(version=''){
getMasterDbList(); getMasterDbList();
} }
if (rdata.slave_status){ // if (rdata.slave_status){
getAsyncMasterDbList(); getAsyncMasterDbList();
getAsyncDataList() getAsyncDataList()
} // }
}); });
} }
getMasterStatus(); getMasterStatus();
+6 -4
View File
@@ -37,11 +37,12 @@ max_connect_errors = 100
open_files_limit = 2560 open_files_limit = 2560
max_allowed_packet = 128M max_allowed_packet = 128M
#skip-external-locking skip_name_resolve = 1
#skip-grant-tables skip-ssl
#loose-skip-innodb
#skip-networking #skip-networking
#skip-name-resolve #skip-external-locking
#loose-skip-innodb
#skip-grant-tables
log-bin=mysql-bin log-bin=mysql-bin
binlog_format=mixed binlog_format=mixed
@@ -65,6 +66,7 @@ binlog-ignore-db = performance_schema
#slave #slave
log-slave-updates log-slave-updates
#replicate-do-db #replicate-do-db
slave_skip_errors=1062
replicate-ignore-db = information_schema replicate-ignore-db = information_schema
replicate-ignore-db = performance_schema replicate-ignore-db = performance_schema
replicate-ignore-db = mysql replicate-ignore-db = mysql
+1
View File
@@ -62,6 +62,7 @@ binlog-ignore-db = performance_schema
#slave #slave
log_replica_updates log_replica_updates
#replicate-do-db #replicate-do-db
slave_skip_errors=1062
replicate-ignore-db = information_schema replicate-ignore-db = information_schema
replicate-ignore-db = performance_schema replicate-ignore-db = performance_schema
replicate-ignore-db = mysql replicate-ignore-db = mysql
+13
View File
@@ -40,4 +40,17 @@ CREATE TABLE IF NOT EXISTS `slave_id_rsa` (
`addtime` TEXT `addtime` TEXT
); );
-- 从库配置主库的[user]
-- drop table `slave_user`;
CREATE TABLE IF NOT EXISTS `slave_sync_user` (
`id` INTEGER PRIMARY KEY AUTOINCREMENT,
`ip` TEXT,
`port` TEXT,
`user` TEXT,
`pass` TEXT,
`mode` TEXT,
`cmd` TEXT,
`addtime` TEXT
);
+1
View File
@@ -13,6 +13,7 @@
<p onclick="myLogs();">日志</p> <p onclick="myLogs();">日志</p>
<p onclick="pluginLogs('mysql-yum',$('.plugin_version').attr('version'),'show_log');">慢日志</p> <p onclick="pluginLogs('mysql-yum',$('.plugin_version').attr('version'),'show_log');">慢日志</p>
<p onclick="dbList()">管理列表</p> <p onclick="dbList()">管理列表</p>
<p onclick="masterOrSlaveConf($('.plugin_version').attr('version'))">主从配置</p>
</div> </div>
<div class="bt-w-con pd15"> <div class="bt-w-con pd15">
<div class="soft-man-con"></div> <div class="soft-man-con"></div>
+346 -34
View File
@@ -129,20 +129,27 @@ def contentReplace(content):
def pSqliteDb(dbname='databases'): def pSqliteDb(dbname='databases'):
file = getServerDir() + '/mysql.db' file = getServerDir() + '/mysql.db'
name = 'mysql' name = 'mysql'
if not os.path.exists(file):
import_sql = mw.readFile(getPluginDir() + '/conf/mysql.sql')
md5_sql = mw.md5(import_sql)
import_sign = False
save_md5_file = getServerDir() + '/import_sql.md5'
if os.path.exists(save_md5_file):
save_md5_sql = mw.readFile(save_md5_file)
if save_md5_sql != md5_sql:
import_sign = True
mw.writeFile(save_md5_file, md5_sql)
else:
mw.writeFile(save_md5_file, md5_sql)
if not os.path.exists(file) or import_sql:
conn = mw.M(dbname).dbPos(getServerDir(), name) conn = mw.M(dbname).dbPos(getServerDir(), name)
csql = mw.readFile(getPluginDir() + '/conf/mysql.sql') csql_list = import_sql.split(';')
csql_list = csql.split(';')
for index in range(len(csql_list)): for index in range(len(csql_list)):
conn.execute(csql_list[index], ()) conn.execute(csql_list[index], ())
else:
# 现有run conn = mw.M(dbname).dbPos(getServerDir(), name)
# conn = mw.M(dbname).dbPos(getServerDir(), name)
# csql = mw.readFile(getPluginDir() + '/conf/mysql.sql')
# csql_list = csql.split(';')
# for index in range(len(csql_list)):
# conn.execute(csql_list[index], ())
conn = mw.M(dbname).dbPos(getServerDir(), name)
return conn return conn
@@ -161,9 +168,16 @@ def pMysqlDb():
def initDreplace(version=''): def initDreplace(version=''):
my_dir = getServerDir() + '/etc' conf_dir = getServerDir() + '/etc'
if not os.path.exists(my_dir): mode_dir = conf_dir + '/mode'
os.mkdir(my_dir)
conf_list = [
conf_dir,
mode_dir,
]
for conf in conf_list:
if not os.path.exists(conf):
os.mkdir(conf)
tmp_dir = getServerDir() + '/tmp' tmp_dir = getServerDir() + '/tmp'
if not os.path.exists(tmp_dir): if not os.path.exists(tmp_dir):
@@ -171,12 +185,26 @@ def initDreplace(version=''):
mw.execShell("chown -R mysql:mysql " + tmp_dir) mw.execShell("chown -R mysql:mysql " + tmp_dir)
mw.execShell("chmod 750 " + tmp_dir) mw.execShell("chmod 750 " + tmp_dir)
mysql_conf = my_dir + '/my.cnf' my_conf = conf_dir + '/my.cnf'
if not os.path.exists(mysql_conf): if not os.path.exists(my_conf):
mysql_conf_tpl = getPluginDir() + '/conf/my' + version + '.cnf' tpl = getPluginDir() + '/conf/my' + version + '.cnf'
content = mw.readFile(mysql_conf_tpl) content = mw.readFile(tpl)
content = contentReplace(content) content = contentReplace(content)
mw.writeFile(mysql_conf, content) mw.writeFile(my_conf, content)
classic_conf = mode_dir + '/classic.cnf'
if not os.path.exists(classic_conf):
tpl = getPluginDir() + '/conf/classic.cnf'
content = mw.readFile(tpl)
content = contentReplace(content)
mw.writeFile(classic_conf, content)
gtid_conf = mode_dir + '/gtid.cnf'
if not os.path.exists(gtid_conf):
tpl = getPluginDir() + '/conf/gtid.cnf'
content = mw.readFile(tpl)
content = contentReplace(content)
mw.writeFile(gtid_conf, content)
# systemd # systemd
systemDir = mw.systemdCfgDir() systemDir = mw.systemdCfgDir()
@@ -233,6 +261,13 @@ def binLog():
return mw.returnJson(True, '设置成功!') return mw.returnJson(True, '设置成功!')
def cleanBinLog():
db = pMysqlDb()
cleanTime = time.strftime('%Y-%m-%d %H:%i:%s', time.localtime())
db.execute("PURGE MASTER LOGS BEFORE '" + cleanTime + "';")
return mw.returnJson(True, '清理BINLOG成功!')
def setSkipGrantTables(v): def setSkipGrantTables(v):
''' '''
设置是否密码验证 设置是否密码验证
@@ -362,6 +397,7 @@ def my8cmd(version, method):
initDreplace(version) initDreplace(version)
# mysql 8.0 and 5.7 # mysql 8.0 and 5.7
try: try:
isInited = True
if version == '5.7': if version == '5.7':
isInited = initMysql57Data() isInited = initMysql57Data()
elif version == '8.0': elif version == '8.0':
@@ -434,7 +470,7 @@ def getMyDbPos():
return tmp.groups()[0].strip() return tmp.groups()[0].strip()
def setMyDbPos(): def setMyDbPos(version=''):
args = getArgs() args = getArgs()
data = checkArgs(args, ['datadir']) data = checkArgs(args, ['datadir'])
if not data[0]: if not data[0]:
@@ -449,7 +485,7 @@ def setMyDbPos():
mw.execShell('mkdir -p ' + t_datadir) mw.execShell('mkdir -p ' + t_datadir)
# mw.execShell('/etc/init.d/mysqld stop') # mw.execShell('/etc/init.d/mysqld stop')
stop() stop(version)
mw.execShell('cp -rf ' + s_datadir + '/* ' + t_datadir + '/') mw.execShell('cp -rf ' + s_datadir + '/* ' + t_datadir + '/')
mw.execShell('chown -R mysql mysql ' + t_datadir) mw.execShell('chown -R mysql mysql ' + t_datadir)
mw.execShell('chmod -R 755 ' + t_datadir) mw.execShell('chmod -R 755 ' + t_datadir)
@@ -463,17 +499,17 @@ def setMyDbPos():
mycnf = mycnf.replace(s_datadir, t_datadir) mycnf = mycnf.replace(s_datadir, t_datadir)
mw.writeFile(myfile, mycnf) mw.writeFile(myfile, mycnf)
start() restart(version)
result = mw.execShell( result = mw.execShell(
'ps aux|grep mysqld| grep -v grep|grep -v python') 'ps aux|grep "mysql-apt/bin/usr/sbin/mysqld"| grep -v grep|grep -v python')
if len(result[0]) > 10: if len(result[0]) > 10:
mw.writeFile('data/datadir.pl', t_datadir) mw.writeFile('data/datadir.pl', t_datadir)
return mw.returnJson(True, '存储目录迁移成功!') return mw.returnJson(True, '存储目录迁移成功!')
else: else:
mw.execShell('pkill -9 mysqld') mw.execShell('pkill -9 mysqld')
mw.writeFile(myfile, mw.readFile(path + '/etc/my_backup.cnf')) mw.writeFile(myfile, mw.readFile(path + '/etc/my_backup.cnf'))
start() start(version)
return mw.returnJson(False, '文件迁移失败!') return mw.returnJson(False, '文件迁移失败!')
@@ -719,11 +755,13 @@ def importDbExternal():
mysql_cmd = getServerDir() + '/bin/usr/bin/mysql -S ' + sock + ' -uroot -p' + \ mysql_cmd = getServerDir() + '/bin/usr/bin/mysql -S ' + sock + ' -uroot -p' + \
pwd + ' ' + name + ' < ' + import_sql pwd + ' ' + name + ' < ' + import_sql
# print(mysql_cmd) rdata = mw.execShell(mysql_cmd)
os.system(mysql_cmd)
if ext != 'sql': if ext != 'sql':
os.remove(import_sql) os.remove(import_sql)
if rdata[1].lower().find('error') > -1:
return mw.returnJson(False, rdata[1])
return mw.returnJson(True, 'ok') return mw.returnJson(True, 'ok')
@@ -748,8 +786,10 @@ def importDbBackup():
mysql_cmd = getServerDir() + '/bin/usr/bin/mysql -S ' + sock + ' -uroot -p' + pwd + \ mysql_cmd = getServerDir() + '/bin/usr/bin/mysql -S ' + sock + ' -uroot -p' + pwd + \
' ' + name + ' < ' + file_path_sql ' ' + name + ' < ' + file_path_sql
# print(mysql_cmd) rdata = mw.execShell(mysql_cmd)
os.system(mysql_cmd) if rdata[1].lower().find('error') > -1:
return mw.returnJson(False, rdata[1])
return mw.returnJson(True, 'ok') return mw.returnJson(True, 'ok')
@@ -1079,7 +1119,7 @@ def addDb():
dataAccess = args['dataAccess'].strip() dataAccess = args['dataAccess'].strip()
ps = args['ps'].strip() ps = args['ps'].strip()
reg = "^[\w\.-]+$" reg = "^[\w-]+$"
if not re.match(reg, args['name']): if not re.match(reg, args['name']):
return mw.returnJson(False, '数据库名称不能带有特殊符号!') return mw.returnJson(False, '数据库名称不能带有特殊符号!')
checks = ['root', 'mysql', 'test', 'sys', 'panel_logs'] checks = ['root', 'mysql', 'test', 'sys', 'panel_logs']
@@ -1747,7 +1787,7 @@ def addMasterRepSlaveUser(version=''):
# address = args['address'].strip() # address = args['address'].strip()
# dataAccess = args['dataAccess'].strip() # dataAccess = args['dataAccess'].strip()
reg = "^[\w\.-]+$" reg = "^[\w-]+$"
if not re.match(reg, username): if not re.match(reg, username):
return mw.returnJson(False, '用户名不能带有特殊符号!') return mw.returnJson(False, '用户名不能带有特殊符号!')
checks = ['root', 'mysql', 'test', 'sys', 'panel_logs'] checks = ['root', 'mysql', 'test', 'sys', 'panel_logs']
@@ -1775,17 +1815,18 @@ def addMasterRepSlaveUser(version=''):
if isError != None: if isError != None:
return isError return isError
sql = "FLUSH PRIVILEGES;"
pdb.execute(sql)
else: else:
sql = "GRANT REPLICATION SLAVE ON *.* TO '" + username + \ sql = "GRANT REPLICATION SLAVE ON *.* TO '" + username + \
"'@'%' identified by '" + password + "';" "'@'%' identified by '" + password + "';"
result = pdb.execute(sql) result = pdb.execute(sql)
result = pdb.execute('FLUSH PRIVILEGES;')
isError = isSqlError(result) isError = isSqlError(result)
if isError != None: if isError != None:
return isError return isError
sql_select = "grant select,lock tables,PROCESS on *.* to " + username + "@'%';"
pdb.execute(sql_select)
pdb.execute('FLUSH PRIVILEGES;')
addTime = time.strftime('%Y-%m-%d %X', time.localtime()) addTime = time.strftime('%Y-%m-%d %X', time.localtime())
psdb.add('username,password,accept,ps,addtime', psdb.add('username,password,accept,ps,addtime',
(username, password, '%', '', addTime)) (username, password, '%', '', addTime))
@@ -1922,6 +1963,123 @@ def getSlaveSSHList(version=''):
return mw.getJson(data) return mw.getJson(data)
def getSlaveSyncUserByIp(version=''):
args = getArgs()
data = checkArgs(args, ['ip'])
if not data[0]:
return data[1]
ip = args['ip']
conn = pSqliteDb('slave_sync_user')
data = conn.field('ip,port,user,pass,mode,cmd').where(
"ip=?", (ip,)).select()
return mw.returnJson(True, 'ok', data)
def addSlaveSyncUser(version=''):
import base64
args = getArgs()
data = checkArgs(args, ['ip'])
if not data[0]:
return data[1]
ip = args['ip']
if ip == "":
return mw.returnJson(True, 'ok')
data = checkArgs(args, ['port', 'user', 'pass', 'mode'])
if not data[0]:
return data[1]
cmd = args['cmd']
port = args['port']
user = args['user']
apass = args['pass']
mode = args['mode']
addTime = time.strftime('%Y-%m-%d %X', time.localtime())
conn = pSqliteDb('slave_sync_user')
data = conn.field('ip').where("ip=?", (ip,)).select()
if len(data) > 0:
res = conn.where("ip=?", (ip,)).save(
'port,user,pass,mode,cmd', (port, user, apass, mode, cmd))
else:
conn.add('ip,port,user,cmd,user,pass,mode,addtime',
(ip, port, user, cmd, user, apass, mode, addTime))
return mw.returnJson(True, '设置成功!')
def delSlaveSyncUser(version=''):
args = getArgs()
data = checkArgs(args, ['ip'])
if not data[0]:
return data[1]
ip = args['ip']
conn = pSqliteDb('slave_sync_user')
conn.where("ip=?", (ip,)).delete()
return mw.returnJson(True, '删除成功!')
def getSlaveSyncUserList(version=''):
args = getArgs()
data = checkArgs(args, ['page', 'page_size'])
if not data[0]:
return data[1]
page = int(args['page'])
page_size = int(args['page_size'])
conn = pSqliteDb('slave_sync_user')
limit = str((page - 1) * page_size) + ',' + str(page_size)
field = 'id,ip,port,user,pass,cmd,addtime'
clist = conn.field(field).limit(limit).order('id desc').select()
count = conn.count()
data = {}
_page = {}
_page['count'] = count
_page['p'] = page
_page['row'] = page_size
_page['tojs'] = args['tojs']
data['page'] = mw.getPage(_page)
data['data'] = clist
return mw.getJson(data)
def getSyncModeFile():
return getServerDir() + "/sync.mode"
def getSlaveSyncMode(version):
sync_mode = getSyncModeFile()
if os.path.exists(sync_mode):
mode = mw.readFile(sync_mode).strip()
return mw.returnJson(True, 'ok', mode)
return mw.returnJson(False, 'fail')
def setSlaveSyncMode(version):
args = getArgs()
data = checkArgs(args, ['mode'])
if not data[0]:
return data[1]
mode = args['mode']
sync_mode = getSyncModeFile()
if mode == 'none':
os.remove(sync_mode)
else:
mw.writeFile(sync_mode, mode)
return mw.returnJson(True, '设置成功', mode)
def getSlaveSSHByIp(version=''): def getSlaveSSHByIp(version=''):
args = getArgs() args = getArgs()
data = checkArgs(args, ['ip']) data = checkArgs(args, ['ip'])
@@ -2024,6 +2182,47 @@ def getSlaveSyncCmd(version=''):
def initSlaveStatus(version=''): def initSlaveStatus(version=''):
mode_file = getSyncModeFile()
if not os.path.exists(mode_file):
return mw.returnJson(False, '需要先设置同步配置')
mode = mw.readFile(mode_file)
if mode == 'ssh':
return initSlaveStatusSSH(version)
if mode == 'sync-user':
return initSlaveStatusSyncUser(version)
def initSlaveStatusSyncUser(version=''):
conn = pSqliteDb('slave_sync_user')
data = conn.field('ip,port,user,pass,mode,cmd').find()
if len(data) < 1:
return mw.returnJson(False, '需要先添加同步用户配置!')
# print(data)
db = pMysqlDb()
dlist = db.query('show slave status')
if len(dlist) > 0:
return mw.returnJson(False, '已经初始化好了zz...')
u = data
mode_name = 'classic'
if u['mode'] == '1':
mode_name = 'gtid'
local_mode = recognizeDbMode()
if local_mode != mode_name:
return mw.returnJson(False, '同步模式不一致!')
t = db.query(u['cmd'])
# print(t)
db.query("start slave user='{}' password='{}';".format(
u['user'], u['pass']))
return mw.returnJson(True, '初始化成功!')
def initSlaveStatusSSH(version=''):
db = pMysqlDb() db = pMysqlDb()
dlist = db.query('show slave status') dlist = db.query('show slave status')
if len(dlist) > 0: if len(dlist) > 0:
@@ -2092,6 +2291,41 @@ def initSlaveStatus(version=''):
def setSlaveStatus(version=''): def setSlaveStatus(version=''):
mode_file = getSyncModeFile()
if not os.path.exists(mode_file):
return mw.returnJson(False, '需要先设置同步配置')
mode = mw.readFile(mode_file)
if mode == 'ssh':
return setSlaveStatusSSH(version)
if mode == 'sync-user':
return setSlaveStatusSyncUser(version)
def setSlaveStatusSyncUser(version=''):
db = pMysqlDb()
dlist = db.query('show slave status')
if len(dlist) == 0:
return mw.returnJson(False, '需要手动添加同步账户或者执行初始化!')
if len(dlist) > 0 and (dlist[0]["Slave_IO_Running"] == 'Yes' or dlist[0]["Slave_SQL_Running"] == 'Yes'):
db.query('stop slave')
else:
ip = dlist[0]['Master_Host']
conn = pSqliteDb('slave_sync_user')
data = conn.field('ip,port,user,pass,mode,cmd').find()
if len(data) == 0:
return mw.returnJson(False, '没有数据无法重启!')
user = data['user']
apass = data['pass']
db.query("start slave")
# db.query("start slave user='{}' password='{}';".format(user, apass))
return mw.returnJson(True, '设置成功!')
def setSlaveStatusSSH(version=''):
db = pMysqlDb() db = pMysqlDb()
dlist = db.query('show slave status') dlist = db.query('show slave status')
@@ -2160,6 +2394,69 @@ def writeDbSyncStatus(data):
def doFullSync(version=''): def doFullSync(version=''):
mode_file = getSyncModeFile()
if not os.path.exists(mode_file):
return mw.returnJson(False, '需要先设置同步配置')
mode = mw.readFile(mode_file)
if mode == 'ssh':
return doFullSyncSSH(version)
if mode == 'sync-user':
return doFullSyncUser(version)
def doFullSyncUser(version=''):
args = getArgs()
data = checkArgs(args, ['db'])
if not data[0]:
return data[1]
sync_db = args['db']
db = pMysqlDb()
conn = pSqliteDb('slave_sync_user')
data = conn.field('ip,port,user,pass,mode,cmd').find()
user = data['user']
apass = data['pass']
port = data['port']
ip = data['ip']
bak_file = '/tmp/tmp.sql'
writeDbSyncStatus({'code': 0, 'msg': '开始同步...', 'progress': 0})
dmp_option = ''
mode = recognizeDbMode()
if mode == 'gtid':
dmp_option = ' --set-gtid-purged=off '
writeDbSyncStatus({'code': 1, 'msg': '远程导出数据...', 'progress': 20})
if not os.path.exists(bak_file):
dump_sql_data = getServerDir() + "/bin/mysqldump " + dmp_option + " --force --opt --default-character-set=utf8 --single-transaction -h" + ip + " -P" + \
port + " -u" + user + " -p" + apass + \
" --ssl-mode=DISABLED " + sync_db + " > " + bak_file
mw.execShell(dump_sql_data)
writeDbSyncStatus({'code': 2, 'msg': '本地导入数据...', 'progress': 40})
if os.path.exists(bak_file):
pwd = pSqliteDb('config').where('id=?', (1,)).getField('mysql_root')
sock = getSocketFile()
my_import_cmd = getServerDir() + '/bin/mysql -S ' + sock + ' -uroot -p' + pwd + \
' ' + sync_db + ' < ' + bak_file
mw.execShell(my_import_cmd)
if version == '8.0':
db.query("start slave user='{}' password='{}';".format(user, apass))
else:
db.query("start slave")
writeDbSyncStatus({'code': 6, 'msg': '从库重启完成...', 'progress': 100})
os.system("rm -rf " + bak_file)
return True
def doFullSyncSSH(version=''):
args = getArgs() args = getArgs()
data = checkArgs(args, ['db']) data = checkArgs(args, ['db'])
@@ -2343,6 +2640,9 @@ def uninstallPreInspection(version):
if mw.isDebugMode(): if mw.isDebugMode():
return 'ok' return 'ok'
import plugins_api
plugins_api.plugins_api().removeIndex(getPluginName(), version)
return "请手动删除MySQL[{}]<br/> rm -rf {}".format(version, getServerDir()) return "请手动删除MySQL[{}]<br/> rm -rf {}".format(version, getServerDir())
if __name__ == "__main__": if __name__ == "__main__":
@@ -2392,7 +2692,7 @@ if __name__ == "__main__":
elif func == 'my_db_pos': elif func == 'my_db_pos':
print(getMyDbPos()) print(getMyDbPos())
elif func == 'set_db_pos': elif func == 'set_db_pos':
print(setMyDbPos()) print(setMyDbPos(version))
elif func == 'my_port': elif func == 'my_port':
print(getMyPort()) print(getMyPort())
elif func == 'set_my_port': elif func == 'set_my_port':
@@ -2485,6 +2785,18 @@ if __name__ == "__main__":
print(delSlaveSSH(version)) print(delSlaveSSH(version))
elif func == 'update_slave_ssh': elif func == 'update_slave_ssh':
print(updateSlaveSSH(version)) print(updateSlaveSSH(version))
elif func == 'get_slave_sync_user_list':
print(getSlaveSyncUserList(version))
elif func == 'get_slave_sync_user_by_ip':
print(getSlaveSyncUserByIp(version))
elif func == 'add_slave_sync_user':
print(addSlaveSyncUser(version))
elif func == 'del_slave_sync_user':
print(delSlaveSyncUser(version))
elif func == 'get_slave_sync_mode':
print(getSlaveSyncMode(version))
elif func == 'set_slave_sync_mode':
print(setSlaveSyncMode(version))
elif func == 'init_slave_status': elif func == 'init_slave_status':
print(initSlaveStatus(version)) print(initSlaveStatus(version))
elif func == 'set_slave_status': elif func == 'set_slave_status':
+1 -1
View File
@@ -4,7 +4,7 @@
"tip":"soft", "tip":"soft",
"name":"mysql-yum", "name":"mysql-yum",
"type":"运行环境", "type":"运行环境",
"ps":"[<b style='color:red;'>潜龙勿用</b>]一种关系数据库管理系统[centos](极速安装)", "ps":"一种关系数据库管理系统[centos](极速安装)",
"todo_versions":["5.7","8.0"], "todo_versions":["5.7","8.0"],
"versions":["5.7","8.0"], "versions":["5.7","8.0"],
"shell":"install.sh", "shell":"install.sh",
+280 -19
View File
@@ -265,7 +265,7 @@ function myPerfOpt() {
} }
function reBootMySqld(){ function reBootMySqld(){
pluginOpService('mysql-apt','restart',''); pluginOpService('mysql-yum','restart','');
} }
@@ -731,7 +731,7 @@ function addDatabase(type){
layer.close(index); layer.close(index);
dbList(); dbList();
} }
},{icon: rdata.status ? 1 : 2},600); },{icon: rdata.status ? 1 : 2}, 2000);
}); });
} }
}); });
@@ -818,8 +818,8 @@ function openPhpmyadmin(name,username,password){
data = syncPost('/plugins/run',{'name':'phpmyadmin','func':'get_cfg'}); data = syncPost('/plugins/run',{'name':'phpmyadmin','func':'get_cfg'});
var rdata = $.parseJSON(data.data); var rdata = $.parseJSON(data.data);
if (rdata.choose != 'mysql-apt'){ if (rdata.choose != 'mysql-yum'){
layer.msg('当前为['+rdata.choose+']模式,若要使用请切换模式.',{icon:2,shade: [0.3, '#000']}); layer.msg('当前为['+rdata.choose+']模式,若要使用请修改phpMyAdmin访问切换.',{icon:2,shade: [0.3, '#000']});
return; return;
} }
@@ -1207,8 +1207,8 @@ function myLogs(){
var limitCon = '<p class="conf_p">\ var limitCon = '<p class="conf_p">\
<span class="f14 c6 mr20">二进制日志 </span><span class="f14 c6 mr20">' + toSize(rdata.msg) + '</span>\ <span class="f14 c6 mr20">二进制日志 </span><span class="f14 c6 mr20">' + toSize(rdata.msg) + '</span>\
'+line_status+'\ '+line_status+'\
<p class="f14 c6 mtb10" style="border-top:#ddd 1px solid; padding:10px 0">错误日志<button class="btn btn-default btn-clear btn-xs" style="float:right;">清理日志</button></p>\ <p class="f14 c6 mtb10" style="border-top:#ddd 1px solid; padding:10px 0">错误日志<button class="btn btn-default btn-clear btn-xs" style="float:right;" >清理日志</button></p>\
<textarea readonly style="margin: 0px;width: 100%;height: 440px;background-color: #333;color:#fff; padding:0 5px" id="error_log"></textarea>\ <textarea readonly style="margin: 0px;width: 100%;height: 438px;background-color: #333;color:#fff; padding:0 5px" id="error_log"></textarea>\
</p>'; </p>';
$(".soft-man-con").html(limitCon); $(".soft-man-con").html(limitCon);
@@ -1545,13 +1545,9 @@ function getMasterRepSlaveUserCmd(username, db=''){
function delMasterRepSlaveUser(username){ function delMasterRepSlaveUser(username){
myPost('del_master_rep_slave_user', {username:username}, function(data){ myPost('del_master_rep_slave_user', {username:username}, function(data){
var rdata = $.parseJSON(data.data); var rdata = $.parseJSON(data.data);
layer.msg(rdata.msg); showMsg(rdata.msg, function(){
$('.layui-layer-close1').click();
setTimeout(function(){
getMasterRepSlaveList(); getMasterRepSlaveList();
},1000); },{icon: rdata.status ? 1 : 2},1000)
}); });
} }
@@ -1821,8 +1817,23 @@ function addSlaveSSH(ip=''){
function delSlaveSSH(ip){ function delSlaveSSH(ip){
myPost('del_slave_ssh', {ip:ip}, function(rdata){ myPost('del_slave_ssh', {ip:ip}, function(rdata){
var rdata = $.parseJSON(rdata.data); var rdata = $.parseJSON(rdata.data);
layer.msg(rdata.msg, {icon: rdata.status ? 1 : 2}); showMsg(rdata.msg,function(){
getSlaveSSHPage(); if (rdata.status){
getSlaveSSHPage();
}
},{icon: rdata.status ? 1 : 2}, 600);
});
}
function delSlaveSyncUser(ip){
myPost('del_slave_sync_user', {ip:ip}, function(rdata){
var rdata = $.parseJSON(rdata.data);
showMsg(rdata.msg,function(){
if (rdata.status){
getSlaveSyncUserPage();
}
},{icon: rdata.status ? 1 : 2}, 600);
}); });
} }
@@ -1872,6 +1883,237 @@ function getSlaveSSHPage(page=1){
} }
function addSlaveSyncUser(ip=''){
myPost('get_slave_sync_user_by_ip', {ip:ip}, function(rdata){
var rdata = $.parseJSON(rdata.data);
var ip = '127.0.0.1';
var port = "22";
var cmd = '';
var user = 'input_sync_user';
var pass = 'input_sync_pwd';
var mode = '0';
if (rdata.data.length>0){
ip = rdata.data[0]['ip'];
port = rdata.data[0]['port'];
cmd = rdata.data[0]['cmd'];
user = rdata.data[0]['user'];
pass = rdata.data[0]['pass'];
mode = rdata.data[0]['mode'];
}
var index = layer.open({
type: 1,
area: ['500px','470px'],
title: '同步账户',
closeBtn: 1,
shift: 5,
shadeClose: true,
btn:["确认","取消"],
content: "<form class='bt-form pd20'>\
<div class='line'><span class='tname'>IP</span><div class='info-r'><input name='ip' class='bt-input-text mr5' type='text' style='width:330px;' value='"+ip+"'></div></div>\
<div class='line'><span class='tname'>端口</span><div class='info-r'><input name='port' class='bt-input-text mr5' type='number' style='width:330px;' value='"+port+"'></div></div>\
<div class='line'><span class='tname'>同步账户</span><div class='info-r'><input name='user' class='bt-input-text mr5' type='text' style='width:330px;' value='"+user+"'></div></div>\
<div class='line'><span class='tname'>同步密码</span><div class='info-r'><input name='pass' class='bt-input-text mr5' type='text' style='width:330px;' value='"+pass+"'></div></div>\
<div class='line'>\
<span class='tname'>CMD[最好填好]</span>\
<div class='info-r'><textarea class='bt-input-text mr5' row='20' cols='30' name='cmd' style='width:330px;height:150px;'></textarea></div>\
</div>\
<input type='hidden' name='mode' value='"+mode+"' />\
</form>",
success:function(){
$('textarea[name="cmd"]').html(cmd);
$('textarea[name="cmd"]').change(function(){
var val = $(this).val();
var vlist = val.split(',');
var a = {};
for (var i in vlist) {
var tmp = toTrim(vlist[i]);
var tmp_a = tmp.split(" ");
var real_tmp = tmp_a[tmp_a.length-1];
var kv = real_tmp.split("=");
a[kv[0]] = kv[1].replace("'",'').replace("'",'');
}
$('input[name="ip"]').val(a['MASTER_HOST']);
$('input[name="port"]').val(a['MASTER_PORT']);
$('input[name="user"]').val(a['MASTER_USER']);
$('input[name="pass"]').val(a['MASTER_PASSWORD']);
console.log(a['MASTER_AUTO_POSITION'],typeof(a['MASTER_AUTO_POSITION']));
if (typeof(a['MASTER_AUTO_POSITION']) != 'undefined' ){
$('input[name="mode"]').val('1');
}
});
},
yes:function(index){
var ip = $('input[name="ip"]').val();
var port = $('input[name="port"]').val();
var user = $('input[name="user"]').val();
var pass = $('input[name="pass"]').val();
var cmd = $('textarea[name="cmd"]').val();
var mode = $('input[name="mode"]').val();
var data = {ip:ip,port:port,cmd:cmd,user:user,pass:pass,mode:mode};
myPost('add_slave_sync_user', data, function(ret_data){
layer.close(index);
var rdata = $.parseJSON(ret_data.data);
showMsg(rdata.msg,function(){
if (rdata.status){
getSlaveSyncUserPage();
}
},{icon: rdata.status ? 1 : 2},600);
});
}
});
});
}
function getSlaveSyncUserPage(page=1){
var _data = {};
_data['page'] = page;
_data['page_size'] = 5;
_data['tojs'] ='getSlaveSyncUserPage';
myPost('get_slave_sync_user_list', _data, function(data){
var layerId = null;
var rdata = [];
try {
rdata = $.parseJSON(data.data);
} catch(e) {
console.log(e);
}
var list = '';
var user_list = rdata['data'];
for (i in user_list) {
var ip = user_list[i]['ip'];
var port = user_list[i]['port'];
var user = user_list[i]['user'];
var apass = user_list[i]['pass'];
var cmd = '未设置';
if (user_list[i]['cmd']!=''){
cmd = '已设置';
}
list += '<tr><td>'+ip+'</td>\
<td>'+port+'</td>\
<td>'+user+'</td>\
<td>'+apass+'</td>\
<td>'+cmd+'</td>\
<td>\
<a class="btlink" onclick="addSlaveSyncUser(\''+ip+'\');">修改</a> | \
<a class="btlink" onclick="delSlaveSyncUser(\''+ip+'\');">删除</a>\
</td>\
</tr>';
}
$('.get-slave-ssh-list tbody').html(list);
$('.dataTables_paginate_4').html(rdata['page']);
});
}
function getSlaveCfg(){
myPost('get_slave_sync_mode', '', function(data){
var rdata = $.parseJSON(data.data);
var mode_none = 'success';
var mode_ssh = 'danger';
var mode_sync_user = 'danger';
if(rdata.status){
var mode_none = 'danger';
if (rdata.data == 'ssh'){
var mode_ssh = 'success';
var mode_sync_user = 'danger';
} else {
var mode_ssh = 'danger';
var mode_sync_user = 'success';
}
}
layerId = layer.open({
type: 1,
title: '同步配置',
area: ['400px','180px'],
content:"<div class='bt-form pd20 c6'>\
<p class='conf_p'>\
<span class='f14 c6 mr20'>当前从库同步模式</span>\
<b class='f14 c6 mr20'></b>\
<button class='btn btn-"+mode_none+" btn-xs slave-db-mode btn-none'>无</button>\
<button class='btn btn-"+mode_ssh+" btn-xs slave-db-mode btn-ssh'>SSH</button>\
<button class='btn btn-"+mode_sync_user+" btn-xs slave-db-mode btn-sync-user'>同步账户</button>\
</p>\
<hr />\
<p class='conf_p'>\
<span class='f14 c6 mr20'>配置设置</span>\
<b class='f14 c6 mr20'></b>\
<button class='btn btn-success btn-xs btn-slave-ssh'>SSH</button>\
<button class='btn btn-success btn-xs btn-slave-user'>同步账户</button>\
</p>\
</div>",
success:function(){
$('.btn-slave-ssh').click(function(){
getSlaveSSHList();
});
$('.btn-slave-user').click(function(){
getSlaveUserList();
});
$('.slave-db-mode').click(function(){
var _this = this;
var mode = 'none';
if ($(this).hasClass('btn-ssh')){
mode = 'ssh';
}
if ($(this).hasClass('btn-sync-user')){
mode = 'sync-user';
}
myPost('set_slave_sync_mode', {mode:mode}, function(data){
var rdata = $.parseJSON(data.data);
showMsg(rdata.msg,function(){
$('.slave-db-mode').remove('btn-success').addClass('btn-danger');
$(_this).removeClass('btn-danger').addClass('btn-success');
},{icon:rdata.status?1:2},2000);
});
});
}
});
});
}
function getSlaveUserList(){
var page = '<div class="dataTables_paginate_4 dataTables_paginate paging_bootstrap page" style="margin-top:0px;"></div>';
page += '<div class="table_toolbar" style="left:0px;"><span class="sync btn btn-default btn-sm" onclick="addSlaveSyncUser()" title="">添加同步账户</span></div>';
layerId = layer.open({
type: 1,
title: '同步账户列表',
area: '500px',
content:"<div class='bt-form pd20 c6'>\
<div class='divtable mtb10'>\
<div><table class='table table-hover get-slave-ssh-list'>\
<thead><tr><th>IP</th><th>PORT</th><th>同步账户</th><th>同步密码</th><th>CMD</th><th>操作</th></tr></thead>\
<tbody></tbody>\
</table></div>\
"+page +"\
</div>\
</div>",
success:function(){
getSlaveSyncUserPage(1);
}
});
}
function getSlaveSSHList(page=1){ function getSlaveSSHList(page=1){
var page = '<div class="dataTables_paginate_4 dataTables_paginate paging_bootstrap page" style="margin-top:0px;"></div>'; var page = '<div class="dataTables_paginate_4 dataTables_paginate paging_bootstrap page" style="margin-top:0px;"></div>';
@@ -1991,7 +2233,7 @@ function masterOrSlaveConf(version=''){
for(i in rdata.data){ for(i in rdata.data){
var v = rdata.data[i]; var v = rdata.data[i];
var status = "异常"; var status = "<a class='btlink db_error'>异常</>";
if (v['Slave_SQL_Running'] == 'Yes' && v['Slave_IO_Running'] == 'Yes'){ if (v['Slave_SQL_Running'] == 'Yes' && v['Slave_IO_Running'] == 'Yes'){
status = "正常"; status = "正常";
} }
@@ -2033,6 +2275,26 @@ function masterOrSlaveConf(version=''){
// <span class="sync btn btn-default btn-sm" onclick="getMasterRepSlaveList()" title="">添加</span>\ // <span class="sync btn btn-default btn-sm" onclick="getMasterRepSlaveList()" title="">添加</span>\
// </div> // </div>
$(".table_slave_status_list").html(con); $(".table_slave_status_list").html(con);
$('.db_error').click(function(){
layer.open({
type: 1,
title: '同步异常信息',
area: '500px',
content:"<form class='bt-form pd20 pb70'>\
<div class='line'>"+v['Error']+"</div>\
<div class='bt-form-submit-btn'>\
<button type='button' class='btn btn-success btn-sm btn-title class-copy-db-err'>复制</button>\
</div>\
</form>",
success:function(){
copyText(v['Error']);
$('.class-copy-db-err').click(function(){
copyText(v['Error']);
});
}
});
});
}); });
} }
@@ -2079,7 +2341,6 @@ function masterOrSlaveConf(version=''){
}); });
} }
function getMasterStatus(){ function getMasterStatus(){
myPost('get_master_status', '', function(rdata){ myPost('get_master_status', '', function(rdata){
@@ -2110,7 +2371,7 @@ function masterOrSlaveConf(version=''){
<p class="conf_p">\ <p class="conf_p">\
<span class="f14 c6 mr20">Slave[从]配置</span><span class="f14 c6 mr20"></span>\ <span class="f14 c6 mr20">Slave[从]配置</span><span class="f14 c6 mr20"></span>\
<button class="btn '+(!rdata.slave_status ? 'btn-danger' : 'btn-success')+' btn-xs btn-slave">'+(!rdata.slave_status ? '未启动' : '已启动') +'</button>\ <button class="btn '+(!rdata.slave_status ? 'btn-danger' : 'btn-success')+' btn-xs btn-slave">'+(!rdata.slave_status ? '未启动' : '已启动') +'</button>\
<button class="btn btn-success btn-xs" onclick="getSlaveSSHList()" >[主]SSH配置</button>\ <button class="btn btn-success btn-xs" onclick="getSlaveCfg()" >同步配置</button>\
<button class="btn btn-success btn-xs" onclick="initSlaveStatus()" >初始化</button>\ <button class="btn btn-success btn-xs" onclick="initSlaveStatus()" >初始化</button>\
</p>\ </p>\
<hr/>\ <hr/>\
@@ -2184,10 +2445,10 @@ function masterOrSlaveConf(version=''){
getMasterDbList(); getMasterDbList();
} }
if (rdata.slave_status){ // if (rdata.slave_status){
getAsyncMasterDbList(); getAsyncMasterDbList();
getAsyncDataList() getAsyncDataList()
} // }
}); });
} }
getMasterStatus(); getMasterStatus();
+3 -1
View File
@@ -67,8 +67,10 @@ binlog-ignore-db = performance_schema
#slave #slave
log-slave-updates = 1 log-slave-updates = 1
skip-slave-start = 1 # Prevent replication from starting automatically with MySQL
#skip-slave-start = 1
#replicate-do-db #replicate-do-db
slave_skip_errors=1062
replicate-ignore-db = information_schema replicate-ignore-db = information_schema
replicate-ignore-db = performance_schema replicate-ignore-db = performance_schema
replicate-ignore-db = mysql replicate-ignore-db = mysql
+3 -1
View File
@@ -69,8 +69,10 @@ binlog-ignore-db = performance_schema
#slave #slave
log-slave-updates = 1 log-slave-updates = 1
skip-slave-start = 1 # Prevent replication from starting automatically with MySQL
#skip-slave-start = 1
#replicate-do-db #replicate-do-db
slave_skip_errors=1062
replicate-ignore-db = information_schema replicate-ignore-db = information_schema
replicate-ignore-db = performance_schema replicate-ignore-db = performance_schema
replicate-ignore-db = mysql replicate-ignore-db = mysql
+6 -5
View File
@@ -8,9 +8,6 @@ default-character-set = UTF8MB4
[mysqld] [mysqld]
!include {$SERVER_APP_PATH}/etc/mode/classic.cnf !include {$SERVER_APP_PATH}/etc/mode/classic.cnf
;sha256_password_private_key_path=mysql.pem
;sha256_password_public_key_path=mysql.pub
pid-file = {$SERVER_APP_PATH}/data/mysql.pid pid-file = {$SERVER_APP_PATH}/data/mysql.pid
user = mysql user = mysql
port = 3306 port = 3306
@@ -44,6 +41,7 @@ open_files_limit = 2560
max_allowed_packet = 128M max_allowed_packet = 128M
skip_name_resolve = 1 skip_name_resolve = 1
skip-ssl
#skip-networking #skip-networking
#skip-external-locking #skip-external-locking
#loose-skip-innodb #loose-skip-innodb
@@ -70,9 +68,12 @@ binlog-ignore-db = information_schema
binlog-ignore-db = performance_schema binlog-ignore-db = performance_schema
#slave #slave
log-slave-updates = 1 # 5.7 It can not be opened later
skip-slave-start = 1 #log-slave-updates = 1
# Prevent replication from starting automatically with MySQL
#skip-slave-start = 1
#replicate-do-db #replicate-do-db
slave_skip_errors=1062
replicate-ignore-db = information_schema replicate-ignore-db = information_schema
replicate-ignore-db = performance_schema replicate-ignore-db = performance_schema
replicate-ignore-db = mysql replicate-ignore-db = mysql
+3 -1
View File
@@ -68,8 +68,10 @@ binlog-ignore-db = performance_schema
#slave #slave
log_replica_updates = 1 log_replica_updates = 1
skip_replica_start = 1 # Prevent replication from starting automatically with MySQL
#skip_replica_start = 1
#replicate-do-db #replicate-do-db
replica_skip_errors=1062
replicate-ignore-db = information_schema replicate-ignore-db = information_schema
replicate-ignore-db = performance_schema replicate-ignore-db = performance_schema
replicate-ignore-db = mysql replicate-ignore-db = mysql
+13
View File
@@ -40,4 +40,17 @@ CREATE TABLE IF NOT EXISTS `slave_id_rsa` (
`addtime` TEXT `addtime` TEXT
); );
-- 从库配置主库的[user]
-- drop table `slave_user`;
CREATE TABLE IF NOT EXISTS `slave_sync_user` (
`id` INTEGER PRIMARY KEY AUTOINCREMENT,
`ip` TEXT,
`port` TEXT,
`user` TEXT,
`pass` TEXT,
`mode` TEXT,
`cmd` TEXT,
`addtime` TEXT
);
+2 -2
View File
@@ -16,7 +16,7 @@
<p onclick="masterOrSlaveConf($('.plugin_version').attr('version'))">主从配置</p> <p onclick="masterOrSlaveConf($('.plugin_version').attr('version'))">主从配置</p>
</div> </div>
<div class="bt-w-con pd15"> <div class="bt-w-con pd15">
<div class="soft-man-con" style="height: 530px; overflow: auto;"></div> <div class="soft-man-con"></div>
</div> </div>
</div> </div>
@@ -53,7 +53,7 @@
.conf_p span { .conf_p span {
display: inline-block; display: inline-block;
margin-right: 10px; margin-right: 10px;
width: 135px; width: 115px;
text-align: right; text-align: right;
} }
</style> </style>
+401 -38
View File
@@ -48,19 +48,20 @@ def getInitDFile():
def getArgs(): def getArgs():
args = sys.argv[2:] args = sys.argv[2:]
tmp = {} tmp = {}
args_len = len(args) args_len = len(args)
if args_len == 1: if args_len == 1:
t = args[0].strip('{').strip('}') t = args[0].strip('{').strip('}')
t = t.split(':') if t.strip() == '':
tmp = []
else:
t = t.split(':', 1)
tmp[t[0]] = t[1]
tmp[t[0]] = t[1] tmp[t[0]] = t[1]
elif args_len > 1: elif args_len > 1:
for i in range(len(args)): for i in range(len(args)):
t = args[i].split(':') t = args[i].split(':', 1)
tmp[t[0]] = t[1] tmp[t[0]] = t[1]
return tmp return tmp
@@ -127,20 +128,27 @@ def contentReplace(content):
def pSqliteDb(dbname='databases'): def pSqliteDb(dbname='databases'):
file = getServerDir() + '/mysql.db' file = getServerDir() + '/mysql.db'
name = 'mysql' name = 'mysql'
if not os.path.exists(file):
import_sql = mw.readFile(getPluginDir() + '/conf/mysql.sql')
md5_sql = mw.md5(import_sql)
import_sign = False
save_md5_file = getServerDir() + '/import_sql.md5'
if os.path.exists(save_md5_file):
save_md5_sql = mw.readFile(save_md5_file)
if save_md5_sql != md5_sql:
import_sign = True
mw.writeFile(save_md5_file, md5_sql)
else:
mw.writeFile(save_md5_file, md5_sql)
if not os.path.exists(file) or import_sql:
conn = mw.M(dbname).dbPos(getServerDir(), name) conn = mw.M(dbname).dbPos(getServerDir(), name)
csql = mw.readFile(getPluginDir() + '/conf/mysql.sql') csql_list = import_sql.split(';')
csql_list = csql.split(';')
for index in range(len(csql_list)): for index in range(len(csql_list)):
conn.execute(csql_list[index], ()) conn.execute(csql_list[index], ())
else:
# 现有run conn = mw.M(dbname).dbPos(getServerDir(), name)
# conn = mw.M(dbname).dbPos(getServerDir(), name)
# csql = mw.readFile(getPluginDir() + '/conf/mysql.sql')
# csql_list = csql.split(';')
# for index in range(len(csql_list)):
# conn.execute(csql_list[index], ())
conn = mw.M(dbname).dbPos(getServerDir(), name)
return conn return conn
@@ -481,14 +489,14 @@ def initMysql8Pwd():
# 删除冗余账户 # 删除冗余账户
hostname = mw.execShell('hostname')[0].strip() hostname = mw.execShell('hostname')[0].strip()
if hostname != 'localhost':
drop_hostname = serverdir + '/bin/mysql --defaults-file=' + \
myconf + ' -uroot -p' + pwd + ' -e "drop user \'\'@\'' + hostname + '\'";'
mw.execShell(drop_hostname)
drop_hostname = serverdir + '/bin/mysql --defaults-file=' + \ drop_root_hostname = serverdir + '/bin/mysql --defaults-file=' + \
myconf + ' -uroot -p' + pwd + ' -e "drop user \'\'@\'' + hostname + '\'";' myconf + ' -uroot -p' + pwd + ' -e "drop user \'root\'@\'' + hostname + '\'";'
mw.execShell(drop_hostname) mw.execShell(drop_root_hostname)
drop_root_hostname = serverdir + '/bin/mysql --defaults-file=' + \
myconf + ' -uroot -p' + pwd + ' -e "drop user \'root\'@\'' + hostname + '\'";'
mw.execShell(drop_root_hostname)
pSqliteDb('config').where('id=?', (1,)).save('mysql_root', (pwd,)) pSqliteDb('config').where('id=?', (1,)).save('mysql_root', (pwd,))
@@ -835,12 +843,29 @@ def setDbBackup():
return data[1] return data[1]
scDir = mw.getRunDir() + '/scripts/backup.py' scDir = mw.getRunDir() + '/scripts/backup.py'
cmd = 'python3 ' + scDir + ' database ' + args['name'] + ' 3' cmd = 'python3 ' + scDir + ' database ' + args['name'] + ' 3'
os.system(cmd) os.system(cmd)
return mw.returnJson(True, 'ok') return mw.returnJson(True, 'ok')
# 数据库密码处理
def myPass(act, root):
conf_file = getConf()
mw.execShell("sed -i '/user=root/d' {}".format(conf_file))
mw.execShell("sed -i '/password=/d' {}".format(conf_file))
if act:
mycnf = mw.readFile(conf_file)
src_dump = "[mysqldump]\n"
sub_dump = src_dump + "user=root\npassword=\"{}\"\n".format(root)
if not mycnf:
return False
mycnf = mycnf.replace(src_dump, sub_dump)
if len(mycnf) > 100:
mw.writeFile(conf_file, mycnf)
return True
return True
def importDbExternal(): def importDbExternal():
args = getArgs() args = getArgs()
data = checkArgs(args, ['file', 'name']) data = checkArgs(args, ['file', 'name'])
@@ -895,15 +920,22 @@ def importDbExternal():
pwd = pSqliteDb('config').where('id=?', (1,)).getField('mysql_root') pwd = pSqliteDb('config').where('id=?', (1,)).getField('mysql_root')
sock = getSocketFile() sock = getSocketFile()
os.environ["MYSQL_PWD"] = pwd my_cnf = getConf()
mysql_cmd = getServerDir() + '/bin/mysql -S ' + sock + ' -uroot -p' + \
pwd + ' ' + name + ' < ' + import_sql myPass(True, pwd)
mysql_cmd = getServerDir() + '/bin/mysql --defaults-file=' + my_cnf + \
' -uroot -p\"' + pwd + '\" -f ' + name + ' < ' + import_sql
# print(mysql_cmd) # print(mysql_cmd)
os.system(mysql_cmd) rdata = mw.execShell(mysql_cmd)
myPass(False, pwd)
# print(rdata)
if ext != 'sql': if ext != 'sql':
os.remove(import_sql) os.remove(import_sql)
if rdata[1].lower().find('error') > -1:
return mw.returnJson(False, rdata[1])
return mw.returnJson(True, 'ok') return mw.returnJson(True, 'ok')
@@ -923,13 +955,23 @@ def importDbBackup():
cmd = 'cd ' + mw.getRootDir() + '/backup/database && gzip -d ' + file cmd = 'cd ' + mw.getRootDir() + '/backup/database && gzip -d ' + file
mw.execShell(cmd) mw.execShell(cmd)
local_mode = recognizeDbMode()
if local_mode == 'gtid':
pdb = pMysqlDb()
pdb.execute('reset master')
pwd = pSqliteDb('config').where('id=?', (1,)).getField('mysql_root') pwd = pSqliteDb('config').where('id=?', (1,)).getField('mysql_root')
sock = getSocketFile() sock = getSocketFile()
mysql_cmd = getServerDir() + '/bin/mysql -S ' + sock + ' -uroot -p' + pwd + \ mysql_cmd = getServerDir() + '/bin/mysql -S ' + sock + ' -uroot -p' + pwd + \
' ' + name + ' < ' + file_path_sql ' ' + name + ' < ' + file_path_sql
# print(mysql_cmd) # print(mysql_cmd)
os.system(mysql_cmd) # os.system(mysql_cmd)
rdata = mw.execShell(mysql_cmd)
if rdata[1].lower().find('error') > -1:
return mw.returnJson(False, rdata[1])
return mw.returnJson(True, 'ok') return mw.returnJson(True, 'ok')
@@ -1260,7 +1302,7 @@ def addDb():
dataAccess = args['dataAccess'].strip() dataAccess = args['dataAccess'].strip()
ps = args['ps'].strip() ps = args['ps'].strip()
reg = "^[\w\.-]+$" reg = "^[\w-]+$"
if not re.match(reg, args['name']): if not re.match(reg, args['name']):
return mw.returnJson(False, '数据库名称不能带有特殊符号!') return mw.returnJson(False, '数据库名称不能带有特殊符号!')
checks = ['root', 'mysql', 'test', 'sys', 'panel_logs'] checks = ['root', 'mysql', 'test', 'sys', 'panel_logs']
@@ -1928,7 +1970,7 @@ def addMasterRepSlaveUser(version=''):
# address = args['address'].strip() # address = args['address'].strip()
# dataAccess = args['dataAccess'].strip() # dataAccess = args['dataAccess'].strip()
reg = "^[\w\.-]+$" reg = "^[\w-]+$"
if not re.match(reg, username): if not re.match(reg, username):
return mw.returnJson(False, '用户名不能带有特殊符号!') return mw.returnJson(False, '用户名不能带有特殊符号!')
checks = ['root', 'mysql', 'test', 'sys', 'panel_logs'] checks = ['root', 'mysql', 'test', 'sys', 'panel_logs']
@@ -1956,17 +1998,18 @@ def addMasterRepSlaveUser(version=''):
if isError != None: if isError != None:
return isError return isError
sql = "FLUSH PRIVILEGES;"
pdb.execute(sql)
else: else:
sql = "GRANT REPLICATION SLAVE ON *.* TO '" + username + \ sql = "grant replication SLAVE ON *.* TO '" + username + \
"'@'%' identified by '" + password + "';" "'@'%' identified by '" + password + "';"
result = pdb.execute(sql) result = pdb.execute(sql)
result = pdb.execute('FLUSH PRIVILEGES;')
isError = isSqlError(result) isError = isSqlError(result)
if isError != None: if isError != None:
return isError return isError
sql_select = "grant select,lock tables,PROCESS on *.* to " + username + "@'%';"
pdb.execute(sql_select)
pdb.execute('FLUSH PRIVILEGES;')
addTime = time.strftime('%Y-%m-%d %X', time.localtime()) addTime = time.strftime('%Y-%m-%d %X', time.localtime())
psdb.add('username,password,accept,ps,addtime', psdb.add('username,password,accept,ps,addtime',
(username, password, '%', '', addTime)) (username, password, '%', '', addTime))
@@ -2103,6 +2146,123 @@ def getSlaveSSHList(version=''):
return mw.getJson(data) return mw.getJson(data)
def getSlaveSyncUserByIp(version=''):
args = getArgs()
data = checkArgs(args, ['ip'])
if not data[0]:
return data[1]
ip = args['ip']
conn = pSqliteDb('slave_sync_user')
data = conn.field('ip,port,user,pass,mode,cmd').where(
"ip=?", (ip,)).select()
return mw.returnJson(True, 'ok', data)
def addSlaveSyncUser(version=''):
import base64
args = getArgs()
data = checkArgs(args, ['ip'])
if not data[0]:
return data[1]
ip = args['ip']
if ip == "":
return mw.returnJson(True, 'ok')
data = checkArgs(args, ['port', 'user', 'pass', 'mode'])
if not data[0]:
return data[1]
cmd = args['cmd']
port = args['port']
user = args['user']
apass = args['pass']
mode = args['mode']
addTime = time.strftime('%Y-%m-%d %X', time.localtime())
conn = pSqliteDb('slave_sync_user')
data = conn.field('ip').where("ip=?", (ip,)).select()
if len(data) > 0:
res = conn.where("ip=?", (ip,)).save(
'port,user,pass,mode,cmd', (port, user, apass, mode, cmd))
else:
conn.add('ip,port,user,cmd,user,pass,mode,addtime',
(ip, port, user, cmd, user, apass, mode, addTime))
return mw.returnJson(True, '设置成功!')
def delSlaveSyncUser(version=''):
args = getArgs()
data = checkArgs(args, ['ip'])
if not data[0]:
return data[1]
ip = args['ip']
conn = pSqliteDb('slave_sync_user')
conn.where("ip=?", (ip,)).delete()
return mw.returnJson(True, '删除成功!')
def getSlaveSyncUserList(version=''):
args = getArgs()
data = checkArgs(args, ['page', 'page_size'])
if not data[0]:
return data[1]
page = int(args['page'])
page_size = int(args['page_size'])
conn = pSqliteDb('slave_sync_user')
limit = str((page - 1) * page_size) + ',' + str(page_size)
field = 'id,ip,port,user,pass,cmd,addtime'
clist = conn.field(field).limit(limit).order('id desc').select()
count = conn.count()
data = {}
_page = {}
_page['count'] = count
_page['p'] = page
_page['row'] = page_size
_page['tojs'] = args['tojs']
data['page'] = mw.getPage(_page)
data['data'] = clist
return mw.getJson(data)
def getSyncModeFile():
return getServerDir() + "/sync.mode"
def getSlaveSyncMode(version):
sync_mode = getSyncModeFile()
if os.path.exists(sync_mode):
mode = mw.readFile(sync_mode).strip()
return mw.returnJson(True, 'ok', mode)
return mw.returnJson(False, 'fail')
def setSlaveSyncMode(version):
args = getArgs()
data = checkArgs(args, ['mode'])
if not data[0]:
return data[1]
mode = args['mode']
sync_mode = getSyncModeFile()
if mode == 'none':
os.remove(sync_mode)
else:
mw.writeFile(sync_mode, mode)
return mw.returnJson(True, '设置成功', mode)
def getSlaveSSHByIp(version=''): def getSlaveSSHByIp(version=''):
args = getArgs() args = getArgs()
data = checkArgs(args, ['ip']) data = checkArgs(args, ['ip'])
@@ -2160,7 +2320,7 @@ def delSlaveSSH(version=''):
conn = pSqliteDb('slave_id_rsa') conn = pSqliteDb('slave_id_rsa')
conn.where("ip=?", (ip,)).delete() conn.where("ip=?", (ip,)).delete()
return mw.returnJson(True, 'ok') return mw.returnJson(True, '删除SSH成功!')
def updateSlaveSSH(version=''): def updateSlaveSSH(version=''):
@@ -2189,6 +2349,24 @@ def getSlaveList(version=''):
tmp['Master_Log_File'] = dlist[x]["Master_Log_File"] tmp['Master_Log_File'] = dlist[x]["Master_Log_File"]
tmp['Slave_IO_Running'] = dlist[x]["Slave_IO_Running"] tmp['Slave_IO_Running'] = dlist[x]["Slave_IO_Running"]
tmp['Slave_SQL_Running'] = dlist[x]["Slave_SQL_Running"] tmp['Slave_SQL_Running'] = dlist[x]["Slave_SQL_Running"]
tmp['Last_Error'] = dlist[x]["Last_Error"]
tmp['Last_IO_Error'] = dlist[x]["Last_IO_Error"]
tmp['Last_SQL_Error'] = dlist[x]["Last_SQL_Error"]
tmp['Slave_SQL_Running_State'] = dlist[x]["Slave_SQL_Running_State"]
tmp['Error'] = ''
if tmp['Last_Error'] != '':
tmp['Error'] = tmp['Last_Error']
if tmp['Last_IO_Error'] != '':
tmp['Error'] = tmp['Last_IO_Error']
if tmp['Last_SQL_Error'] != '':
tmp['Error'] = tmp['Last_SQL_Error']
if tmp['Error'] == '':
tmp['Error'] = tmp['Slave_SQL_Running_State']
ret.append(tmp) ret.append(tmp)
data = {} data = {}
data['data'] = ret data['data'] = ret
@@ -2205,6 +2383,77 @@ def getSlaveSyncCmd(version=''):
def initSlaveStatus(version=''): def initSlaveStatus(version=''):
mode_file = getSyncModeFile()
if not os.path.exists(mode_file):
return mw.returnJson(False, '需要先设置同步配置')
mode = mw.readFile(mode_file)
if mode == 'ssh':
return initSlaveStatusSSH(version)
if mode == 'sync-user':
return initSlaveStatusSyncUser(version)
def makeSyncUsercmd(u, version=''):
mode = recognizeDbMode()
sql = ''
ip = u['ip']
port = u['port']
username = u['user']
password = u['pass']
if mode == "gtid":
sql = "CHANGE MASTER TO MASTER_HOST='" + ip + "', MASTER_PORT=" + port + ", MASTER_USER='" + \
username + "', MASTER_PASSWORD='" + \
password + "', MASTER_AUTO_POSITION=1"
if version == '8.0':
sql = "CHANGE REPLICATION SOURCE TO SOURCE_HOST='" + ip + "', SOURCE_PORT=" + port + ", SOURCE_USER='" + \
username + "', SOURCE_PASSWORD='" + \
password + "', MASTER_AUTO_POSITION=1"
return sql
def initSlaveStatusSyncUser(version=''):
conn = pSqliteDb('slave_sync_user')
data = conn.field('ip,port,user,pass,mode,cmd').find()
if len(data) < 1:
return mw.returnJson(False, '需要先添加同步用户配置!')
# print(data)
db = pMysqlDb()
dlist = db.query('show slave status')
if len(dlist) > 0:
return mw.returnJson(False, '已经初始化好了zz...')
u = data
mode_name = 'classic'
if u['mode'] == '1':
mode_name = 'gtid'
local_mode = recognizeDbMode()
if local_mode != mode_name:
return mw.returnJson(False, '同步模式不一致!')
if u['cmd'] == '' and local_mode == 'gtid':
sql = makeSyncUsercmd(u, version)
# print(sql)
t = db.query(sql)
else:
if u['cmd'] == '':
return mw.returnJson(False, '经典模式下,必须手写同步命令!')
# print(u['cmd'])
t = db.query(u['cmd'])
isError = isSqlError(t)
if isError:
return isError
db.query("start slave user='{}' password='{}';".format(
u['user'], u['pass']))
return mw.returnJson(True, '初始化成功!')
def initSlaveStatusSSH(version=''):
db = pMysqlDb() db = pMysqlDb()
dlist = db.query('show slave status') dlist = db.query('show slave status')
if len(dlist) > 0: if len(dlist) > 0:
@@ -2214,7 +2463,7 @@ def initSlaveStatus(version=''):
data = conn.field('ip,port,id_rsa').find() data = conn.field('ip,port,id_rsa').find()
if len(data) < 1: if len(data) < 1:
return mw.returnJson(False, '需要先配置【[主]SSH配置】!') return mw.returnJson(False, '需要先添加SSH配置!')
SSH_PRIVATE_KEY = "/tmp/t_ssh.txt" SSH_PRIVATE_KEY = "/tmp/t_ssh.txt"
ip = data['ip'] ip = data['ip']
@@ -2273,11 +2522,46 @@ def initSlaveStatus(version=''):
def setSlaveStatus(version=''): def setSlaveStatus(version=''):
mode_file = getSyncModeFile()
if not os.path.exists(mode_file):
return mw.returnJson(False, '需要先设置同步配置')
mode = mw.readFile(mode_file)
if mode == 'ssh':
return setSlaveStatusSSH(version)
if mode == 'sync-user':
return setSlaveStatusSyncUser(version)
def setSlaveStatusSyncUser(version=''):
db = pMysqlDb()
dlist = db.query('show slave status')
if len(dlist) == 0:
return mw.returnJson(False, '需要手动添加同步账户或者执行初始化!')
if len(dlist) > 0 and (dlist[0]["Slave_IO_Running"] == 'Yes' or dlist[0]["Slave_SQL_Running"] == 'Yes'):
db.query('stop slave')
else:
ip = dlist[0]['Master_Host']
conn = pSqliteDb('slave_sync_user')
data = conn.field('ip,port,user,pass,mode,cmd').find()
if len(data) == 0:
return mw.returnJson(False, '没有数据无法重启!')
user = data['user']
apass = data['pass']
db.query("start slave")
# db.query("start slave user='{}' password='{}';".format(user, apass))
return mw.returnJson(True, '设置成功!')
def setSlaveStatusSSH(version=''):
db = pMysqlDb() db = pMysqlDb()
dlist = db.query('show slave status') dlist = db.query('show slave status')
if len(dlist) == 0: if len(dlist) == 0:
return mw.returnJson(False, '需要手动添加主服务命令或者执行[初始化]!') return mw.returnJson(False, '需要手动添加主服务命令或者执行初始化!')
if len(dlist) > 0 and (dlist[0]["Slave_IO_Running"] == 'Yes' or dlist[0]["Slave_SQL_Running"] == 'Yes'): if len(dlist) > 0 and (dlist[0]["Slave_IO_Running"] == 'Yes' or dlist[0]["Slave_SQL_Running"] == 'Yes'):
db.query('stop slave') db.query('stop slave')
@@ -2342,6 +2626,70 @@ def writeDbSyncStatus(data):
def doFullSync(version=''): def doFullSync(version=''):
mode_file = getSyncModeFile()
if not os.path.exists(mode_file):
return mw.returnJson(False, '需要先设置同步配置')
mode = mw.readFile(mode_file)
if mode == 'ssh':
return doFullSyncSSH(version)
if mode == 'sync-user':
return doFullSyncUser(version)
def doFullSyncUser(version=''):
args = getArgs()
data = checkArgs(args, ['db'])
if not data[0]:
return data[1]
sync_db = args['db']
db = pMysqlDb()
conn = pSqliteDb('slave_sync_user')
data = conn.field('ip,port,user,pass,mode,cmd').find()
user = data['user']
apass = data['pass']
port = data['port']
ip = data['ip']
bak_file = '/tmp/tmp.sql'
writeDbSyncStatus({'code': 0, 'msg': '开始同步...', 'progress': 0})
dmp_option = ''
mode = recognizeDbMode()
if mode == 'gtid':
dmp_option = ' --set-gtid-purged=off '
writeDbSyncStatus({'code': 1, 'msg': '远程导出数据...', 'progress': 20})
if not os.path.exists(bak_file):
dump_sql_data = getServerDir() + "/bin/mysqldump " + dmp_option + " --force --opt --default-character-set=utf8 --single-transaction -h" + ip + " -P" + \
port + " -u" + user + " -p\"" + apass + \
"\" --ssl-mode=DISABLED " + sync_db + " > " + bak_file
# print(dump_sql_data)
mw.execShell(dump_sql_data)
writeDbSyncStatus({'code': 2, 'msg': '本地导入数据...', 'progress': 40})
if os.path.exists(bak_file):
pwd = pSqliteDb('config').where('id=?', (1,)).getField('mysql_root')
sock = getSocketFile()
my_import_cmd = getServerDir() + '/bin/mysql -S ' + sock + ' -uroot -p' + pwd + \
' ' + sync_db + ' < ' + bak_file
mw.execShell(my_import_cmd)
if version == '8.0':
db.query("start slave user='{}' password='{}';".format(user, apass))
else:
db.query("start slave")
writeDbSyncStatus({'code': 6, 'msg': '从库重启完成...', 'progress': 100})
os.system("rm -rf " + bak_file)
return True
def doFullSyncSSH(version=''):
args = getArgs() args = getArgs()
data = checkArgs(args, ['db']) data = checkArgs(args, ['db'])
@@ -2511,6 +2859,9 @@ def uninstallPreInspection(version):
if mw.isDebugMode(): if mw.isDebugMode():
return 'ok' return 'ok'
import plugins_api
plugins_api.plugins_api().removeIndex(getPluginName(), version)
return "请手动删除MySQL[{}]<br/> rm -rf {}".format(version, getServerDir()) return "请手动删除MySQL[{}]<br/> rm -rf {}".format(version, getServerDir())
if __name__ == "__main__": if __name__ == "__main__":
@@ -2653,6 +3004,18 @@ if __name__ == "__main__":
print(delSlaveSSH(version)) print(delSlaveSSH(version))
elif func == 'update_slave_ssh': elif func == 'update_slave_ssh':
print(updateSlaveSSH(version)) print(updateSlaveSSH(version))
elif func == 'get_slave_sync_user_list':
print(getSlaveSyncUserList(version))
elif func == 'get_slave_sync_user_by_ip':
print(getSlaveSyncUserByIp(version))
elif func == 'add_slave_sync_user':
print(addSlaveSyncUser(version))
elif func == 'del_slave_sync_user':
print(delSlaveSyncUser(version))
elif func == 'get_slave_sync_mode':
print(getSlaveSyncMode(version))
elif func == 'set_slave_sync_mode':
print(setSlaveSyncMode(version))
elif func == 'init_slave_status': elif func == 'init_slave_status':
print(initSlaveStatus(version)) print(initSlaveStatus(version))
elif func == 'set_slave_status': elif func == 'set_slave_status':
+277 -16
View File
@@ -731,7 +731,7 @@ function addDatabase(type){
layer.close(index); layer.close(index);
dbList(); dbList();
} }
},{icon: rdata.status ? 1 : 2},600); },{icon: rdata.status ? 1 : 2}, 2000);
}); });
} }
}); });
@@ -819,7 +819,7 @@ function openPhpmyadmin(name,username,password){
data = syncPost('/plugins/run',{'name':'phpmyadmin','func':'get_cfg'}); data = syncPost('/plugins/run',{'name':'phpmyadmin','func':'get_cfg'});
var rdata = $.parseJSON(data.data); var rdata = $.parseJSON(data.data);
if (rdata.choose != 'mysql'){ if (rdata.choose != 'mysql'){
layer.msg('当前为['+rdata.choose+']模式,若要使用请切换模式.',{icon:2,shade: [0.3, '#000']}); layer.msg('当前为['+rdata.choose+']模式,若要使用请修改phpMyAdmin访问切换.',{icon:2,shade: [0.3, '#000']});
return; return;
} }
@@ -1208,7 +1208,7 @@ function myLogs(){
<span class="f14 c6 mr20">二进制日志 </span><span class="f14 c6 mr20">' + toSize(rdata.msg) + '</span>\ <span class="f14 c6 mr20">二进制日志 </span><span class="f14 c6 mr20">' + toSize(rdata.msg) + '</span>\
'+line_status+'\ '+line_status+'\
<p class="f14 c6 mtb10" style="border-top:#ddd 1px solid; padding:10px 0">错误日志<button class="btn btn-default btn-clear btn-xs" style="float:right;" >清理日志</button></p>\ <p class="f14 c6 mtb10" style="border-top:#ddd 1px solid; padding:10px 0">错误日志<button class="btn btn-default btn-clear btn-xs" style="float:right;" >清理日志</button></p>\
<textarea readonly style="margin: 0px;width: 100%;height: 440px;background-color: #333;color:#fff; padding:0 5px" id="error_log"></textarea>\ <textarea readonly style="margin: 0px;width: 100%;height: 438px;background-color: #333;color:#fff; padding:0 5px" id="error_log"></textarea>\
</p>'; </p>';
$(".soft-man-con").html(limitCon); $(".soft-man-con").html(limitCon);
@@ -1545,13 +1545,9 @@ function getMasterRepSlaveUserCmd(username, db=''){
function delMasterRepSlaveUser(username){ function delMasterRepSlaveUser(username){
myPost('del_master_rep_slave_user', {username:username}, function(data){ myPost('del_master_rep_slave_user', {username:username}, function(data){
var rdata = $.parseJSON(data.data); var rdata = $.parseJSON(data.data);
layer.msg(rdata.msg); showMsg(rdata.msg, function(){
$('.layui-layer-close1').click();
setTimeout(function(){
getMasterRepSlaveList(); getMasterRepSlaveList();
},1000); },{icon: rdata.status ? 1 : 2},1000)
}); });
} }
@@ -1821,8 +1817,23 @@ function addSlaveSSH(ip=''){
function delSlaveSSH(ip){ function delSlaveSSH(ip){
myPost('del_slave_ssh', {ip:ip}, function(rdata){ myPost('del_slave_ssh', {ip:ip}, function(rdata){
var rdata = $.parseJSON(rdata.data); var rdata = $.parseJSON(rdata.data);
layer.msg(rdata.msg, {icon: rdata.status ? 1 : 2}); showMsg(rdata.msg,function(){
getSlaveSSHPage(); if (rdata.status){
getSlaveSSHPage();
}
},{icon: rdata.status ? 1 : 2}, 600);
});
}
function delSlaveSyncUser(ip){
myPost('del_slave_sync_user', {ip:ip}, function(rdata){
var rdata = $.parseJSON(rdata.data);
showMsg(rdata.msg,function(){
if (rdata.status){
getSlaveSyncUserPage();
}
},{icon: rdata.status ? 1 : 2}, 600);
}); });
} }
@@ -1872,6 +1883,237 @@ function getSlaveSSHPage(page=1){
} }
function addSlaveSyncUser(ip=''){
myPost('get_slave_sync_user_by_ip', {ip:ip}, function(rdata){
var rdata = $.parseJSON(rdata.data);
var ip = '127.0.0.1';
var port = "22";
var cmd = '';
var user = 'input_sync_user';
var pass = 'input_sync_pwd';
var mode = '0';
if (rdata.data.length>0){
ip = rdata.data[0]['ip'];
port = rdata.data[0]['port'];
cmd = rdata.data[0]['cmd'];
user = rdata.data[0]['user'];
pass = rdata.data[0]['pass'];
mode = rdata.data[0]['mode'];
}
var index = layer.open({
type: 1,
area: ['500px','470px'],
title: '同步账户',
closeBtn: 1,
shift: 5,
shadeClose: true,
btn:["确认","取消"],
content: "<form class='bt-form pd20'>\
<div class='line'><span class='tname'>IP</span><div class='info-r'><input name='ip' class='bt-input-text mr5' type='text' style='width:330px;' value='"+ip+"'></div></div>\
<div class='line'><span class='tname'>端口</span><div class='info-r'><input name='port' class='bt-input-text mr5' type='number' style='width:330px;' value='"+port+"'></div></div>\
<div class='line'><span class='tname'>同步账户</span><div class='info-r'><input name='user' class='bt-input-text mr5' type='text' style='width:330px;' value='"+user+"'></div></div>\
<div class='line'><span class='tname'>同步密码</span><div class='info-r'><input name='pass' class='bt-input-text mr5' type='text' style='width:330px;' value='"+pass+"'></div></div>\
<div class='line'>\
<span class='tname'>CMD[最好填好]</span>\
<div class='info-r'><textarea class='bt-input-text mr5' row='20' cols='30' name='cmd' style='width:330px;height:150px;'></textarea></div>\
</div>\
<input type='hidden' name='mode' value='"+mode+"' />\
</form>",
success:function(){
$('textarea[name="cmd"]').html(cmd);
$('textarea[name="cmd"]').change(function(){
var val = $(this).val();
var vlist = val.split(',');
var a = {};
for (var i in vlist) {
var tmp = toTrim(vlist[i]);
var tmp_a = tmp.split(" ");
var real_tmp = tmp_a[tmp_a.length-1];
var kv = real_tmp.split("=");
a[kv[0]] = kv[1].replace("'",'').replace("'",'');
}
$('input[name="ip"]').val(a['MASTER_HOST']);
$('input[name="port"]').val(a['MASTER_PORT']);
$('input[name="user"]').val(a['MASTER_USER']);
$('input[name="pass"]').val(a['MASTER_PASSWORD']);
console.log(a['MASTER_AUTO_POSITION'],typeof(a['MASTER_AUTO_POSITION']));
if (typeof(a['MASTER_AUTO_POSITION']) != 'undefined' ){
$('input[name="mode"]').val('1');
}
});
},
yes:function(index){
var ip = $('input[name="ip"]').val();
var port = $('input[name="port"]').val();
var user = $('input[name="user"]').val();
var pass = $('input[name="pass"]').val();
var cmd = $('textarea[name="cmd"]').val();
var mode = $('input[name="mode"]').val();
var data = {ip:ip,port:port,cmd:cmd,user:user,pass:pass,mode:mode};
myPost('add_slave_sync_user', data, function(ret_data){
layer.close(index);
var rdata = $.parseJSON(ret_data.data);
showMsg(rdata.msg,function(){
if (rdata.status){
getSlaveSyncUserPage();
}
},{icon: rdata.status ? 1 : 2},600);
});
}
});
});
}
function getSlaveSyncUserPage(page=1){
var _data = {};
_data['page'] = page;
_data['page_size'] = 5;
_data['tojs'] ='getSlaveSyncUserPage';
myPost('get_slave_sync_user_list', _data, function(data){
var layerId = null;
var rdata = [];
try {
rdata = $.parseJSON(data.data);
} catch(e) {
console.log(e);
}
var list = '';
var user_list = rdata['data'];
for (i in user_list) {
var ip = user_list[i]['ip'];
var port = user_list[i]['port'];
var user = user_list[i]['user'];
var apass = user_list[i]['pass'];
var cmd = '未设置';
if (user_list[i]['cmd']!=''){
cmd = '已设置';
}
list += '<tr><td>'+ip+'</td>\
<td>'+port+'</td>\
<td>'+user+'</td>\
<td>'+apass+'</td>\
<td>'+cmd+'</td>\
<td>\
<a class="btlink" onclick="addSlaveSyncUser(\''+ip+'\');">修改</a> | \
<a class="btlink" onclick="delSlaveSyncUser(\''+ip+'\');">删除</a>\
</td>\
</tr>';
}
$('.get-slave-ssh-list tbody').html(list);
$('.dataTables_paginate_4').html(rdata['page']);
});
}
function getSlaveCfg(){
myPost('get_slave_sync_mode', '', function(data){
var rdata = $.parseJSON(data.data);
var mode_none = 'success';
var mode_ssh = 'danger';
var mode_sync_user = 'danger';
if(rdata.status){
var mode_none = 'danger';
if (rdata.data == 'ssh'){
var mode_ssh = 'success';
var mode_sync_user = 'danger';
} else {
var mode_ssh = 'danger';
var mode_sync_user = 'success';
}
}
layerId = layer.open({
type: 1,
title: '同步配置',
area: ['400px','180px'],
content:"<div class='bt-form pd20 c6'>\
<p class='conf_p'>\
<span class='f14 c6 mr20'>当前从库同步模式</span>\
<b class='f14 c6 mr20'></b>\
<button class='btn btn-"+mode_none+" btn-xs slave-db-mode btn-none'>无</button>\
<button class='btn btn-"+mode_ssh+" btn-xs slave-db-mode btn-ssh'>SSH</button>\
<button class='btn btn-"+mode_sync_user+" btn-xs slave-db-mode btn-sync-user'>同步账户</button>\
</p>\
<hr />\
<p class='conf_p'>\
<span class='f14 c6 mr20'>配置设置</span>\
<b class='f14 c6 mr20'></b>\
<button class='btn btn-success btn-xs btn-slave-ssh'>SSH</button>\
<button class='btn btn-success btn-xs btn-slave-user'>同步账户</button>\
</p>\
</div>",
success:function(){
$('.btn-slave-ssh').click(function(){
getSlaveSSHList();
});
$('.btn-slave-user').click(function(){
getSlaveUserList();
});
$('.slave-db-mode').click(function(){
var _this = this;
var mode = 'none';
if ($(this).hasClass('btn-ssh')){
mode = 'ssh';
}
if ($(this).hasClass('btn-sync-user')){
mode = 'sync-user';
}
myPost('set_slave_sync_mode', {mode:mode}, function(data){
var rdata = $.parseJSON(data.data);
showMsg(rdata.msg,function(){
$('.slave-db-mode').remove('btn-success').addClass('btn-danger');
$(_this).removeClass('btn-danger').addClass('btn-success');
},{icon:rdata.status?1:2},2000);
});
});
}
});
});
}
function getSlaveUserList(){
var page = '<div class="dataTables_paginate_4 dataTables_paginate paging_bootstrap page" style="margin-top:0px;"></div>';
page += '<div class="table_toolbar" style="left:0px;"><span class="sync btn btn-default btn-sm" onclick="addSlaveSyncUser()" title="">添加同步账户</span></div>';
layerId = layer.open({
type: 1,
title: '同步账户列表',
area: '500px',
content:"<div class='bt-form pd20 c6'>\
<div class='divtable mtb10'>\
<div><table class='table table-hover get-slave-ssh-list'>\
<thead><tr><th>IP</th><th>PORT</th><th>同步账户</th><th>同步密码</th><th>CMD</th><th>操作</th></tr></thead>\
<tbody></tbody>\
</table></div>\
"+page +"\
</div>\
</div>",
success:function(){
getSlaveSyncUserPage(1);
}
});
}
function getSlaveSSHList(page=1){ function getSlaveSSHList(page=1){
var page = '<div class="dataTables_paginate_4 dataTables_paginate paging_bootstrap page" style="margin-top:0px;"></div>'; var page = '<div class="dataTables_paginate_4 dataTables_paginate paging_bootstrap page" style="margin-top:0px;"></div>';
@@ -1991,7 +2233,7 @@ function masterOrSlaveConf(version=''){
for(i in rdata.data){ for(i in rdata.data){
var v = rdata.data[i]; var v = rdata.data[i];
var status = "异常"; var status = "<a class='btlink db_error'>异常</>";
if (v['Slave_SQL_Running'] == 'Yes' && v['Slave_IO_Running'] == 'Yes'){ if (v['Slave_SQL_Running'] == 'Yes' && v['Slave_IO_Running'] == 'Yes'){
status = "正常"; status = "正常";
} }
@@ -2033,6 +2275,26 @@ function masterOrSlaveConf(version=''){
// <span class="sync btn btn-default btn-sm" onclick="getMasterRepSlaveList()" title="">添加</span>\ // <span class="sync btn btn-default btn-sm" onclick="getMasterRepSlaveList()" title="">添加</span>\
// </div> // </div>
$(".table_slave_status_list").html(con); $(".table_slave_status_list").html(con);
$('.db_error').click(function(){
layer.open({
type: 1,
title: '同步异常信息',
area: '500px',
content:"<form class='bt-form pd20 pb70'>\
<div class='line'>"+v['Error']+"</div>\
<div class='bt-form-submit-btn'>\
<button type='button' class='btn btn-success btn-sm btn-title class-copy-db-err'>复制</button>\
</div>\
</form>",
success:function(){
copyText(v['Error']);
$('.class-copy-db-err').click(function(){
copyText(v['Error']);
});
}
});
});
}); });
} }
@@ -2079,7 +2341,6 @@ function masterOrSlaveConf(version=''){
}); });
} }
function getMasterStatus(){ function getMasterStatus(){
myPost('get_master_status', '', function(rdata){ myPost('get_master_status', '', function(rdata){
@@ -2110,7 +2371,7 @@ function masterOrSlaveConf(version=''){
<p class="conf_p">\ <p class="conf_p">\
<span class="f14 c6 mr20">Slave[从]配置</span><span class="f14 c6 mr20"></span>\ <span class="f14 c6 mr20">Slave[从]配置</span><span class="f14 c6 mr20"></span>\
<button class="btn '+(!rdata.slave_status ? 'btn-danger' : 'btn-success')+' btn-xs btn-slave">'+(!rdata.slave_status ? '未启动' : '已启动') +'</button>\ <button class="btn '+(!rdata.slave_status ? 'btn-danger' : 'btn-success')+' btn-xs btn-slave">'+(!rdata.slave_status ? '未启动' : '已启动') +'</button>\
<button class="btn btn-success btn-xs" onclick="getSlaveSSHList()" >[主]SSH配置</button>\ <button class="btn btn-success btn-xs" onclick="getSlaveCfg()" >同步配置</button>\
<button class="btn btn-success btn-xs" onclick="initSlaveStatus()" >初始化</button>\ <button class="btn btn-success btn-xs" onclick="initSlaveStatus()" >初始化</button>\
</p>\ </p>\
<hr/>\ <hr/>\
@@ -2184,10 +2445,10 @@ function masterOrSlaveConf(version=''){
getMasterDbList(); getMasterDbList();
} }
if (rdata.slave_status){ // if (rdata.slave_status){
getAsyncMasterDbList(); getAsyncMasterDbList();
getAsyncDataList() getAsyncDataList()
} // }
}); });
} }
getMasterStatus(); getMasterStatus();
+1 -8
View File
@@ -1,10 +1,3 @@
lua_shared_dict waf_limit 30m; lua_shared_dict waf_limit 30m;
lua_shared_dict waf_drop_ip 10m; lua_shared_dict waf_drop_ip 10m;
lua_shared_dict waf_drop_sum 10m; lua_shared_dict waf_drop_sum 10m;
lua_package_path "{$WAF_PATH}/html/?.lua;{$WAF_PATH}/conf/?.lua;{$WAF_PATH}/lua/?.lua;{$ROOT_PATH}/openresty/lualib/?.lua;;";
lua_package_cpath "{$WAF_PATH}/conf/?.so;{$ROOT_PATH}/openresty/lualib/?.so;;";
init_worker_by_lua_file {$WAF_PATH}/lua/init_worker.lua;
access_by_lua_file {$WAF_PATH}/lua/init.lua;
# init_by_lua_file {$WAF_PATH}/lua/init.lua;
+131 -64
View File
@@ -6,6 +6,7 @@ import os
import time import time
import subprocess import subprocess
import json import json
import re
sys.path.append(os.getcwd() + "/class/core") sys.path.append(os.getcwd() + "/class/core")
import mw import mw
@@ -224,21 +225,13 @@ def initTotalInfo():
_name[name] = tmp _name[name] = tmp
total_contents['sites'] = _name total_contents['sites'] = _name
total_contents['start_time'] = str(time.time())
cjson = mw.getJson(total_contents) cjson = mw.getJson(total_contents)
mw.writeFile(path_total, cjson) mw.writeFile(path_total, cjson)
def status(): def dstWafConf():
path = getConf() return mw.getServerDir() + "/web_conf/nginx/vhost/opwaf.conf"
if not os.path.exists(path):
return 'stop'
conf = mw.readFile(path)
if conf.find("#include luawaf.conf;") != -1:
return 'stop'
if conf.find("luawaf.conf;") == -1:
return 'stop'
return 'start'
def contentReplace(content): def contentReplace(content):
@@ -328,11 +321,45 @@ def autoMakeConfig():
def restartWeb(): def restartWeb():
autoMakeConfig() autoMakeConfig()
mw.restartWeb() mw.opWeb('stop')
mw.opWeb('start')
def makeDstLua():
root_init_dir = mw.getServerDir() + '/web_conf/nginx/lua/init_by_lua_file'
root_worker_dir = mw.getServerDir() + '/web_conf/nginx/lua/init_worker_by_lua_file'
root_access_dir = mw.getServerDir() + '/web_conf/nginx/lua/access_by_lua_file'
path = getServerDir()
path_tpl = getPluginDir()
waf_common_tpl = path_tpl + "/waf/lua/waf_common.lua"
waf_common_dst = path + "/waf/lua/waf_common.lua"
content = mw.readFile(waf_common_tpl)
content = contentReplace(content)
mw.writeFile(waf_common_dst, content)
waf_init_tpl = path_tpl + "/waf/lua/init_preload.lua"
waf_init_dst = root_init_dir + "/waf_init_preload.lua"
content = mw.readFile(waf_init_tpl)
content = contentReplace(content)
mw.writeFile(waf_init_dst, content)
init_worker_tpl = path_tpl + "/waf/lua/init_worker.lua"
init_worker_dst = root_worker_dir + '/opwaf_init_worker.lua'
content = mw.readFile(init_worker_tpl)
content = contentReplace(content)
mw.writeFile(init_worker_dst, content)
access_file_tpl = path_tpl + "/waf/lua/init.lua"
access_file_dst = root_access_dir + '/opwaf_init.lua'
content = mw.readFile(access_file_tpl)
content = contentReplace(content)
mw.writeFile(access_file_dst, content)
mw.opLuaMakeAll()
def initDreplace(): def initDreplace():
path = getServerDir() path = getServerDir()
if not os.path.exists(path + '/waf/lua'): if not os.path.exists(path + '/waf/lua'):
sdir = getPluginDir() + '/waf' sdir = getPluginDir() + '/waf'
@@ -355,26 +382,14 @@ def initDreplace():
content['reqfile_path'] = wfDir content['reqfile_path'] = wfDir
mw.writeFile(config, mw.getJson(content)) mw.writeFile(config, mw.getJson(content))
config = path + "/waf/lua/init.lua" makeDstLua()
content = mw.readFile(config)
content = contentReplace(content)
mw.writeFile(config, content)
config_common = path + "/waf/lua/common.lua" waf_conf = dstWafConf()
content = mw.readFile(config_common) if not os.path.exists(waf_conf):
content = contentReplace(content) waf_tpl = getPluginDir() + "/conf/luawaf.conf"
mw.writeFile(config_common, content) content = mw.readFile(waf_tpl)
content = contentReplace(content)
init_worker = path + "/waf/lua/init_worker.lua" mw.writeFile(waf_conf, content)
content = mw.readFile(init_worker)
content = contentReplace(content)
mw.writeFile(init_worker, content)
waf_conf = mw.getServerDir() + "/openresty/nginx/conf/luawaf.conf"
waf_tpl = getPluginDir() + "/conf/luawaf.conf"
content = mw.readFile(waf_tpl)
content = contentReplace(content)
mw.writeFile(waf_conf, content)
initDomainInfo() initDomainInfo()
initSiteInfo() initSiteInfo()
@@ -388,56 +403,73 @@ def initDreplace():
mw.execShell("chown -R www:www " + path) mw.execShell("chown -R www:www " + path)
def status():
path = getConf()
if not os.path.exists(path):
return 'stop'
waf_conf = dstWafConf()
if not os.path.exists(waf_conf):
return 'stop'
return 'start'
def start(): def start():
initDreplace() initDreplace()
path = getConf()
conf = mw.readFile(path)
conf = conf.replace('#include luawaf.conf;', "include luawaf.conf;")
mw.writeFile(path, conf)
import tool_task import tool_task
tool_task.createBgTask() tool_task.createBgTask()
mw.restartWeb() restartWeb()
return 'ok' return 'ok'
def stop(): def stop():
path = getConf() root_init_dir = mw.getServerDir() + '/web_conf/nginx/lua/init_by_lua_file'
conf = mw.readFile(path) root_worker_dir = mw.getServerDir() + '/web_conf/nginx/lua/init_worker_by_lua_file'
conf = conf.replace('include luawaf.conf;', "#include luawaf.conf;") root_access_dir = mw.getServerDir() + '/web_conf/nginx/lua/access_by_lua_file'
mw.writeFile(path, conf) waf_init_dst = root_init_dir + "/waf_init_preload.lua"
if os.path.exists(waf_init_dst):
os.remove(waf_init_dst)
init_worker_dst = root_worker_dir + '/opwaf_init_worker.lua'
if os.path.exists(init_worker_dst):
os.remove(init_worker_dst)
access_file_dst = root_access_dir + '/opwaf_init.lua'
if os.path.exists(access_file_dst):
os.remove(access_file_dst)
wafconf = dstWafConf()
if os.path.exists(wafconf):
os.remove(wafconf)
import tool_task import tool_task
tool_task.removeBgTask() tool_task.removeBgTask()
mw.restartWeb() mw.opLuaMakeAll()
restartWeb()
return 'ok' return 'ok'
def restart(): def restart():
mw.restartWeb() restartWeb()
return 'ok' return 'ok'
def reload(): def reload():
stop() stop()
path = getServerDir() makeDstLua()
path_tpl = getPluginDir()
config = path + "/waf/lua/init.lua"
config_tpl = path_tpl + "/waf/lua/init.lua"
content = mw.readFile(config_tpl)
content = contentReplace(content)
mw.writeFile(config, content)
errlog = mw.getServerDir() + "/openresty/nginx/logs/error.log" errlog = mw.getServerDir() + "/openresty/nginx/logs/error.log"
mw.execShell('rm -rf ' + errlog) mw.execShell('rm -rf ' + errlog)
start() start()
restartWeb()
return 'ok' return 'ok'
@@ -1022,24 +1054,41 @@ def removeSiteCdnHeader():
def outputData(): def outputData():
args = getArgs() args = getArgs()
data = checkArgs(args, ['s_Name']) data = checkArgs(args, ['sname'])
if not data[0]: if not data[0]:
return data[1] return data[1]
path = getRuleJsonPath(args['s_Name']) path = getRuleJsonPath(args['sname'])
content = mw.readFile(path) content = mw.readFile(path)
return mw.returnJson(True, 'ok', content) return mw.returnJson(True, 'ok', content)
def importData(): def importData():
args = getArgs() args = getArgs()
data = checkArgs(args, ['s_Name', 'pdata']) data = checkArgs(args, ['sname', 'pdata'])
if not data[0]: if not data[0]:
return data[1] return data[1]
path = getRuleJsonPath(args['s_Name']) path = getRuleJsonPath(args['sname'])
mw.writeFile(path, args['pdata'])
restartWeb() source_data = mw.readFile(path)
source_data = json.loads(source_data)
save_data = []
save_data.append(source_data[0])
pdata = args['pdata'].strip()
try:
pdata = json.loads(pdata)
mw.writeFile(path, json.dumps(pdata))
except Exception as e:
pdata = pdata.split("\\n")
for x in pdata:
pval = x.strip()
if pval != "":
vv = json.loads(pval)
save_data.append(vv[0])
mw.writeFile(path, json.dumps(save_data))
# restartWeb()
return mw.returnJson(True, '设置成功!') return mw.returnJson(True, '设置成功!')
@@ -1167,6 +1216,28 @@ def getWafConf():
return mw.readFile(conf) return mw.readFile(conf)
def cleanDropIp():
url = "http://127.0.0.1/clean_waf_drop_ip"
data = mw.httpGet(url)
return mw.returnJson(True, 'ok!', data)
def testRun():
# args = getArgs()
# data = checkArgs(args, ['siteName'])
# if not data[0]:
# return data[1]
conf_json = getServerDir() + "/waf/default.json"
data = mw.readFile(conf_json)
pdata = json.loads(data)
default_site = pdata['default']
url = "http://" + default_site + '/?t=../etc/passwd'
returnData = mw.httpGet(url, 10)
return mw.returnJson(True, '测试运行成功!', returnData)
def installPreInspection(): def installPreInspection():
check_op = mw.getServerDir() + "/openresty" check_op = mw.getServerDir() + "/openresty"
if not os.path.exists(check_op): if not os.path.exists(check_op):
@@ -1174,12 +1245,6 @@ def installPreInspection():
return 'ok' return 'ok'
def cleanDropIp():
url = "http://127.0.0.1/clean_waf_drop_ip"
data = mw.httpGet(url)
return mw.returnJson(True, 'ok!', data)
if __name__ == "__main__": if __name__ == "__main__":
func = sys.argv[1] func = sys.argv[1]
if func == 'status': if func == 'status':
@@ -1268,5 +1333,7 @@ if __name__ == "__main__":
print(getWafSite()) print(getWafSite())
elif func == 'clean_drop_ip': elif func == 'clean_drop_ip':
print(cleanDropIp()) print(cleanDropIp())
elif func == 'test_run':
print(testRun())
else: else:
print('error') print('error')
+13 -1
View File
@@ -1,4 +1,16 @@
{ {
"hook":[
{
"tag":"site_cb",
"site_cb": {
"title":"网站统计",
"name":"op_waf",
"add":{"func":"reload"},
"update":{"func":"reload"},
"delete":{"func":"reload"}
}
}
],
"title":"OP防火墙", "title":"OP防火墙",
"tip":"soft", "tip":"soft",
"name":"op_waf", "name":"op_waf",
@@ -12,5 +24,5 @@
"home":"https://github.com/loveshell/ngx_lua_waf", "home":"https://github.com/loveshell/ngx_lua_waf",
"date":"2019-04-21", "date":"2019-04-21",
"pid": "1", "pid": "1",
"versions": ["0.2.3"] "versions": ["0.2.6"]
} }
+7 -8
View File
@@ -24,7 +24,7 @@ else
fi fi
Install_of(){ Install_App(){
echo '正在安装脚本文件...' > $install_tmp echo '正在安装脚本文件...' > $install_tmp
mkdir -p $serverPath/source/op_waf mkdir -p $serverPath/source/op_waf
@@ -41,7 +41,9 @@ Install_of(){
cd $serverPath/source/op_waf && tar xvf luarocks-3.5.0.tar.gz cd $serverPath/source/op_waf && tar xvf luarocks-3.5.0.tar.gz
# cd luarocks-3.9.1 && ./configure && make bootstrap # cd luarocks-3.9.1 && ./configure && make bootstrap
cd luarocks-3.5.0 && ./configure --prefix=$serverPath/op_waf/luarocks --with-lua-include=$serverPath/openresty/luajit/include/luajit-2.1 --with-lua-bin=$serverPath/openresty/luajit/bin cd luarocks-3.5.0 && ./configure --prefix=$serverPath/op_waf/luarocks \
--with-lua-include=$serverPath/openresty/luajit/include/luajit-2.1 \
--with-lua-bin=$serverPath/openresty/luajit/bin
make -I${serverPath}/openresty/luajit/bin make -I${serverPath}/openresty/luajit/bin
make install make install
fi fi
@@ -85,13 +87,10 @@ Install_of(){
echo 'install ok' > $install_tmp echo 'install ok' > $install_tmp
cd ${rootPath} && python3 ${rootPath}/plugins/op_waf/index.py start cd ${rootPath} && python3 ${rootPath}/plugins/op_waf/index.py start
# cd ${rootPath} && python3 ${rootPath}/plugins/op_waf/index.py restart # cd ${rootPath} && python3 ${rootPath}/plugins/op_waf/index.py restart
} }
Uninstall_of(){ Uninstall_App(){
cd ${rootPath} && python3 ${rootPath}/plugins/op_waf/index.py stop cd ${rootPath} && python3 ${rootPath}/plugins/op_waf/index.py stop
if [ "$?" == "0" ];then if [ "$?" == "0" ];then
@@ -102,7 +101,7 @@ Uninstall_of(){
action=$1 action=$1
if [ "${1}" == 'install' ];then if [ "${1}" == 'install' ];then
Install_of Install_App
else else
Uninstall_of Uninstall_App
fi fi
+44 -22
View File
@@ -568,14 +568,14 @@ function outputLayer(rdata, name, type) {
'<div class="ml0" style="position:relative;" id="focus_tips">' + '<div class="ml0" style="position:relative;" id="focus_tips">' +
'<textarea class="bt-input-text mr20 config" name="config" style="width: 300px; height: 250px; line-height: 22px; display: none;" id="lead_data">' + (rdata != '' ? JSON.stringify(rdata) : '') + '</textarea>' + '<textarea class="bt-input-text mr20 config" name="config" style="width: 300px; height: 250px; line-height: 22px; display: none;" id="lead_data">' + (rdata != '' ? JSON.stringify(rdata) : '') + '</textarea>' +
'<div class="placeholder c9" style="top: 15px; left: 15px; display:' + (rdata == "" ? "block;" : "none;") + '">导入格式如下:' + '<div class="placeholder c9" style="top: 15px; left: 15px; display:' + (rdata == "" ? "block;" : "none;") + '">导入格式如下:' +
(name == 'ip_white' || name == 'ip_black' ? "[[[127, 0, 0, 1],[127, 0, 0, 255]]]" : "[\"^/test\",\"^/web\"]") + (name == 'ip_white' || name == 'ip_black' ? "[[[127, 0, 0, 1],[127, 0, 0, 255]],[[192, 0, 0, 1],[192, 0, 0, 255]]]" : "[\"^/test\",\"^/web\"]") +
'</div>' + '</div>' +
'</div>' + '</div>' +
'</div>' + '</div>' +
'<div class="line "><div class="ml0">' + '<div class="line "><div class="ml0">' +
(type ? '<button name="btn_save_to" class="btn btn-success btn-sm mr5 btn_save_to" >导出配置</button>' : '<button name="btn_save" class="btn btn-success btn-sm mr5 btn_save">保存</button>') + (type ? '<button name="btn_save_to" class="btn btn-success btn-sm mr5 btn_save_to" >导出配置</button>' : '<button name="btn_save" class="btn btn-success btn-sm mr5 btn_save">保存</button>') +
'</div></div>' + '</div></div>' +
'</div>' '</div>',
}); });
var lead_error = CodeMirror.fromTextArea(document.getElementById("lead_data"), { var lead_error = CodeMirror.fromTextArea(document.getElementById("lead_data"), {
mode: 'html', mode: 'html',
@@ -585,7 +585,10 @@ function outputLayer(rdata, name, type) {
}); });
setTimeout(function () { setTimeout(function () {
$('.btn_save').on('click', function () { $('.btn_save').on('click', function () {
importData(name, lead_error.getValue()); importData(name, lead_error.getValue(), function(){
layer.close(window.Load_layer);
ipWhiteLoadList();
});
}) })
$('.btn_save_to').on('click', function () { $('.btn_save_to').on('click', function () {
funDownload(lead_error.getValue(), name + '.json'); funDownload(lead_error.getValue(), name + '.json');
@@ -601,7 +604,7 @@ function outputLayer(rdata, name, type) {
function outputData(name, callback) { function outputData(name, callback) {
var loadT = layer.msg('正在导出数据..', { icon: 16, time: 0 }); var loadT = layer.msg('正在导出数据..', { icon: 16, time: 0 });
owPost('output_data', { s_Name: name } , function(data){ owPost('output_data', { sname: name } , function(data){
var tmp = $.parseJSON(data.data); var tmp = $.parseJSON(data.data);
var rdata = $.parseJSON(tmp.data); var rdata = $.parseJSON(tmp.data);
if (callback) callback(rdata,res); if (callback) callback(rdata,res);
@@ -611,7 +614,7 @@ function outputData(name, callback) {
//导入数据 //导入数据
function importData(name, pdata, callback) { function importData(name, pdata, callback) {
owPost('import_data', { s_Name: name, pdata: pdata } , function(data){ owPost('import_data', { sname: name, pdata: pdata } , function(data){
var rdata = $.parseJSON(data.data); var rdata = $.parseJSON(data.data);
if (callback) callback(); if (callback) callback();
layer.msg(rdata.msg, { icon: rdata.status ? 1 : 2 }); layer.msg(rdata.msg, { icon: rdata.status ? 1 : 2 });
@@ -622,6 +625,22 @@ function fileInput(name) {
outputLayer('', name, false); outputLayer('', name, false);
} }
function ipWhiteLoadList(){
getRuleByName('ip_white', function(data){
var tmp = $.parseJSON(data.data);
var rdata = $.parseJSON(tmp.data);
var tbody = ''
for (var i = 0; i < rdata.length; i++) {
tbody += '<tr>\
<td>'+ rdata[i][0].join('.') + '</td>\
<td>'+ rdata[i][1].join('.') + '</td>\
<td class="text-right"><a class="btlink" onclick="removeIpWhite('+ i + ')">删除</a></td>\
</tr>'
}
$("#ip_white_con").html(tbody);
});
}
//IP白名单 //IP白名单
function ipWhite(type) { function ipWhite(type) {
if (type == undefined) { if (type == undefined) {
@@ -675,20 +694,7 @@ function ipWhite(type) {
}); });
tableFixed("ipWhite"); tableFixed("ipWhite");
} }
ipWhiteLoadList();
getRuleByName('ip_white', function(data){
var tmp = $.parseJSON(data.data);
var rdata = $.parseJSON(tmp.data);
var tbody = ''
for (var i = 0; i < rdata.length; i++) {
tbody += '<tr>\
<td>'+ rdata[i][0].join('.') + '</td>\
<td>'+ rdata[i][1].join('.') + '</td>\
<td class="text-right"><a class="btlink" onclick="removeIpWhite('+ i + ')">删除</a></td>\
</tr>'
}
$("#ip_white_con").html(tbody);
});
} }
//IP白名单 //IP白名单
@@ -955,8 +961,12 @@ function wafScreen(){
owPost('waf_srceen', {}, function(data){ owPost('waf_srceen', {}, function(data){
var rdata = $.parseJSON(data.data); var rdata = $.parseJSON(data.data);
var end_time = Date.now();
var cos_time = (end_time/1000) - parseInt(rdata['start_time']);
var cos_day = parseInt(parseInt(cos_time)/86400);
var con = '<div class="wavbox alert alert-success" style="margin-right:16px">总拦截<span>'+rdata.total+'</span>次</div>'; var con = '<div class="wavbox alert alert-success" style="margin-right:16px">总拦截<span>'+rdata.total+'</span>次</div>';
con += '<div class="wavbox alert alert-info" style="margin-right:16px">安全防护<span>0</span>天</div>'; con += '<div class="wavbox alert alert-info" style="margin-right:16px">安全防护<span>'+cos_day+'</span>天</div>';
con += '<div class="screen">\ con += '<div class="screen">\
<div class="line"><span class="name">POST渗透</span><span class="val">'+rdata.rules.post+'</span></div>\ <div class="line"><span class="name">POST渗透</span><span class="val">'+rdata.rules.post+'</span></div>\
@@ -1712,9 +1722,12 @@ function wafLogs(){
<button data-name="l7" type="button" class="btn btn-default">近7天</button>\ <button data-name="l7" type="button" class="btn btn-default">近7天</button>\
<button data-name="l30" type="button" class="btn btn-default">近30天</button>\ <button data-name="l30" type="button" class="btn btn-default">近30天</button>\
</div>\ </div>\
<span class="last-span"><input data-name="" type="text" id="time_choose" lay-key="1000001_'+randstr+'" class="form-control btn-group-sm" autocomplete="off" placeholder="自定义时间" style="display: inline-block;font-size: 12px;padding: 0 10px;height:30px;width: 200px;"></span>\ <span class="last-span"><input data-name="" type="text" id="time_choose" lay-key="1000001_'+randstr+'" class="form-control btn-group-sm" autocomplete="off" placeholder="自定义时间" style="display: inline-block;font-size: 12px;padding: 0 10px;height:30px;width: 155px;"></span>\
</div>\
<div style="float:right;">\
<button id="UncoverAll" class="btn btn-success btn-sm" style="padding-left: 5px;padding-right: 5px;">解封所有</button>\
<button id="testRun" class="btn btn-default btn-sm" style="padding-left: 5px;padding-right: 5px;">测试</button>\
</div>\ </div>\
<div style="float:right;"><button id="UncoverAll" class="btn btn-success btn-sm">解封所有</button></div>\
</div>\ </div>\
<div class="divtable mtb10" id="ws_table"></div>\ <div class="divtable mtb10" id="ws_table"></div>\
</div>'; </div>';
@@ -1733,6 +1746,15 @@ function wafLogs(){
}); });
}); });
//测试demo
$("#testRun").click(function(){
owPost('test_run',{},function(data){
var rdata = $.parseJSON(data.data);
showMsg(rdata.msg, function(){
wafLogRequest(1);
},{icon:1,shade: [0.3, '#000']},2000);
});
});
//日期范围 //日期范围
laydate.render({ laydate.render({
+9
View File
@@ -0,0 +1,9 @@
#!/bin/bash
DST_DIR=/www/server/op_waf
DIS_FILE=${DST_DIR}/cpu.info
CPU_USAGE=`top -bn 1 | fgrep 'Cpu(s)' | awk '{print 100 -$8}' | awk -F . '{print $1}'`
echo $CPU_USAGE
echo $CPU_USAGE > $DIS_FILE
echo "done success!"
+50
View File
@@ -0,0 +1,50 @@
#!/bin/bash
DST_DIR=/www/server/op_waf
DIS_FILE=${DST_DIR}/cpu.info
function GetCpuUsage(){
cpu_info=`cat /proc/stat | head -n 1`
idle_cpu=`echo $cpu_info|awk '{print $2}'`
cpu_total_time=0
for ci in ${cpu_info[@]}; do
if [ "$ci" == "cpu" ];then
continue
else
#echo $ci
cpu_total_time=`expr $cpu_total_time + $ci`
fi
done
#echo "idle_cpu:${idle_cpu}"
#echo "cpu_total_time:${cpu_total_time}"
cpu_percet=$(awk "BEGIN{print ((${cpu_total_time}-${idle_cpu})/${cpu_total_time})*100}")
echo "${cpu_percet}"
return 0
}
# one value detal
getOne=`GetCpuUsage`
CPU_USAGE=`echo $getOne | awk -F . '{print $1}'`
echo "cpu usage:${CPU_USAGE}"
echo $CPU_USAGE > $DIS_FILE
echo "done success!"
# two value compare
# getOne=`GetCpuUsage`
# echo "getOne:$getOne"
# sleep 1
# getTwo=`GetCpuUsage`
# echo "getTwo:$getTwo"
# cpu_percet_calc=$(awk "BEGIN{print (${getOne}+${getTwo})/2}")
# echo "cpu_percet_calc:${cpu_percet_calc}"
# #echo '0.61212' | awk -F . '{print $1}'
# CPU_USAGE=`echo $cpu_percet_calc | awk -F . '{print $1}'`
# echo "cpu usage:${CPU_USAGE}"
# echo $CPU_USAGE > $DIS_FILE
# echo "done success!"
+2 -1
View File
@@ -26,4 +26,5 @@ fi
# $RUN_CMD --stap --shdict 'limit 10m' test_find_server_name.lua # $RUN_CMD --stap --shdict 'limit 10m' test_find_server_name.lua
# $RUN_CMD test_rand.lua # $RUN_CMD test_rand.lua
$RUN_CMD test_ffi_time.lua # $RUN_CMD test_ffi_time.lua
$RUN_CMD test_get_cpu.lua
+69
View File
@@ -0,0 +1,69 @@
-- cd /www/server/mdserver-web/plugins/op_waf/t/bench && bash bench.sh
local function target()
ngx.re.find("hello, world.", [[\w+\.]], "jo")
end
for i = 1, 100 do
target()
end
local function file_exists(path)
local file = io.open(path, "rb")
if file then file:close() end
return file ~= nil
end
-- 以上为预热操作
collectgarbage()
local json = require "cjson"
local ngx_re = require "ngx.re"
local function data_split(self, str,reps )
local rsList = {}
string.gsub(str,'[^'..reps..']+',function(w)
table.insert(rsList,w)
end)
return rsList
end
local function get_cpu_stat()
local cpu_total = 0
local fp = io.open('/proc/stat','r')
local cpu_line = fp:read()
fp:close()
local list = ngx_re.split(cpu_line," ")
table.remove(list,1)
table.remove(list,1)
local idie = list[4]
for i,v in pairs(list)
do
cpu_total = cpu_total + v
end
local use_percent = tonumber(100-(idie/cpu_total)*100)
return cpu_total,idie,use_percent
end
local function get_cpu_percent()
local cpu_total,idie,use_percent = get_cpu_stat()
ngx.sleep(2)
local cpu_total2,idie2,use_percent2 = get_cpu_stat()
local cpu_usage_percent = tonumber(100-(((idie2-idie)/(cpu_total2-cpu_total))*100))
ngx.say("cpu_usage_percent:"..cpu_usage_percent)
return cpu_usage_percent
end
ngx.update_time()
local begin = ngx.now()
local N = 1e1
for i = 1, N do
get_cpu_stat()
end
ngx.update_time()
ngx.say("test_get_cpu elapsed: ", (ngx.now() - begin))
+7 -6
View File
@@ -309,12 +309,12 @@ def test_Upload():
def test_start(): def test_start():
# test_OK() # test_OK()
# test_Dir() test_Dir()
# test_UA() # test_UA()
test_Header() # test_Header()
# test_UA_for(1000) # test_UA_for(1000)
test_POST() # test_POST()
test_scan() # test_scan()
# test_CC() # test_CC()
# test_url_ext() # test_url_ext()
# test_cdn() # test_cdn()
@@ -322,6 +322,7 @@ def test_start():
if __name__ == "__main__": if __name__ == "__main__":
os.system('cd /Users/midoks/Desktop/mwdev/server/mdserver-web/plugins/op_waf && sh install.sh uninstall 0.2.2 && sh install.sh install 0.2.2') # os.system('cd /Users/midoks/Desktop/mwdev/server/mdserver-web/plugins/op_waf && sh install.sh uninstall 0.2.2 && sh install.sh install 0.2.2')
os.system('cd /Users/midoks/Desktop/mwdev/server/mdserver-web/ && python3 plugins/openresty/index.py stop && python3 plugins/openresty/index.py start') os.system('cd /Users/midoks/Desktop/mwdev/server/mdserver-web/ && python3 /Users/midoks/Desktop/mwdev/server/mdserver-web/plugins/op_waf/index.py reload')
# os.system('cd /Users/midoks/Desktop/mwdev/server/mdserver-web/ && python3 plugins/openresty/index.py stop && python3 plugins/openresty/index.py start')
test_start() test_start()
+86 -20
View File
@@ -1,6 +1,46 @@
#!/bin/sh #!/bin/sh
export PATH=$PATH:/opt/stap/bin:/opt/stapxx export PATH=$PATH:/opt/stap/bin:/opt/stapxx
# cd /www/server/mdserver-web/plugins/op_waf/t && bash ngx_debug.sh lua ok
# cd /www/server/mdserver-web/plugins/op_waf/t && bash ngx_debug.sh c ok
if [ ${_os} == "Darwin" ]; then
OSNAME='macos'
elif grep -Eq "openSUSE" /etc/*-release; then
OSNAME='opensuse'
zypper refresh
zypper install cron wget curl zip unzip
elif grep -Eq "FreeBSD" /etc/*-release; then
OSNAME='freebsd'
elif grep -Eqi "CentOS" /etc/issue || grep -Eq "CentOS" /etc/*-release; then
OSNAME='rhel'
yum install -y wget curl zip unzip tar crontabs
elif grep -Eqi "Fedora" /etc/issue || grep -Eq "Fedora" /etc/*-release; then
OSNAME='fedora'
yum install -y wget curl zip unzip tar crontabs
elif grep -Eqi "Rocky" /etc/issue || grep -Eq "Rocky" /etc/*-release; then
OSNAME='rhel'
yum install -y wget curl zip unzip tar crontabs
elif grep -Eqi "AlmaLinux" /etc/issue || grep -Eq "AlmaLinux" /etc/*-release; then
OSNAME='rhel'
yum install -y wget curl zip unzip tar crontabs
elif grep -Eqi "Amazon Linux" /etc/issue || grep -Eq "Amazon Linux" /etc/*-release; then
OSNAME='amazon'
yum install -y wget curl zip unzip tar crontabs
elif grep -Eqi "Debian" /etc/issue || grep -Eq "Debian" /etc/os-release; then
OSNAME='debian'
apt update -y
apt install -y wget curl zip unzip tar cron
elif grep -Eqi "Ubuntu" /etc/issue || grep -Eq "Ubuntu" /etc/os-release; then
OSNAME='ubuntu'
apt update -y
apt install -y wget curl zip unzip tar cron
else
OSNAME='unknow'
fi
# https://moonbingbing.gitbooks.io/openresty-best-practices/content/flame_graph/install.html # https://moonbingbing.gitbooks.io/openresty-best-practices/content/flame_graph/install.html
# apt install elfutils # apt install elfutils
# sudo apt-get install -y systemtap gcc # sudo apt-get install -y systemtap gcc
@@ -23,7 +63,7 @@ then
exit exit
fi fi
pid=`ps -ef|grep openresty | grep -v grep | awk '{print $2}'` pids=`ps -ef|grep nginx | grep -v grep | awk '{print $2}'`
name=$2 name=$2
@@ -33,7 +73,13 @@ name=$2
# apt install -y kernel-debuginfo-common kernel-debuginfo # apt install -y kernel-debuginfo-common kernel-debuginfo
# apt install -y kernel-* # apt install -y kernel-*
if [ "$OSNAME" == "debian" ];then
apt install -y systemtap
apt-get install -y build-essential
apt-get install -y linux-headers-$(uname -r)
elif [ "$OSNAME" == "centos" ];then
yum install -y kernel-devel-$(uname -r)
fi
# /opt/stapxx/samples/lj-lua-stacks.sxx --arg time=5 --skip-badvars -x 45266 > tmp.bt # /opt/stapxx/samples/lj-lua-stacks.sxx --arg time=5 --skip-badvars -x 45266 > tmp.bt
@@ -58,25 +104,45 @@ if [ ! -d /opt/FlameGraph ];then
cd /opt && git clone https://github.com/brendangregg/FlameGraph cd /opt && git clone https://github.com/brendangregg/FlameGraph
fi fi
if [ $1 == "lua" ]; then for pid in ${pids[@]}; do
# /opt/openresty-systemtap-toolkit/ngx-sample-lua-bt -p 377452 --luajit20 -t 30 >temp.bt echo "strace:$pid"
/opt/openresty-systemtap-toolkit/ngx-sample-lua-bt -p $pid --luajit20 -t 30 >temp.bt if [ $1 == "lua" ]; then
# /opt/openresty-systemtap-toolkit/fix-lua-bt temp.bt >t1.bt # --without-luajit-gc64 | lua 模式编译时需要使用此参数
/opt/openresty-systemtap-toolkit/fix-lua-bt temp.bt >${name}.bt /opt/openresty-systemtap-toolkit/ngx-sample-lua-bt -p $pid --luajit20 -t 30 >temp.bt
elif [ $1 == "c" ]; then /opt/openresty-systemtap-toolkit/fix-lua-bt temp.bt >${name}_${pid}.bt
# /opt/openresty-systemtap-toolkit/sample-bt -p 496435 -t 10 -u > t2.bt elif [ $1 == "c" ]; then
/opt/openresty-systemtap-toolkit/sample-bt -p $pid -t 10 -u > ${name}.bt /opt/openresty-systemtap-toolkit/sample-bt -p $pid -t 10 -u > ${name}_${pid}.bt
else else
echo "type is only lua/c" echo "type is only lua/c"
exit exit
fi fi
/opt/FlameGraph/stackcollapse-stap.pl ${name}_${pid}.bt >${name}_${pid}.cbt
/opt/FlameGraph/flamegraph.pl ${name}_${pid}.cbt >${name}_${pid}.svg
rm -f temp.bt ${name}_${pid}.bt ${name}_${pid}.cbt
echo "strace:$pid, end!"
echo "${name}_${pid}.svg -- make ok"
done
# if [ $1 == "lua" ]; then
# # /opt/openresty-systemtap-toolkit/ngx-sample-lua-bt -p 377452 --luajit20 -t 30 >temp.bt
# /opt/openresty-systemtap-toolkit/ngx-sample-lua-bt -p $pid --luajit20 -t 30 >temp.bt
# # /opt/openresty-systemtap-toolkit/fix-lua-bt temp.bt >t1.bt
# /opt/openresty-systemtap-toolkit/fix-lua-bt temp.bt >${name}.bt
# elif [ $1 == "c" ]; then
# # /opt/openresty-systemtap-toolkit/sample-bt -p 496435 -t 10 -u > t2.bt
# /opt/openresty-systemtap-toolkit/sample-bt -p $pid -t 10 -u > ${name}.bt
# else
# echo "type is only lua/c"
# exit
# fi
# # debuginfo-install kernel-3.10.0-1160.80.1.el7.x86_64
# # /opt/FlameGraph/stackcollapse-perf.pl perf.unfold &> perf.folded
# # /opt/FlameGraph/flamegraph.pl perf.folded > perf.svg
# /opt/FlameGraph/stackcollapse-perf.pl perf.unfold &> perf.folded # /opt/FlameGraph/stackcollapse-stap.pl ${name}.bt >${name}.cbt
# /opt/FlameGraph/flamegraph.pl perf.folded > perf.svg # /opt/FlameGraph/flamegraph.pl ${name}.cbt >${name}.svg
# rm -f temp.bt ${name}.bt ${name}.cbt
/opt/FlameGraph/stackcollapse-stap.pl ${name}.bt >${name}.cbt
/opt/FlameGraph/flamegraph.pl ${name}.cbt >${name}.svg
rm -f temp.bt ${name}.bt ${name}.cbt
+22 -6
View File
@@ -54,7 +54,9 @@ def createBgTask():
"period": "minute-n", "period": "minute-n",
"minute-n": "1", "minute-n": "1",
} }
createBgTaskByName(getPluginName(), args)
if mw.isAppleSystem():
createBgTaskByName(getPluginName(), args)
def createBgTaskByName(name, args): def createBgTaskByName(name, args):
@@ -97,8 +99,14 @@ logs_file=$plugin_path/${rname}.log
''' % (mw_dir, name, getServerDir(), getPluginDir()) ''' % (mw_dir, name, getServerDir(), getPluginDir())
cmd += 'echo "★【`date +"%Y-%m-%d %H:%M:%S"`】 STSRT★" >> $logs_file' + "\n" cmd += 'echo "★【`date +"%Y-%m-%d %H:%M:%S"`】 STSRT★" >> $logs_file' + "\n"
cmd += 'echo ">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>" >> $logs_file' + "\n" cmd += 'echo ">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>" >> $logs_file' + "\n"
cmd += 'echo "cd $mw_dir && source bin/activate && python3 $script_path/tool_task.py run >> $logs_file 2>&1"' + "\n"
cmd += 'cd $mw_dir && source bin/activate && python3 $script_path/tool_task.py run >> $logs_file 2>&1' + "\n" if mw.isAppleSystem():
cmd += 'echo "cd $mw_dir && source bin/activate && python3 $script_path/tool_task.py run >> $logs_file 2>&1"' + "\n"
cmd += 'cd $mw_dir && source bin/activate && python3 $script_path/tool_task.py run >> $logs_file 2>&1' + "\n"
else:
cmd += 'echo "cd $mw_dir && source bin/activate && bash $script_path/shell/cpu_usage_file.sh >> $logs_file 2>&1"' + "\n"
cmd += 'cd $mw_dir && source bin/activate && bash $script_path/shell/cpu_usage.sh >> $logs_file 2>&1' + "\n"
cmd += 'echo "【`date +"%Y-%m-%d %H:%M:%S"`】 END★" >> $logs_file' + "\n" cmd += 'echo "【`date +"%Y-%m-%d %H:%M:%S"`】 END★" >> $logs_file' + "\n"
cmd += 'echo "<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<" >> $logs_file' + "\n" cmd += 'echo "<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<" >> $logs_file' + "\n"
@@ -128,6 +136,9 @@ logs_file=$plugin_path/${rname}.log
def removeBgTask(): def removeBgTask():
if not mw.isAppleSystem():
return False
cfg_list = getConfigData() cfg_list = getConfigData()
for x in range(len(cfg_list)): for x in range(len(cfg_list)):
cfg = cfg_list[x] cfg = cfg_list[x]
@@ -147,10 +158,15 @@ def removeBgTask():
def getCpuUsed(): def getCpuUsed():
import psutil
used = psutil.cpu_percent(interval=1)
path = getServerDir() + "/cpu.info" path = getServerDir() + "/cpu.info"
mw.writeFile(path, str(int(used))) if mw.isAppleSystem():
import psutil
used = psutil.cpu_percent(interval=1)
mw.writeFile(path, str(int(used)))
else:
cmd = "top -bn 1 | fgrep 'Cpu(s)' | awk '{print 100 -$8}' | awk -F . '{print $1}'"
data = mw.execShell(cmd)
mw.writeFile(path, str(int(data[0].strip())))
def run(): def run():
+34 -8
View File
@@ -2,7 +2,7 @@
local json = require "cjson" local json = require "cjson"
local ngx_match = ngx.re.find local ngx_match = ngx.re.find
local __WAF = require "common" local __WAF = require "waf_common"
-- print(json.encode(__C)) -- print(json.encode(__C))
local C = __WAF:getInstance() local C = __WAF:getInstance()
@@ -11,11 +11,11 @@ local config = require "waf_config"
local site_config = require "waf_site" local site_config = require "waf_site"
local config_domains = require "waf_domains" local config_domains = require "waf_domains"
-- C:D("config:"..C:to_json(config))
C:setConfData(config, site_config) C:setConfData(config, site_config)
C:setDebug(true) C:setDebug(true)
-- C:D("config:"..C:to_json(config))
local get_html = require "html_get" local get_html = require "html_get"
local post_html = require "html_post" local post_html = require "html_post"
@@ -36,7 +36,8 @@ local url_rules = require "rule_url"
local url_white_rules = require "rule_url_white" local url_white_rules = require "rule_url_white"
local server_name = string.gsub(C:get_sn(config_domains),'_','.') -- local server_name = string.gsub(C:get_sn(config_domains),'_','.')
local server_name = C:get_sn(config_domains)
local function initParams() local function initParams()
local data = {} local data = {}
@@ -246,7 +247,8 @@ local function waf_drop_ip()
end end
local function waf_cc() local function waf_cc()
if not config['cc']['open'] or not C:is_site_config('cc') then return false end if not config['cc']['open'] then return false end
if not C:is_site_config('cc') then return false end
local ip = params['ip'] local ip = params['ip']
@@ -342,13 +344,22 @@ local function waf_cc_increase()
local make_uri = "/"..make_uri_str local make_uri = "/"..make_uri_str
if params['uri_request_args']['token'] then if params['uri_request_args']['token'] then
ngx.header.content_type = "application/json"
local args_token = params['uri_request_args']['token'] local args_token = params['uri_request_args']['token']
if args_token == make_token then if args_token == make_token then
ngx.shared.waf_limit:set(cache_token, 1, config['safe_verify']['time']) ngx.shared.waf_limit:set(cache_token, 1, tonumber(config['safe_verify']['time']))
local data = get_return_state(0, "ok") local data = get_return_state(0, "ok")
ngx.say(json.encode(data)) ngx.say(json.encode(data))
ngx.exit(200) ngx.exit(200)
end end
-- C:D("debug[args]:"..tostring(params['uri_request_args']['debug']))
-- if params['uri_request_args']['debug'] == 'ok' then
-- ngx.header.content_type = "application/json"
-- local data = get_return_state(0, "ok")
-- ngx.say(json.encode(data))
-- ngx.exit(200)
-- end
end end
local cc_html = ngx.re.gsub(cc_safe_js_html, "{uri}", make_uri_str) local cc_html = ngx.re.gsub(cc_safe_js_html, "{uri}", make_uri_str)
@@ -521,43 +532,58 @@ end
function waf() function waf()
min_route() min_route()
-- C:D("min_route")
-- white ip -- white ip
if waf_ip_white() then return true end if waf_ip_white() then return true end
-- C:D("waf_ip_white")
-- url white -- url white
if waf_url_white() then return true end if waf_url_white() then return true end
-- C:D("waf_url_white")
-- black ip -- black ip
if waf_ip_black() then return true end if waf_ip_black() then return true end
-- C:D("waf_ip_black")
-- 封禁ip返回 -- 封禁ip返回
if waf_drop_ip() then return true end if waf_drop_ip() then return true end
-- C:D("waf_drop_ip")
-- ua check -- ua check
if waf_user_agent() then return true end if waf_user_agent() then return true end
-- C:D("waf_user_agent")
if waf_url() then return true end if waf_url() then return true end
-- C:D("waf_url")
-- cc setting -- cc setting
if waf_cc_increase() then return true end if waf_cc_increase() then return true end
-- C:D("waf_cc_increase")
if waf_cc() then return true end if waf_cc() then return true end
-- C:D("waf_cc")
-- cookie检查 -- cookie检查
if waf_cookie() then return true end if waf_cookie() then return true end
-- C:D("waf_cookie")
-- args参数拦截 -- args参数拦截
if waf_get_args() then return true end if waf_get_args() then return true end
-- C:D("waf_get_args")
-- 扫描软件禁止 -- 扫描软件禁止
if waf_scan_black() then return true end if waf_scan_black() then return true end
-- C:D("waf_scan_black")
if waf_post() then return true end if waf_post() then return true end
-- C:D("waf_post")
if site_config[server_name] and site_config[server_name]['open'] then if site_config[server_name] and site_config[server_name]['open'] then
if X_Forwarded() then return true end if X_Forwarded() then return true end
-- C:D("X_Forwarded")
if post_X_Forwarded() then return true end if post_X_Forwarded() then return true end
-- C:D("post_X_Forwarded")
if url_ext() then return true end if url_ext() then return true end
-- C:D("url_ext")
if post_data() then return true end if post_data() then return true end
-- C:D("post_data")
end end
end end
+11
View File
@@ -0,0 +1,11 @@
local waf_root = "{$WAF_ROOT}"
local waf_cpath = waf_root.."/waf/lua/?.lua;"..waf_root.."/waf/conf/?.lua;"..waf_root.."/waf/html/?.lua;"
local waf_sopath = waf_root.."/waf/conf/?.so;"
if not package.path:find(waf_cpath) then
package.path = waf_cpath .. package.path
end
if not package.cpath:find(waf_sopath) then
package.cpath = waf_sopath .. package.cpath
end
+41 -14
View File
@@ -1,31 +1,58 @@
local waf_root = "{$WAF_ROOT}"
-- local waf_cpath = waf_root.."/waf/lua/?.lua;"..waf_root.."/waf/conf/?.lua;"..waf_root.."/waf/html/?.lua;"
-- local waf_sopath = waf_root.."/waf/conf/?.so;"
-- if not package.path:find(waf_cpath) then
-- package.path = waf_cpath .. package.path
-- end
-- if not package.cpath:find(waf_sopath) then
-- package.cpath = waf_sopath .. package.cpath
-- end
local json = require "cjson" local json = require "cjson"
local waf_root = "{$WAF_ROOT}" local __WAF_C = require "waf_common"
local cpath = waf_root.."/waf/" local WAF_C = __WAF_C:getInstance()
local __C = require "common" local waf_config = require "waf_config"
local C = __C:getInstance() local waf_site_config = require "waf_site"
WAF_C:setConfData(waf_config, waf_site_config)
WAF_C:setDebug(true)
-- C:D("init worker"..tostring(ngx.worker.id()))
local function timer_stats_total_log(premature) local function waf_timer_stats_total_log(premature)
C:timer_stats_total() WAF_C:timer_stats_total()
end end
local waf_clean_expire_data = function(premature)
WAF_C:clean_log()
end
ngx.shared.waf_limit:set("cpu_usage", 0, 10) ngx.shared.waf_limit:set("cpu_usage", 0, 10)
function timer_every_get_cpu(premature) function waf_timer_every_get_cpu(premature)
local cpu_percent = C:read_file_body(waf_root.."/cpu.info") if WAF_C:file_exists('/proc/stat') then
if cpu_percent then local lua_cpu_percent = WAF_C:get_cpu_percent()
ngx.shared.waf_limit:set("cpu_usage", tonumber(cpu_percent), 10) -- WAF_C:D("lua_cpu_percent:"..tostring(lua_cpu_percent))
ngx.shared.waf_limit:set("cpu_usage", math.floor(lua_cpu_percent), 10)
else else
ngx.shared.waf_limit:set("cpu_usage", 0, 10) local cpu_percent = WAF_C:read_file_body(waf_root.."/cpu.info")
-- WAF_C:D("cpu_usage:"..tostring(cpu_percent ))
if cpu_percent then
ngx.shared.waf_limit:set("cpu_usage", tonumber(cpu_percent), 10)
else
ngx.shared.waf_limit:set("cpu_usage", 0, 10)
end
end end
end end
if 0 == ngx.worker.id() then if ngx.worker.id() == 0 then
ngx.timer.every(5, timer_every_get_cpu)
ngx.timer.every(6, waf_timer_every_get_cpu)
-- 异步执行 -- 异步执行
ngx.timer.every(3, timer_stats_total_log) ngx.timer.every(3, waf_timer_stats_total_log)
ngx.timer.every(10, waf_clean_expire_data)
WAF_C:cron()
end end
@@ -1,3 +1,14 @@
local waf_root = "{$WAF_ROOT}"
local waf_cpath = waf_root.."/waf/lua/?.lua;"..waf_root.."/waf/conf/?.lua;"..waf_root.."/waf/html/?.lua;"
local waf_sopath = waf_root.."/waf/conf/?.so;"
if not package.path:find(waf_cpath) then
package.path = waf_cpath .. package.path
end
if not package.cpath:find(waf_sopath) then
package.cpath = waf_sopath .. package.cpath
end
local setmetatable = setmetatable local setmetatable = setmetatable
local _M = { _VERSION = '0.02' } local _M = { _VERSION = '0.02' }
@@ -6,10 +17,11 @@ local mt = { __index = _M }
local json = require "cjson" local json = require "cjson"
local sqlite3 = require "lsqlite3" local sqlite3 = require "lsqlite3"
local ngx_re = require "ngx.re"
local ngx_match = ngx.re.find local ngx_match = ngx.re.find
local debug_mode = false local debug_mode = false
local waf_root = "{$WAF_ROOT}"
local cpath = waf_root.."/waf/" local cpath = waf_root.."/waf/"
local log_dir = waf_root.."/logs/" local log_dir = waf_root.."/logs/"
local rpath = cpath.."/rule/" local rpath = cpath.."/rule/"
@@ -31,43 +43,26 @@ function _M.new(self)
end end
function _M.getInstance(self) -- function _M.getInstance(self)
if rawget(self, "instance") == nil then -- if rawget(self, "instance") == nil then
rawset(self, "instance", self:new()) -- rawset(self, "instance", self.new())
-- end
-- assert(self.instance ~= nil)
-- return self.instance
-- end
if 0 == ngx.worker.id() then function _M.getInstance(self)
self:cron() if self.instance == nil then
end self.instance = self:new()
end end
assert(self.instance ~= nil) assert(self.instance ~= nil)
return self.instance return self.instance
end end
function _M.initDB(self)
local path = log_dir .. "/waf.db"
db, err = sqlite3.open(path)
if err then
self:D("initDB err:"..tostring(err))
return nil
end
db:exec([[PRAGMA synchronous = 0]])
db:exec([[PRAGMA cache_size = 8000]])
db:exec([[PRAGMA page_size = 32768]])
db:exec([[PRAGMA journal_mode = wal]])
db:exec([[PRAGMA journal_size_limit = 1073741824]])
return db
end
-- 后台任务 -- 后台任务
function _M.cron(self) function _M.cron(self)
local timer_every_get_data = function (premature)
self.clean_log() local timer_every_import_data = function(premature)
end
ngx.timer.every(10, timer_every_get_data)
local timer_every_import_data = function (premature)
local llen, _ = ngx.shared.waf_limit:llen('waf_limit_logs') local llen, _ = ngx.shared.waf_limit:llen('waf_limit_logs')
if llen == 0 then if llen == 0 then
@@ -81,7 +76,6 @@ function _M.cron(self)
local stmt2 = db:prepare[[INSERT INTO logs(time, ip, domain, server_name, method, status_code, uri, user_agent, rule_name, reason) local stmt2 = db:prepare[[INSERT INTO logs(time, ip, domain, server_name, method, status_code, uri, user_agent, rule_name, reason)
VALUES(:time, :ip, :domain, :server_name, :method, :status_code, :uri, :user_agent, :rule_name, :reason)]] VALUES(:time, :ip, :domain, :server_name, :method, :status_code, :uri, :user_agent, :rule_name, :reason)]]
if not stmt2 then if not stmt2 then
self:D("waf timer db:prepare fail!:"..tostring(stmt2)) self:D("waf timer db:prepare fail!:"..tostring(stmt2))
return false return false
@@ -126,6 +120,22 @@ function _M.cron(self)
ngx.timer.every(0.5, timer_every_import_data) ngx.timer.every(0.5, timer_every_import_data)
end end
function _M.initDB(self)
local path = log_dir .. "/waf.db"
db, err = sqlite3.open(path)
if err then
self:D("initDB err:"..tostring(err))
return nil
end
db:exec([[PRAGMA synchronous = 0]])
db:exec([[PRAGMA cache_size = 8000]])
db:exec([[PRAGMA page_size = 32768]])
db:exec([[PRAGMA journal_mode = wal]])
db:exec([[PRAGMA journal_size_limit = 1073741824]])
return db
end
function _M.clean_log(self) function _M.clean_log(self)
local db = self:initDB() local db = self:initDB()
@@ -382,6 +392,12 @@ function _M.read_file(self, name)
return data return data
end end
function _M.file_exists(self,path)
local file = io.open(path, "rb")
if file then file:close() end
return file ~= nil
end
function _M.select_rule(self, rules) function _M.select_rule(self, rules)
if not rules then return {} end if not rules then return {} end
@@ -751,6 +767,34 @@ function _M.is_key(self, arr, key)
return false return false
end end
function _M.get_cpu_stat(self)
local cpu_total = 0
local fp = io.open('/proc/stat','r')
local cpu_line = fp:read()
fp:close()
local list = ngx_re.split(cpu_line," ")
table.remove(list,1)
table.remove(list,1)
local idie = list[4]
for i,v in pairs(list)
do
cpu_total = cpu_total + v
end
local use_percent = tonumber(100-(idie/cpu_total)*100)
return cpu_total,idie,use_percent
end
function _M.get_cpu_percent(self)
local cpu_total,idie,use_percent = self:get_cpu_stat()
ngx.sleep(2)
local cpu_total2,idie2,use_percent2 = self:get_cpu_stat()
local cpu_usage_percent = tonumber(100-(((idie2-idie)/(cpu_total2-cpu_total))*100))
return cpu_usage_percent
end
function _M.return_post_data(self) function _M.return_post_data(self)
if method ~= "POST" then return false end if method ~= "POST" then return false end
+1 -1
View File
@@ -1 +1 @@
[[[127, 0, 0, 1], [127, 0, 0, 255]]] [[[127,0,0,1], [127, 0, 0, 255]]]
+13
View File
@@ -0,0 +1,13 @@
lua_package_path "{$SERVER_PATH}/web_conf/nginx/lua/?.lua;{$SERVER_PATH}/openresty/lualib/?.lua;;";
lua_package_cpath "{$SERVER_PATH}/web_conf/nginx/lua/?.so;{$SERVER_PATH}/openresty/lualib/?.so;;";
lua_code_cache on;
#init_by_lua_file
init_by_lua_file {$SERVER_PATH}/web_conf/nginx/lua/empty.lua;
#init_worker_by_lua
init_worker_by_lua_file {$SERVER_PATH}/web_conf/nginx/lua/empty.lua;
#access_by_lua_file
access_by_lua_file {$SERVER_PATH}/web_conf/nginx/lua/empty.lua;
+2 -1
View File
@@ -16,7 +16,7 @@ http
{ {
include mime.types; include mime.types;
#include luawaf.conf; include {$SERVER_PATH}/web_conf/nginx/lua/lua.conf;
#include proxy.conf; #include proxy.conf;
default_type application/octet-stream; default_type application/octet-stream;
@@ -28,6 +28,7 @@ http
server_names_hash_bucket_size 512; server_names_hash_bucket_size 512;
client_header_buffer_size 32k; client_header_buffer_size 32k;
large_client_header_buffers 4 32k; large_client_header_buffers 4 32k;
client_body_buffer_size 50m;
client_max_body_size 50m; client_max_body_size 50m;
sendfile on; sendfile on;
+25
View File
@@ -131,10 +131,35 @@ def confReplace():
content = content.replace('{$OS_USER}', user) content = content.replace('{$OS_USER}', user)
content = content.replace('{$OS_USER_GROUP}', user_group) content = content.replace('{$OS_USER_GROUP}', user_group)
# ng_conf_md5 = ''
# ng_conf_md5_file = getServerDir() + '/nginx_conf.md5'
# if not os.path.exists(ng_conf_md5_file):
# ng_conf_md5 = mw.md5(content)
# mw.writeFile(ng_conf_md5_file, ng_conf_md5)
# else:
# ng_conf_md5 = mw.writeFile(ng_conf_md5_file).strip()
# 主配置文件 # 主配置文件
nconf = getServerDir() + '/nginx/conf/nginx.conf' nconf = getServerDir() + '/nginx/conf/nginx.conf'
mw.writeFile(nconf, content) mw.writeFile(nconf, content)
# lua配置
lua_conf_dir = mw.getServerDir() + '/web_conf/nginx/lua'
if not os.path.exists(lua_conf_dir):
mw.execShell('mkdir -p ' + lua_conf_dir)
lua_conf = lua_conf_dir + '/lua.conf'
lua_conf_tpl = getPluginDir() + '/conf/lua.conf'
lua_content = mw.readFile(lua_conf_tpl)
lua_content = lua_content.replace('{$SERVER_PATH}', service_path)
mw.writeFile(lua_conf, lua_content)
empty_lua = lua_conf_dir + '/empty.lua'
if not os.path.exists(empty_lua):
mw.writeFile(empty_lua, '')
mw.opLuaMakeAll()
# 静态配置 # 静态配置
php_conf = mw.getServerDir() + '/web_conf/php/conf' php_conf = mw.getServerDir() + '/web_conf/php/conf'
if not os.path.exists(php_conf): if not os.path.exists(php_conf):
+14 -1
View File
@@ -2,6 +2,8 @@
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH export PATH
# cd /www/server/mdserver-web/plugins/openresty && bash install.sh install 1.21.4.1
curPath=`pwd` curPath=`pwd`
rootPath=$(dirname "$curPath") rootPath=$(dirname "$curPath")
rootPath=$(dirname "$rootPath") rootPath=$(dirname "$rootPath")
@@ -57,10 +59,21 @@ Install_openresty()
mkdir -p ${openrestyDir} mkdir -p ${openrestyDir}
echo '正在安装脚本文件...' > $install_tmp echo '正在安装脚本文件...' > $install_tmp
# wget -O openresty-1.21.4.1.tar.gz https://openresty.org/download/openresty-1.21.4.1.tar.gz
if [ ! -f ${openrestyDir}/openresty-${VERSION}.tar.gz ];then if [ ! -f ${openrestyDir}/openresty-${VERSION}.tar.gz ];then
wget -O ${openrestyDir}/openresty-${VERSION}.tar.gz https://openresty.org/download/openresty-${VERSION}.tar.gz wget --no-check-certificate -O ${openrestyDir}/openresty-${VERSION}.tar.gz https://openresty.org/download/openresty-${VERSION}.tar.gz -T 3
fi fi
DOWNLOAD_SIZE=`wc -c ${openrestyDir}/openresty-${VERSION}.tar.gz | awk '{print $1}'`
if [ "$DOWNLOAD_SIZE" == "0" ];then
echo 'download failed, download again'
rm -rf ${openrestyDir}/openresty-${VERSION}.tar.gz
fi
# Last Download Method
if [ ! -f ${openrestyDir}/openresty-${VERSION}.tar.gz ];then
wget --no-check-certificate -O ${openrestyDir}/openresty-${VERSION}.tar.gz https://dl.midoks.me/soft/openresty/openresty-${VERSION}.tar.gz -T 3
fi
cd ${openrestyDir} && tar -zxvf openresty-${VERSION}.tar.gz cd ${openrestyDir} && tar -zxvf openresty-${VERSION}.tar.gz
+17 -9
View File
@@ -49,15 +49,20 @@ if [ "$OSNAME" == "ubuntu" ];then
fi fi
# apt install $(grep-aptavail -S PHP-defaults -s Package -n) # apt install $(grep-aptavail -S PHP-defaults -s Package -n)
if [ ! -f /etc/apt/sources.list.d/php.list ] && [ "$OSNAME" == "debian" ];then if [ ! -f /etc/apt/sources.list.d/php.list ] && [ "$OSNAME" == "debian" ];then
# install php source # install php source
apt update -y
apt update -y apt -y install apt-transport-https lsb-release ca-certificates curl
apt -y install apt-transport-https lsb-release ca-certificates curl cn=$(curl -fsSL -m 10 http://ipinfo.io/json | grep "\"country\": \"CN\"")
curl -sSLo /usr/share/keyrings/deb.sury.org-php.gpg https://packages.sury.org/php/apt.gpg if [ ! -z "$cn" ];then
sh -c 'echo "deb [signed-by=/usr/share/keyrings/deb.sury.org-php.gpg] https://packages.sury.org/php/ $(lsb_release -sc) main" > /etc/apt/sources.list.d/php.list' curl -sSLo /usr/share/keyrings/deb.sury.org-php.gpg https://mirror.sjtu.edu.cn/sury/php/apt.gpg
apt update -y sh -c 'echo "deb [signed-by=/usr/share/keyrings/deb.sury.org-php.gpg] https://mirror.sjtu.edu.cn/sury/php/ $(lsb_release -sc) main" > /etc/apt/sources.list.d/php.list'
else
curl -sSLo /usr/share/keyrings/deb.sury.org-php.gpg https://packages.sury.org/php/apt.gpg
sh -c 'echo "deb [signed-by=/usr/share/keyrings/deb.sury.org-php.gpg] https://packages.sury.org/php/ $(lsb_release -sc) main" > /etc/apt/sources.list.d/php.list'
fi
apt update -y
fi fi
@@ -78,6 +83,7 @@ if [ "${action}" == "install" ] && [ -d ${serverPath}/php-apt/${type} ];then
# 安装通用扩展 # 安装通用扩展
echo "install PHP-APT[${type}] extend start" echo "install PHP-APT[${type}] extend start"
cd ${rootPath}/plugins/php-apt/versions && bash common.sh ${type:0:1}.${type:1:2} install curl
cd ${rootPath}/plugins/php-apt/versions && bash common.sh ${type:0:1}.${type:1:2} install gd cd ${rootPath}/plugins/php-apt/versions && bash common.sh ${type:0:1}.${type:1:2} install gd
cd ${rootPath}/plugins/php-apt/versions && bash common.sh ${type:0:1}.${type:1:2} install iconv cd ${rootPath}/plugins/php-apt/versions && bash common.sh ${type:0:1}.${type:1:2} install iconv
cd ${rootPath}/plugins/php-apt/versions && bash common.sh ${type:0:1}.${type:1:2} install exif cd ${rootPath}/plugins/php-apt/versions && bash common.sh ${type:0:1}.${type:1:2} install exif
@@ -85,14 +91,16 @@ if [ "${action}" == "install" ] && [ -d ${serverPath}/php-apt/${type} ];then
cd ${rootPath}/plugins/php-apt/versions && bash common.sh ${type:0:1}.${type:1:2} install xml cd ${rootPath}/plugins/php-apt/versions && bash common.sh ${type:0:1}.${type:1:2} install xml
cd ${rootPath}/plugins/php-apt/versions && bash common.sh ${type:0:1}.${type:1:2} install mcrypt cd ${rootPath}/plugins/php-apt/versions && bash common.sh ${type:0:1}.${type:1:2} install mcrypt
cd ${rootPath}/plugins/php-apt/versions && bash common.sh ${type:0:1}.${type:1:2} install mysqlnd cd ${rootPath}/plugins/php-apt/versions && bash common.sh ${type:0:1}.${type:1:2} install mysqlnd
cd ${rootPath}/plugins/php-apt/versions && bash common.sh ${type:0:1}.${type:1:2} install mysql
cd ${rootPath}/plugins/php-apt/versions && bash common.sh ${type:0:1}.${type:1:2} install gettext cd ${rootPath}/plugins/php-apt/versions && bash common.sh ${type:0:1}.${type:1:2} install gettext
cd ${rootPath}/plugins/php-apt/versions && bash common.sh ${type:0:1}.${type:1:2} install redis cd ${rootPath}/plugins/php-apt/versions && bash common.sh ${type:0:1}.${type:1:2} install redis
cd ${rootPath}/plugins/php-apt/versions && bash common.sh ${type:0:1}.${type:1:2} install memcached cd ${rootPath}/plugins/php-apt/versions && bash common.sh ${type:0:1}.${type:1:2} install memcached
cd ${rootPath}/plugins/php-apt/versions && bash common.sh ${type:0:1}.${type:1:2} install mbstring
echo "install PHP-APT[${type}] extend end" echo "install PHP-APT[${type}] extend end"
if [ ! -f /usr/local/bin/composer ];then if [ ! -f /usr/local/bin/composer ];then
cd /tmp cd /tmp
curl -sS https://getcomposer.org/installer | php curl -sS https://getcomposer.org/installer | /usr/bin/php${type:0:1}.${type:1:2}
mv composer.phar /usr/local/bin/composer mv composer.phar /usr/local/bin/composer
fi fi
fi fi
+1 -2
View File
@@ -57,5 +57,4 @@ fi
echo "apt install -y php${version}-${extName}" echo "apt install -y php${version}-${extName}"
echo "apt remove -y php${version}-${extName}" echo "apt remove -y php${version}-${extName}"
systemctl restart php${version}-fpm
+31
View File
@@ -35,6 +35,24 @@
"shell": "mysqlnd.sh", "shell": "mysqlnd.sh",
"check": "mysqlnd" "check": "mysqlnd"
}, },
{
"name": "mysql",
"versions": [
"56",
"70",
"71",
"72",
"73",
"74",
"80",
"81",
"82"
],
"type": "数据库",
"msg": "用于使用MySQL数据库的模块!",
"shell": "mysql.sh",
"check": "mysql"
},
{ {
"name": "sqlite3", "name": "sqlite3",
"versions": [ "versions": [
@@ -215,6 +233,19 @@
"shell": "xml.sh", "shell": "xml.sh",
"check": "xml" "check": "xml"
}, },
{
"name": "curl",
"versions": [
"74",
"80",
"81",
"82"
],
"type": "通用扩展",
"msg": "通用CURL库!",
"shell": "curl.sh",
"check": "curl"
},
{ {
"name": "gd", "name": "gd",
"versions": [ "versions": [
+4 -1
View File
@@ -68,6 +68,8 @@ if [ "${action}" == "install" ] && [ -d ${serverPath}/php-yum/${type} ];then
# 安装通用扩展 # 安装通用扩展
echo "install PHP-YUM[${type}] extend start" echo "install PHP-YUM[${type}] extend start"
cd ${rootPath}/plugins/php-yum/versions && bash common.sh ${type} install mysqlnd
cd ${rootPath}/plugins/php-yum/versions && bash common.sh ${type} install mysql
cd ${rootPath}/plugins/php-yum/versions && bash common.sh ${type} install gd cd ${rootPath}/plugins/php-yum/versions && bash common.sh ${type} install gd
cd ${rootPath}/plugins/php-yum/versions && bash common.sh ${type} install iconv cd ${rootPath}/plugins/php-yum/versions && bash common.sh ${type} install iconv
cd ${rootPath}/plugins/php-yum/versions && bash common.sh ${type} install exif cd ${rootPath}/plugins/php-yum/versions && bash common.sh ${type} install exif
@@ -77,11 +79,12 @@ if [ "${action}" == "install" ] && [ -d ${serverPath}/php-yum/${type} ];then
cd ${rootPath}/plugins/php-yum/versions && bash common.sh ${type} install gettext cd ${rootPath}/plugins/php-yum/versions && bash common.sh ${type} install gettext
cd ${rootPath}/plugins/php-yum/versions && bash common.sh ${type} install redis cd ${rootPath}/plugins/php-yum/versions && bash common.sh ${type} install redis
cd ${rootPath}/plugins/php-yum/versions && bash common.sh ${type} install memcached cd ${rootPath}/plugins/php-yum/versions && bash common.sh ${type} install memcached
cd ${rootPath}/plugins/php-yum/versions && bash common.sh ${type} install mbstring
echo "install PHP-YUM[${type}] extend end" echo "install PHP-YUM[${type}] extend end"
if [ ! -f /usr/local/bin/composer ];then if [ ! -f /usr/local/bin/composer ];then
cd /tmp cd /tmp
curl -sS https://getcomposer.org/installer | php curl -sS https://getcomposer.org/installer | /opt/remi/php${type}/root/usr/bin/php
mv composer.phar /usr/local/bin/composer mv composer.phar /usr/local/bin/composer
fi fi
fi fi
+1 -1
View File
@@ -28,7 +28,7 @@ fi
Uninstall_php() Uninstall_php()
{ {
# $serverPath/php-ya/init.d/php${PHP_VER} stop yum remove -y php74 php74-php-fpm php74-*
rm -rf $serverPath/php-yum/${PHP_VER} rm -rf $serverPath/php-yum/${PHP_VER}
echo "卸载php-${version}..." > $install_tmp echo "卸载php-${version}..." > $install_tmp
} }
+1 -1
View File
@@ -28,7 +28,7 @@ fi
Uninstall_php() Uninstall_php()
{ {
# $serverPath/php-ya/init.d/php${PHP_VER} stop yum remove -y php81 php81-php-fpm php81-*
rm -rf $serverPath/php-yum/${PHP_VER} rm -rf $serverPath/php-yum/${PHP_VER}
echo "卸载php-${version}..." > $install_tmp echo "卸载php-${version}..." > $install_tmp
} }
+1 -1
View File
@@ -28,7 +28,7 @@ fi
Uninstall_php() Uninstall_php()
{ {
# $serverPath/php-yum/init.d/php${PHP_VER} stop yum remove -y php82 php82-php-fpm php82-*
rm -rf $serverPath/php-yum/${PHP_VER} rm -rf $serverPath/php-yum/${PHP_VER}
echo "卸载php-${version}..." > $install_tmp echo "卸载php-${version}..." > $install_tmp
} }

Some files were not shown because too many files have changed in this diff Show More