Compare commits

...
392 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
128 changed files with 8479 additions and 551 deletions
+5 -2
View File
@@ -151,17 +151,19 @@ data/system.db-journal
data/hook_*.json
data/letsencrypt.json
data/basic_auth.json
data/notify.json
data/api.json
data/bind_domain.pl
data/unauthorized_status.pl
plugins/vip_*
plugins/own_*
plugins/my_*
plugins/l2tp
plugins/openlitespeed
plugins/system_safe
plugins/tamper_proof
plugins/tamper_proof_*
plugins/cryptocurrency_trade
plugins/op_load_balance
plugins/gdrive
plugins/mtproxy
@@ -175,4 +177,5 @@ plugins/file_search
debug.out
data/unauthorized_status.pl
+18 -24
View File
@@ -29,6 +29,7 @@
基本上可以使用,后续会继续优化!欢迎提供意见!
- 吹水组 - https://t.me/mdserver_web
- 交流论坛 - https://bbs.midoks.me
```
如果出现问题,最好私给我面板信息。不要让我猜。如果不提供,不要提出问题,自行解决。 — 座右铭
@@ -73,7 +74,7 @@ phpMyAdmin[4.4.15]支持MySQL[5.5-5.7]
phpMyAdmin[5.2.0]支持MySQL[8.0]
PHP[53-72]支持phpMyAdmin[4.4.15]
PHP[72-81]支持phpMyAdmin[5.2.0]
PHP[72-82]支持phpMyAdmin[5.2.0]
```
# 特别赞助
@@ -98,35 +99,28 @@ docker run -itd --name mw-server --privileged=true -p 7200:7200 -p 80:80 -p 443:
```
### 版本更新 0.12.3
### 版本更新 0.14.0
* 优先使用firewalld防火墙。
* PHP[APT]加入curl扩展。
* 配置添加【未认证响应状态】功能。
* 存在ipv6时,启动时强制开启。
* OP防火墙-性能优化。
* 网站统计优化。
* 优化backup_ftp插件。
* 各种细节优化。
* 加入系统加固插件。
### 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
```
### 备用地址
@@ -134,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
```
@@ -156,22 +150,22 @@ wget -O uninstall.sh https://raw.githubusercontent.com/midoks/mdserver-web/maste
- 需已经安装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使用
```
curl -fsSL https://raw.githubusercontent.com/midoks/mdserver-web/dev/scripts/install_dev.sh | bash
curl -fsSL https://raw.githubusercontent.com/midoks/mdserver-web/dev/scripts/update_dev.sh | bash
curl --insecure -fsSL https://raw.githubusercontent.com/midoks/mdserver-web/dev/scripts/install_dev.sh | bash
curl --insecure -fsSL https://raw.githubusercontent.com/midoks/mdserver-web/dev/scripts/update_dev.sh | bash
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 -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/install_dev.sh | bash
curl --insecure -fsSL https://code.midoks.me/midoks/mdserver-web/raw/branch/dev/scripts/update_dev.sh | bash
```
### 捐赠地址 USDT(TRC20)
+8 -9
View File
@@ -371,8 +371,7 @@ fi
response = urllib.request.urlopen(req, timeout=timeout)
return response
except Exception as ex:
self.getError()
# self.getError()
raise Exception("requestsPost: {}".format(self.getError(str(ex))))
def getRequestJson(self, response):
@@ -1007,7 +1006,7 @@ fi
try:
result = {}
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()
result['issuer'] = ''
@@ -1281,16 +1280,16 @@ fullchain.pem 粘贴到证书输入框
# 获取当前正在使用此证书的网站目录
def getSslUsedSite(self, save_path):
pkey_file = '{}/privkey.pem'.format(save_path)
pkey = public.readFile(pkey_file)
pkey = mw.readFile(pkey_file)
if not pkey:
return False
cert_paths = 'vhost/cert'
import panelSite
args = public.dict_obj()
args = mw.dict_obj()
args.siteName = ''
for c_name in os.listdir(cert_paths):
skey_file = '{}/{}/privkey.pem'.format(cert_paths, c_name)
skey = public.readFile(skey_file)
skey = mw.readFile(skey_file)
if not skey:
continue
if skey == pkey:
@@ -1298,7 +1297,7 @@ fullchain.pem 粘贴到证书输入框
run_path = panelSite.panelSite().GetRunPath(args)
if not run_path:
continue
sitePath = public.M('sites').where(
sitePath = mw.M('sites').where(
'name=?', c_name).getField('path')
if not sitePath:
continue
@@ -1316,7 +1315,7 @@ fullchain.pem 粘贴到证书输入框
self.__config['orders'] = {}
import panelSite
siteObj = panelSite.panelSite()
args = public.dict_obj()
args = mw.dict_obj()
for siteName in os.listdir(cert_path):
try:
cert_file = '{}/{}/fullchain.pem'.format(cert_path, siteName)
@@ -1522,7 +1521,7 @@ fullchain.pem 粘贴到证书输入框
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][
'next_retry_time'] - int(time.time())
if timeout > 0:
+6 -6
View File
@@ -916,7 +916,7 @@ class cert_request:
try:
result = {}
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()
result['issuer'] = ''
@@ -1186,16 +1186,16 @@ fullchain.pem 粘贴到证书输入框
# 获取当前正在使用此证书的网站目录
def getSslUsedSite(self, save_path):
pkey_file = '{}/privkey.pem'.format(save_path)
pkey = public.readFile(pkey_file)
pkey = mw.readFile(pkey_file)
if not pkey:
return False
cert_paths = 'vhost/cert'
import panelSite
args = public.dict_obj()
args = mw.dict_obj()
args.siteName = ''
for c_name in os.listdir(cert_paths):
skey_file = '{}/{}/privkey.pem'.format(cert_paths, c_name)
skey = public.readFile(skey_file)
skey = mw.readFile(skey_file)
if not skey:
continue
if skey == pkey:
@@ -1203,7 +1203,7 @@ fullchain.pem 粘贴到证书输入框
run_path = panelSite.panelSite().GetRunPath(args)
if not run_path:
continue
sitePath = public.M('sites').where(
sitePath = mw.M('sites').where(
'name=?', c_name).getField('path')
if not sitePath:
continue
@@ -1221,7 +1221,7 @@ fullchain.pem 粘贴到证书输入框
self.__config['orders'] = {}
import panelSite
siteObj = panelSite.panelSite()
args = public.dict_obj()
args = mw.dict_obj()
for siteName in os.listdir(cert_path):
try:
cert_file = '{}/{}/fullchain.pem'.format(cert_path, siteName)
+192 -12
View File
@@ -27,7 +27,7 @@ from flask import request
class config_api:
__version = '0.12.3'
__version = '0.14.0'
__api_addr = 'data/api.json'
def __init__(self):
@@ -348,7 +348,23 @@ class config_api:
keyPath = 'ssl/private.pem'
certPath = 'ssl/cert.pem'
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['certPem'] = mw.readFile(certPath)
@@ -380,13 +396,86 @@ class config_api:
mw.writeFile('ssl/input.pl', '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, '未绑定域名!')
return mw.returnJson(False, '先要绑定域名!')
siteName = mw.readFile(bind_domain).strip()
auth_to = mw.getRunDir() + "/tmp"
@@ -404,6 +493,8 @@ class config_api:
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)
@@ -415,6 +506,8 @@ class config_api:
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)
@@ -426,6 +519,9 @@ class config_api:
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):
@@ -509,8 +605,6 @@ class config_api:
return mw.returnJson(True, 'SSL已关闭,请使用http协议访问面板!')
else:
try:
if not os.path.exists('ssl/input.ssl'):
mw.createSSL()
mw.writeFile(sslConf, 'True')
keyPath = mw.getRunDir() + '/ssl/private.pem'
@@ -661,6 +755,67 @@ class config_api:
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):
api_file = self.__api_addr
tmp = mw.readFile(api_file)
@@ -693,10 +848,6 @@ class config_api:
def setPanelTokenApi(self):
op_type = request.form.get('op_type', '').strip()
api_file = self.__api_addr
tmp = mw.readFile(api_file)
data = json.loads(tmp)
if op_type == '1':
token = mw.getRandomString(32)
data['token'] = mw.md5(token)
@@ -706,7 +857,14 @@ class config_api:
mw.writeFile(api_file, json.dumps(data))
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']
stats = {True: '开启', False: '关闭'}
if not 'token_crypt' in data:
@@ -721,6 +879,7 @@ class config_api:
return mw.returnJson(not not data['open'], token)
elif op_type == '3':
limit_addr = request.form.get('limit_addr', '').strip()
data['limit_addr'] = limit_addr.split('\n')
mw.writeLog('API配置', '变更IP限制为[%s]' % limit_addr)
@@ -820,7 +979,9 @@ class config_api:
data['username'] = mw.M('users').where(
"id=?", (1,)).getField('username')
# databases hook 获取
data['hook_tag'] = request.args.get('tag', '')
# databases hook
database_hook_file = 'data/hook_database.json'
if os.path.exists(database_hook_file):
df = mw.readFile(database_hook_file)
@@ -829,7 +990,7 @@ class config_api:
else:
data['hook_database'] = []
# menu hook 获取
# menu hook
menu_hook_file = 'data/hook_menu.json'
if os.path.exists(menu_hook_file):
df = mw.readFile(menu_hook_file)
@@ -838,4 +999,23 @@ class config_api:
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
+1 -1
View File
@@ -348,7 +348,7 @@ class Sql():
except Exception as ex:
return "error: " + str(ex)
def query(self, sql, param):
def query(self, sql, param=()):
# 执行SQL语句返回数据集
self.__getConn()
try:
+111 -2
View File
@@ -247,8 +247,8 @@ class files_api:
os.remove(task_log)
return mw.returnJson(True, '任务已删除!')
# 上传文件
def uploadFileApi(self):
# 上传文件
from werkzeug.utils import secure_filename
from flask import request
@@ -263,6 +263,8 @@ class files_api:
if os.path.exists(filename):
s_path = filename
p_stat = os.stat(s_path)
# print(filename)
f.save(filename)
os.chown(filename, p_stat.st_uid, p_stat.st_gid)
os.chmod(filename, p_stat.st_mode)
@@ -271,6 +273,86 @@ class files_api:
mw.writeLog('文件管理', msg)
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):
rPath = self.rPath
if not os.path.exists(rPath):
@@ -626,7 +708,7 @@ class files_api:
return not path in nDirs
def getDirSize(self, path):
if mw.getOs() == 'darwin':
if mw.isAppleSystem():
tmp = mw.execShell('du -sh ' + path)
else:
tmp = mw.execShell('du -sbh ' + path)
@@ -905,6 +987,33 @@ class files_api:
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):
filename = filename.replace('//', '/')
try:
+12 -11
View File
@@ -36,10 +36,10 @@ class firewall_api:
iptables_file = mw.systemdCfgDir() + '/iptables.service'
if os.path.exists('/usr/sbin/firewalld'):
self.__isFirewalld = True
elif os.path.exists(iptables_file):
self.__isIptables = True
elif os.path.exists('/usr/sbin/ufw'):
self.__isUfw = True
elif os.path.exists(iptables_file):
self.__isIptables = True
elif mw.isAppleSystem():
self.__isMac = True
@@ -123,12 +123,12 @@ class firewall_api:
address = port
if self.__isUfw:
mw.execShell('ufw delete deny from ' + address + ' to any')
elif self.__isIptables:
cmd = 'iptables -D INPUT -s ' + address + ' -j DROP'
mw.execShell(cmd)
elif self.__isFirewalld:
mw.execShell(
'firewall-cmd --permanent --remove-rich-rule=\'rule family=ipv4 source address="' + address + '" drop\'')
elif self.__isIptables:
cmd = 'iptables -D INPUT -s ' + address + ' -j DROP'
mw.execShell(cmd)
else:
pass
@@ -149,14 +149,15 @@ class firewall_api:
return mw.returnJson(False, '失败,不能删除当前面板端口!')
if self.__isUfw:
mw.execShell('ufw delete allow ' + port + '/tcp')
elif self.__isIptables:
mw.execShell(
'iptables -D INPUT -p tcp -m state --state NEW -m tcp --dport ' + port + ' -j ACCEPT')
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:
pass
msg = mw.getInfo('删除防火墙放行端口[{1}]成功!', (port,))
@@ -438,13 +439,13 @@ class firewall_api:
def addAcceptPort(self, port):
if self.__isUfw:
mw.execShell('ufw allow ' + port + '/tcp')
elif self.__isIptables:
cmd = 'iptables -I INPUT -p tcp -m state --state NEW -m tcp --dport ' + port + ' -j ACCEPT'
mw.execShell(cmd)
elif self.__isFirewalld:
port = port.replace(':', '-')
cmd = 'firewall-cmd --permanent --zone=public --add-port=' + port + '/tcp'
mw.execShell(cmd)
elif self.__isIptables:
cmd = 'iptables -I INPUT -p tcp -m state --state NEW -m tcp --dport ' + port + ' -j ACCEPT'
mw.execShell(cmd)
else:
pass
return True
+403 -10
View File
@@ -22,10 +22,14 @@ import hashlib
import shlex
import datetime
import subprocess
import glob
import base64
import re
import db
from random import Random
import db
def execShell(cmdstring, cwd=None, timeout=None, shell=True):
@@ -80,6 +84,10 @@ def getPanelDataDir():
return getRunDir() + '/data'
def getPanelTmp():
return getRunDir() + '/tmp'
def getServerDir():
return getRootDir() + '/server'
@@ -123,6 +131,23 @@ def getAcmeDir():
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():
isTask = getRunDir() + '/tmp/panelTask.pl'
writeFile(isTask, 'True')
@@ -519,6 +544,14 @@ def getDate():
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)
@@ -534,10 +567,39 @@ def writeLog(stype, msg, args=()):
uid = session['uid']
except Exception as e:
pass
# print(getTracebackInfo())
# 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:
import time
@@ -656,7 +718,7 @@ def enCrypt(key, strings):
result = f.encrypt(strings)
return result.decode('utf-8')
except:
print(getTracebackInfo())
writeFileLog(getTracebackInfo())
return strings
@@ -675,7 +737,7 @@ def deCrypt(key, strings):
result = f.decrypt(strings).decode('utf-8')
return result
except:
print(getTracebackInfo())
writeFileLog(getTracebackInfo())
return strings
@@ -694,7 +756,7 @@ def enDoubleCrypt(key, strings):
result = f.encrypt(strings)
return result.decode('utf-8')
except:
print(getTracebackInfo())
writeFileLog(getTracebackInfo())
return strings
@@ -712,10 +774,137 @@ def deDoubleCrypt(key, strings):
result = f.decrypt(strings).decode('utf-8')
return result
except:
print(getTracebackInfo())
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):
'''
建立软连接
@@ -988,7 +1177,7 @@ def getLocalIp():
try:
ipaddress = readFile(filename)
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)
result = ip[0].strip()
if result == '':
@@ -997,7 +1186,7 @@ def getLocalIp():
return result
return ipaddress
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)
result = ip[0].strip()
if result == '':
@@ -1122,6 +1311,15 @@ def setOwn(filename, user, group=None):
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):
# 检查端口是否合法
ports = ['21', '443', '888']
@@ -1207,7 +1405,7 @@ def makeConf():
file = getRunDir() + '/data/json/config.json'
if not os.path.exists(file):
c = {}
c['title'] = '大圣面板'
c['title'] = '金蝉面板'
c['home'] = 'http://github/midoks/mdserver-web'
c['recycle_bin'] = True
c['template'] = 'default'
@@ -1507,7 +1705,7 @@ def getCertName(certPath):
result['notAfter'], "%Y-%m-%d")) - time.time()) / 86400)
return result
except Exception as e:
# print(getTracebackInfo())
writeFileLog(getTracebackInfo())
return None
@@ -1605,6 +1803,176 @@ def getMyORMDb():
o = ormDb.ORM()
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():
@@ -1614,6 +1982,31 @@ def getSshDir():
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()
+12 -7
View File
@@ -91,7 +91,7 @@ class plugins_api:
t = mw.readFile(menu_file)
tlist = json.loads(t)
for menu_data in tlist:
if 'path' in menu_data:
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
@@ -256,13 +256,14 @@ class plugins_api:
data = json.loads(t)
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])
break
mw.writeFile(hookPath, json.dumps(data))
def hookInstall(self, info):
valid_hook = ['backup', 'database']
valid_list_hook = ['menu']
valid_list_hook = ['menu', 'global_static', 'site_cb']
if 'hook' in info:
hooks = info['hook']
for h in hooks:
@@ -280,7 +281,7 @@ class plugins_api:
def hookUninstall(self, info):
valid_hook = ['backup', 'database']
valid_list_hook = ['menu']
valid_list_hook = ['menu', 'global_static', 'site_cb']
if 'hook' in info:
hooks = info['hook']
for h in hooks:
@@ -795,7 +796,8 @@ class plugins_api:
for index in range(len(tmp_data)):
plugins_info.append(tmp_data[index])
except Exception as e:
print(e)
print(json_file)
print(mw.getTracebackInfo())
start = (page - 1) * pageSize
end = start + pageSize
@@ -1014,8 +1016,11 @@ class plugins_api:
# shell 调用
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
if args == '':
+42 -10
View File
@@ -72,6 +72,30 @@ class site_api:
mw.execShell("mkdir -p " + 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 ----- ###
def listApi(self):
limit = request.form.get('limit', '10')
@@ -550,7 +574,7 @@ class site_api:
return mw.returnJson(False, '使用中,先关闭再删除')
mw.execShell('rm -rf ' + ssl_lets_dir)
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' # 生成证书路径
if mw.md5(mw.readFile(csr_acme_path)) == mw.md5(mw.readFile(csr_path)):
return mw.returnJson(False, '使用中,先关闭再删除')
@@ -587,9 +611,9 @@ class site_api:
csr_path = self.sslLetsDir + '/' + site_name + '/fullchain.pem' # 生成证书路径
key_path = self.sslLetsDir + '/' + site_name + '/privkey.pem' # 密钥文件路径
elif ssl_type == 'acme':
csr_path = mw.getAcmeDir() + '/' + site_name + '/fullchain.cer' # 生成证书路径
key_path = mw.getAcmeDir() + '/' + site_name + '/' + \
site_name + '.key' # 密钥文件路径
acme_dir = mw.getAcmeDomainDir(site_name)
csr_path = acme_dir + '/fullchain.cer' # 生成证书路径
key_path = acme_dir + '/' + site_name + '.key' # 密钥文件路径
key = mw.readFile(key_path)
csr = mw.readFile(csr_path)
@@ -989,16 +1013,17 @@ class site_api:
# print(cmd)
result = mw.execShell(cmd)
src_path = acme_dir + '/' + domains[0]
src_path = mw.getAcmeDomainDir(domains[0])
src_cert = src_path + '/fullchain.cer'
src_key = src_path + '/' + domains[0] + '.key'
src_cert.replace("\*", "*")
msg = '签发失败,您尝试申请证书的失败次数已达上限!<p>1、检查域名是否绑定到对应站点</p>\
<p>2、检查域名是否正确解析到本服务器,或解析还未完全生效</p>\
<p>3、如果您的站点设置了反向代理,或使用了CDN,请先将其关闭</p>\
<p>4、如果您的站点设置了301重定向,请先将其关闭</p>\
<p>5、如果以上检查都确认没有问题,请尝试更换DNS服务商</p>'
if not os.path.exists(src_cert.replace("\*", "*")):
if not os.path.exists(src_cert):
data = {}
data['err'] = result
data['out'] = result[0]
@@ -1271,6 +1296,7 @@ class site_api:
mw.M('domain').add('pid,name,port,addtime',
(pid, domain_name, domain_port, mw.getDate()))
self.runHook('site_cb', 'add')
return mw.returnJson(True, '域名添加成功!')
def addDirBindApi(self):
@@ -1400,7 +1426,7 @@ class site_api:
data['filename'] = filename
return mw.getJson(data)
# 修改物理路径
# 修改物理路径
def setPathApi(self):
mid = request.form.get('id', '')
path = request.form.get('path', '')
@@ -2529,16 +2555,18 @@ location ^~ {from} {\n\
self.createRootDir(self.sitePath)
self.nginxAddConf()
mw.M('domain').add('pid,name,port,addtime',
(pid, self.siteName, self.sitePort, mw.getDate()))
# 添加更多域名
for domain in siteMenu['domainlist']:
self.addDomain(domain, self.siteName, pid)
mw.M('domain').add('pid,name,port,addtime',
(pid, self.siteName, self.sitePort, mw.getDate()))
data = {}
data['siteStatus'] = False
mw.restartWeb()
self.runHook('site_cb', 'add')
return mw.returnJson(True, '添加成功')
def deleteWSLogs(self, webname):
@@ -2588,6 +2616,8 @@ location ^~ {from} {\n\
mw.execShell('rm -rf ' + ssl_acme_dir)
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(
@@ -2605,6 +2635,8 @@ location ^~ {from} {\n\
mw.M('binding').where("pid=?", (sid,)).delete()
mw.restartWeb()
self.runHook('site_cb', 'delete')
return mw.returnJson(True, '站点删除成功!')
def setEndDate(self, sid, edate):
+27
View File
@@ -166,6 +166,33 @@ class system_api:
except:
return False
def getEnvInfoApi(self, get=None):
serverInfo = {}
serverInfo['status'] = True
sdir = mw.getServerDir()
serverInfo['webserver'] = '未安装'
if os.path.exists(sdir + '/openresty/nginx/sbin/nginx'):
serverInfo['webserver'] = 'OpenResty'
serverInfo['php'] = []
phpversions = ['52', '53', '54', '55', '56', '70', '71',
'72', '73', '74', '80', '81', '82', '83', '84']
phpPath = sdir + '/php/'
for pv in phpversions:
if not os.path.exists(phpPath + pv + '/bin/php'):
continue
serverInfo['php'].append(pv)
serverInfo['mysql'] = False
if os.path.exists(sdir + '/mysql/bin/mysql'):
serverInfo['mysql'] = True
import psutil
try:
diskInfo = psutil.disk_usage('/www')
except:
diskInfo = psutil.disk_usage('/')
serverInfo['disk'] = diskInfo[2]
return mw.returnJson(True, 'ok', serverInfo)
def getPanelInfo(self, get=None):
# 取面板配置
address = mw.GetLocalIp()
+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):
self.__DB_CHARSET = charset
def setHost(self, host):
self.__DB_HOST = host
def setPort(self, port):
self.__DB_PORT = port
+1
View File
@@ -0,0 +1 @@
存放VIP关键数据
+5 -1
View File
@@ -33,7 +33,11 @@ if [ ! -d $WEB_PATH ];then
mkdir -p $WEB_PATH
rsync -vauP --delete --exclude=".*" $GIT_PROJECT_DIR/ $WEB_PATH
else
rsync -vauP --exclude=".*" $GIT_PROJECT_DIR/ $WEB_PATH
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
sysName=`uname`
+2 -2
View File
@@ -1,8 +1,8 @@
{
"ps": "Gitea 是一个开源社区驱动的轻量级代码托管解决方案。",
"ps": "Gitea是一个开源社区驱动的轻量级代码托管解决方案。",
"name": "gitea",
"title": "Gitea",
"versions": ["1.17.2"],
"versions": ["1.17.2","1.18.5"],
"tip": "soft",
"install_pre_inspection":false,
"uninstall_pre_inspection":true,
+1 -1
View File
@@ -14,7 +14,7 @@ if [ -f ${rootPath}/bin/activate ];then
fi
URL_DOWNLOAD=https://dl.gitea.io/
URL_DOWNLOAD=https://dl.gitea.com
bash ${rootPath}/scripts/getos.sh
+1 -1
View File
@@ -8,7 +8,7 @@
"uninstall_pre_inspection":true,
"checks": "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",
"checks":"server/mariadb",
"path":"server/mariadb",
+1 -1
View File
@@ -1185,7 +1185,7 @@ function myLogs(){
<span class="f14 c6 mr20">二进制日志 </span><span class="f14 c6 mr20">' + toSize(rdata.msg) + '</span>\
'+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>\
<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>';
$(".soft-man-con").html(limitCon);
+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
mariadbDir=${serverPath}/source/mariadb
MY_VER=10.6.9
MY_VER=10.6.12
Install_app()
{
+1 -1
View File
@@ -14,7 +14,7 @@ sysName=`uname`
install_tmp=${rootPath}/tmp/mw_install.pl
mariadbDir=${serverPath}/source/mariadb
MY_VER=10.7.4
MY_VER=10.7.8
Install_app()
{
+1 -1
View File
@@ -14,7 +14,7 @@ sysName=`uname`
install_tmp=${rootPath}/tmp/mw_install.pl
mariadbDir=${serverPath}/source/mariadb
MY_VER=10.8.5
MY_VER=10.8.7
Install_app()
{
+1 -1
View File
@@ -14,7 +14,7 @@ sysName=`uname`
install_tmp=${rootPath}/tmp/mw_install.pl
mariadbDir=${serverPath}/source/mariadb
MY_VER=10.9.2
MY_VER=10.9.5
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
+6 -6
View File
@@ -228,11 +228,11 @@ label.checkbox_label span {
</div>
<div class="mtb20">
<span class="panel_setp_span">接收数据的面板地址</span>
<input type="text" class="bt-input-text" name="psync_url" value="" placeholder="接收数据面板地址,如: http://127.0.0.1:8888">
<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="psync_token" value="" placeholder="接收数据面板API密钥" />
<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">
@@ -243,8 +243,7 @@ label.checkbox_label span {
<button class="btn btn-success infoNext">下一步</button>
</div>
</div>
<div class="pa15 psync_path" style="margin:0 50px">
</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">
@@ -253,7 +252,7 @@ label.checkbox_label span {
<input type="checkbox" id="sites_All" checked>
<span>网站</span>
</label>
<ul></ul>
<ul id="sites_li"></ul>
</div>
</div>
<div class="checkbox_data">
@@ -262,7 +261,7 @@ label.checkbox_label span {
<input type="checkbox" id="db_All" checked>
<span>数据库</span>
</label>
<ul></ul>
<ul id="db_li"></ul>
</div>
</div>
</div>
@@ -286,6 +285,7 @@ label.checkbox_label span {
<script type="text/javascript">
resetPluginWinWidth(700);
resetPluginWinWidth(700);
$.getScript( "/plugins/file?name=migration_api&f=js/app.js", function(){
initStep();
});
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -1,6 +1,6 @@
{
"sort": 7,
"ps": "[<span style='color:red;'>潜龙勿用</span>]一键迁移,仅网站数据和MySQL数据",
"ps": "[<span style='color:red;'>潜龙勿用</span>]一键迁移,仅网站数据和MySQL数据(仅支持源码安装软件)",
"name": "migration_api",
"title": "一键迁移API",
"shell": "install.sh",
+246 -22
View File
@@ -1,4 +1,24 @@
function maPost(method,args,callback){
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));
@@ -6,7 +26,7 @@ function maPost(method,args,callback){
_args = JSON.stringify(args);
}
var loadT = layer.msg('正在获取...', { icon: 16, time: 0, shade: 0.3 });
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){
@@ -30,6 +50,7 @@ function maAsyncPost(method,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 });
@@ -58,43 +79,246 @@ function maPostCallbak(method, args, callback){
}
function initStep1(){
maPost('step_one',{}, function(rdata){
console.log(rdata);
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_one',{}, function(rdata){
console.log(rdata);
});
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_one',{}, function(rdata){
console.log(rdata);
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(){
maPost('step_one',{}, function(rdata){
console.log(rdata);
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(){
console.log($('.infoNext'));
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(){
var step = $('.step_head .active span').text();
// console.log(step);
// initStep1();
switch(step){
case '1':initStep1();break;
case '2':initStep2();break;
case '3':initStep3();break;
case '4':initStep4();break;
}
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);
});
}
+1
View File
@@ -65,6 +65,7 @@ binlog-ignore-db = performance_schema
#slave
log-slave-updates
#replicate-do-db
slave_skip_errors=1062
replicate-ignore-db = information_schema
replicate-ignore-db = performance_schema
replicate-ignore-db = mysql
+1
View File
@@ -62,6 +62,7 @@ binlog-ignore-db = performance_schema
#slave
log_replica_updates
#replicate-do-db
slave_skip_errors=1062
replicate-ignore-db = information_schema
replicate-ignore-db = performance_schema
replicate-ignore-db = mysql
+2 -1
View File
@@ -2474,7 +2474,8 @@ def doFullSyncUser(version=''):
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 + " " + sync_db + " > " + bak_file
port + " -u" + user + " -p" + apass + \
" --ssl-mode=DISABLED " + sync_db + " > " + bak_file
mw.execShell(dump_sql_data)
writeDbSyncStatus({'code': 2, 'msg': '本地导入数据...', 'progress': 40})
+1 -1
View File
@@ -1208,7 +1208,7 @@ function myLogs(){
<span class="f14 c6 mr20">二进制日志 </span><span class="f14 c6 mr20">' + toSize(rdata.msg) + '</span>\
'+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>\
<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>';
$(".soft-man-con").html(limitCon);
+1
View File
@@ -66,6 +66,7 @@ binlog-ignore-db = performance_schema
#slave
log-slave-updates
#replicate-do-db
slave_skip_errors=1062
replicate-ignore-db = information_schema
replicate-ignore-db = performance_schema
replicate-ignore-db = mysql
+1
View File
@@ -62,6 +62,7 @@ binlog-ignore-db = performance_schema
#slave
log_replica_updates
#replicate-do-db
slave_skip_errors=1062
replicate-ignore-db = information_schema
replicate-ignore-db = performance_schema
replicate-ignore-db = mysql
+2 -1
View File
@@ -2434,7 +2434,8 @@ def doFullSyncUser(version=''):
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 + " " + sync_db + " > " + bak_file
port + " -u" + user + " -p" + apass + \
" --ssl-mode=DISABLED " + sync_db + " > " + bak_file
mw.execShell(dump_sql_data)
writeDbSyncStatus({'code': 2, 'msg': '本地导入数据...', 'progress': 40})
+1 -1
View File
@@ -1208,7 +1208,7 @@ function myLogs(){
<span class="f14 c6 mr20">二进制日志 </span><span class="f14 c6 mr20">' + toSize(rdata.msg) + '</span>\
'+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>\
<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>';
$(".soft-man-con").html(limitCon);
+1
View File
@@ -70,6 +70,7 @@ log-slave-updates = 1
# Prevent replication from starting automatically with MySQL
#skip-slave-start = 1
#replicate-do-db
slave_skip_errors=1062
replicate-ignore-db = information_schema
replicate-ignore-db = performance_schema
replicate-ignore-db = mysql
+1
View File
@@ -72,6 +72,7 @@ log-slave-updates = 1
# Prevent replication from starting automatically with MySQL
#skip-slave-start = 1
#replicate-do-db
slave_skip_errors=1062
replicate-ignore-db = information_schema
replicate-ignore-db = performance_schema
replicate-ignore-db = mysql
+1
View File
@@ -73,6 +73,7 @@ binlog-ignore-db = performance_schema
# Prevent replication from starting automatically with MySQL
#skip-slave-start = 1
#replicate-do-db
slave_skip_errors=1062
replicate-ignore-db = information_schema
replicate-ignore-db = performance_schema
replicate-ignore-db = mysql
+1
View File
@@ -71,6 +71,7 @@ log_replica_updates = 1
# Prevent replication from starting automatically with MySQL
#skip_replica_start = 1
#replicate-do-db
replica_skip_errors=1062
replicate-ignore-db = information_schema
replicate-ignore-db = performance_schema
replicate-ignore-db = mysql
+1 -1
View File
@@ -16,7 +16,7 @@
<p onclick="masterOrSlaveConf($('.plugin_version').attr('version'))">主从配置</p>
</div>
<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>
+68 -13
View File
@@ -48,19 +48,20 @@ def getInitDFile():
def getArgs():
args = sys.argv[2:]
tmp = {}
args_len = len(args)
if args_len == 1:
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]
elif args_len > 1:
for i in range(len(args)):
t = args[i].split(':')
t = args[i].split(':', 1)
tmp[t[0]] = t[1]
return tmp
@@ -842,12 +843,29 @@ def setDbBackup():
return data[1]
scDir = mw.getRunDir() + '/scripts/backup.py'
cmd = 'python3 ' + scDir + ' database ' + args['name'] + ' 3'
os.system(cmd)
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():
args = getArgs()
data = checkArgs(args, ['file', 'name'])
@@ -902,11 +920,16 @@ def importDbExternal():
pwd = pSqliteDb('config').where('id=?', (1,)).getField('mysql_root')
sock = getSocketFile()
os.environ["MYSQL_PWD"] = pwd
mysql_cmd = getServerDir() + '/bin/mysql -S ' + sock + ' -uroot -p' + \
pwd + ' ' + name + ' < ' + import_sql
my_cnf = getConf()
myPass(True, pwd)
mysql_cmd = getServerDir() + '/bin/mysql --defaults-file=' + my_cnf + \
' -uroot -p\"' + pwd + '\" -f ' + name + ' < ' + import_sql
# print(mysql_cmd)
rdata = mw.execShell(mysql_cmd)
myPass(False, pwd)
# print(rdata)
if ext != 'sql':
os.remove(import_sql)
@@ -2371,6 +2394,25 @@ def initSlaveStatus(version=''):
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()
@@ -2393,8 +2435,19 @@ def initSlaveStatusSyncUser(version=''):
if local_mode != mode_name:
return mw.returnJson(False, '同步模式不一致!')
t = db.query(u['cmd'])
# print(t)
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, '初始化成功!')
@@ -2612,8 +2665,10 @@ def doFullSyncUser(version=''):
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 + " " + sync_db + " > " + 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})
+1 -1
View File
@@ -1208,7 +1208,7 @@ function myLogs(){
<span class="f14 c6 mr20">二进制日志 </span><span class="f14 c6 mr20">' + toSize(rdata.msg) + '</span>\
'+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>\
<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>';
$(".soft-man-con").html(limitCon);
+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防火墙",
"tip":"soft",
"name":"op_waf",
@@ -12,5 +24,5 @@
"home":"https://github.com/loveshell/ngx_lua_waf",
"date":"2019-04-21",
"pid": "1",
"versions": ["0.2.5"]
"versions": ["0.2.6"]
}
+10 -1
View File
@@ -344,13 +344,22 @@ local function waf_cc_increase()
local make_uri = "/"..make_uri_str
if params['uri_request_args']['token'] then
ngx.header.content_type = "application/json"
local args_token = params['uri_request_args']['token']
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")
ngx.say(json.encode(data))
ngx.exit(200)
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
local cc_html = ngx.re.gsub(cc_safe_js_html, "{uri}", make_uri_str)
+2 -2
View File
@@ -204,8 +204,8 @@ def initDreplace():
mw.writeFile(file_bin, content)
mw.execShell('chmod +x ' + file_bin)
# config replace
confReplace()
# config replace
confReplace()
# systemd
# /usr/lib/systemd/system
+12 -1
View File
@@ -59,10 +59,21 @@ Install_openresty()
mkdir -p ${openrestyDir}
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
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
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
+1
View File
@@ -17,6 +17,7 @@
</div>
<script type="text/javascript">
resetPluginWinHeight(500);
$.getScript( "/plugins/file?name=phpmyadmin&f=js/phpmyadmin.js", function(){
pluginService('phpmyadmin');
});
+3 -2
View File
@@ -47,7 +47,8 @@ Install_phpmyadmin()
mkdir -p ${serverPath}/phpmyadmin
mkdir -p ${serverPath}/source/phpmyadmin
echo "${1}" > ${serverPath}/phpmyadmin/version.pl
VER=$1
FDIR=phpMyAdmin-${VER}-all-languages
@@ -67,7 +68,7 @@ Install_phpmyadmin()
cd $serverPath/phpmyadmin/ && mv $FDIR phpmyadmin
echo "${1}" > ${serverPath}/phpmyadmin/version.pl
echo '安装完成' > $install_tmp
cd ${rootPath} && python3 ${rootPath}/plugins/phpmyadmin/index.py start
-1
View File
@@ -26,7 +26,6 @@ AutoRename no
AnonymousCantUpload no
MaxDiskUsage 99
CustomerProof yes
PassivePortRange 48000 50000
PIDFile {$SERVER_PATH}/pureftp/etc/pure-ftpd.pid
PureDB {$SERVER_PATH}/pureftp/etc/pureftpd.pdb
+5
View File
@@ -9,6 +9,11 @@ serverPath=$(dirname "$rootPath")
install_tmp=${rootPath}/tmp/mw_install.pl
if [ -f ${rootPath}/bin/activate ];then
source ${rootPath}/bin/activate
fi
sysName=`uname`
echo "use system: ${sysName}"
+4 -3
View File
@@ -214,7 +214,8 @@ def initdStatus():
if mw.isAppleSystem():
return "Apple Computer does not support"
shell_cmd = 'systemctl status redis | grep loaded | grep "enabled;"'
shell_cmd = 'systemctl status ' + \
getPluginName() + ' | grep loaded | grep "enabled;"'
data = mw.execShell(shell_cmd)
if data[0] == '':
return 'fail'
@@ -225,7 +226,7 @@ def initdInstall():
if mw.isAppleSystem():
return "Apple Computer does not support"
mw.execShell('systemctl enable redis')
mw.execShell('systemctl enable ' + getPluginName())
return 'ok'
@@ -233,7 +234,7 @@ def initdUinstall():
if mw.isAppleSystem():
return "Apple Computer does not support"
mw.execShell('systemctl disable redis')
mw.execShell('systemctl disable ' + getPluginName())
return 'ok'
+2 -13
View File
@@ -1,14 +1,3 @@
function str2Obj(str){
var data = {};
kv = str.split('&');
for(i in kv){
v = kv[i].split('=');
data[v[0]] = v[1];
}
return data;
}
function redisPost(method, version, args,callback){
var loadT = layer.msg('正在获取...', { icon: 16, time: 0, shade: 0.3 });
@@ -18,7 +7,7 @@ function redisPost(method, version, args,callback){
req_data['version'] = version;
if (typeof(args) == 'string'){
req_data['args'] = JSON.stringify(str2Obj(args));
req_data['args'] = JSON.stringify(toArrayObject(args));
} else {
req_data['args'] = JSON.stringify(args);
}
@@ -46,7 +35,7 @@ function redisPostCallbak(method, version, args,callback){
args['version'] = version;
if (typeof(args) == 'string'){
req_data['args'] = JSON.stringify(str2Obj(args));
req_data['args'] = JSON.stringify(toArrayObject(args));
} else {
req_data['args'] = JSON.stringify(args);
}
+4
View File
@@ -16,6 +16,10 @@ bash ${rootPath}/scripts/getos.sh
OSNAME=`cat ${rootPath}/data/osname.pl`
if [ -f ${rootPath}/bin/activate ];then
source ${rootPath}/bin/activate
fi
if id www &> /dev/null ;then
echo "www uid is `id -u www`"
echo "www shell is `grep "^www:" /etc/passwd |cut -d':' -f7 `"
+127
View File
@@ -0,0 +1,127 @@
#
# Discuz Sphinx Config File
#
indexer
{
mem_limit = 32M
write_buffer = 64M
}
searchd
{
listen = 9312
listen = 9306:mysql41
log = {$SERVER_APP}/index/searchd.log
query_log = {$SERVER_APP}/index/query.log
read_timeout = 5
max_children = 0
pid_file = {$SERVER_APP}/index/searchd.pid
seamless_rotate = 1
preopen_indexes = 1
unlink_old = 1
#workers = threads # for RT to work
binlog_path = {$SERVER_APP}/index/binlog
}
source pre_forum_thread
{
type = mysql
sql_host = 127.0.0.1
sql_user = bbs
sql_pass = bbs
sql_db = bbs
sql_port = 3306
sql_query_pre = SET NAMES UTF8
sql_query_pre = SET SESSION query_cache_type=OFF
sql_query_pre = REPLACE INTO bbs_common_sphinxcounter SELECT 1, MAX(tid) FROM bbs_forum_thread
sql_query = SELECT t.tid as id,t.tid,t.subject,t.digest,t.displayorder,t.authorid,t.lastpost,t.special \
FROM bbs_forum_thread AS t WHERE t.tid>=$start AND t.tid<=$end
sql_query_range = SELECT (SELECT MIN(tid) FROM bbs_forum_thread),maxid FROM bbs_common_sphinxcounter WHERE indexid=1
sql_range_step = 5000
sql_attr_uint = tid
sql_attr_uint = digest
sql_attr_uint = displayorder
sql_attr_uint = authorid
sql_attr_uint = special
sql_attr_timestamp =lastpost
}
index pre_forum_thread
{
source = pre_forum_thread
path = {$SERVER_APP}/index/db/pre_forum_thread/index
min_word_len = 2
html_strip = 1
ngram_len = 1
ngram_chars = U+3000..U+2FA1F
}
# threads_minute
source pre_forum_thread_minute : pre_forum_thread
{
sql_query_pre = SET NAMES UTF8
sql_query_pre = SET SESSION query_cache_type=OFF
sql_query_range = SELECT maxid-1,(SELECT MAX(tid) FROM bbs_forum_thread) FROM bbs_common_sphinxcounter WHERE indexid=1
}
# threads_minute
index pre_forum_thread_minute : pre_forum_thread
{
source = pre_forum_thread_minute
path = {$SERVER_APP}/index/db/pre_forum_thread/pre_forum_thread_minute
}
#posts
source pre_forum_post : pre_forum_thread
{
type = mysql
sql_query_pre =
sql_query_pre = SET NAMES UTF8
sql_query_pre = SET SESSION query_cache_type=OFF
sql_query_pre = REPLACE INTO bbs_common_sphinxcounter SELECT 2, MAX(pid) FROM bbs_forum_post
sql_query = SELECT p.pid AS id,p.tid,p.subject,p.message,t.digest,t.displayorder,t.authorid,t.lastpost,t.special \
FROM bbs_forum_post AS p LEFT JOIN bbs_forum_thread AS t USING(tid) where p.pid >=$start and p.pid <=$end \
AND p.first=1
sql_query_range = SELECT (SELECT MIN(pid) FROM bbs_forum_post),maxid FROM bbs_common_sphinxcounter WHERE indexid=2
sql_range_step = 5000
sql_attr_uint = tid
sql_attr_uint = digest
sql_attr_uint= displayorder
sql_attr_uint = authorid
sql_attr_uint = special
sql_attr_timestamp = lastpost
}
#posts
index pre_forum_post
{
source = pre_forum_post
path = {$SERVER_APP}/index/db/pre_forum_thread/pre_forum_post
min_word_len = 2
html_strip = 1
ngram_len = 1
ngram_chars = U+3000..U+2FA1F
}
#pre_forum_post_minute
source pre_forum_post_minute : pre_forum_post
{
sql_query_pre = SET NAMES UTF8
sql_query_pre = SET SESSION query_cache_type=OFF
sql_query_range = SELECT maxid-1,(SELECT MAX(pid) FROM bbs_forum_post) FROM bbs_common_sphinxcounter WHERE indexid=2
}
#pre_forum_post_minute
index pre_forum_post_minute : pre_forum_post
{
source = pre_forum_thread
path = {$SERVER_APP}/index/db/pre_forum_thread/pre_forum_post_minute
}
+1 -1
View File
@@ -23,7 +23,7 @@
.code span {display: block;margin-left: 15px;margin-bottom: 0;}
</style>
<script type="text/javascript">
resetPluginWinWidth(800);
resetPluginWinWidth(850);
$.getScript( "/plugins/file?name=supervisor&f=js/supervisor.js", function(){
pluginService('supervisor');
});
+28 -3
View File
@@ -220,7 +220,8 @@ def getSupList():
infos = fr.readlines()
for line in infos:
if "command=" in line.strip():
d["command"] = line.strip().split('=')[1]
# d["command"] = line.strip().split('=')[1]
d["command"] = "子配置查看"
if "user=" in line.strip():
d["user"] = line.strip().split('=')[1]
if "priority=" in line.strip():
@@ -329,6 +330,27 @@ def startJob():
return mw.returnJson(True, action + '[' + name + ']成功!')
def restartJob():
args = getArgs()
data = checkArgs(args, ['name', 'status'])
if not data[0]:
return data[1]
supCtl = 'supervisorctl -c ' + getServerDir() + "/supervisor.conf"
name = args['name']
status = args['status']
cmd = supCtl + " stop " + name
data = mw.execShell(cmd)
cmd = supCtl + " start " + name
data = mw.execShell(cmd)
if data[1] != '':
return mw.returnJson(False, '[' + name + ']重启失败!')
return mw.returnJson(True, '[' + name + ']重启成功!')
def delJob():
args = getArgs()
data = checkArgs(args, ['name'])
@@ -440,8 +462,9 @@ def configTpl():
pathFile = os.listdir(path)
tmp = []
for one in pathFile:
file = path + '/' + one
tmp.append(file)
if one.endswith(".ini"):
file = path + '/' + one
tmp.append(file)
return mw.getJson(tmp)
@@ -567,6 +590,8 @@ if __name__ == "__main__":
print(addJob())
elif func == 'start_job':
print(startJob())
elif func == 'restart_job':
print(restartJob())
elif func == 'del_job':
print(delJob())
elif func == 'update_job':
+11
View File
@@ -70,6 +70,7 @@ function supList(page, search){
list += '<td style="text-align:right">\
<a href="javascript:;" class="btlink" onclick="startOrStop(\''+rdata.data[i]['program']+'\',\''+sup_status+'\')" title="启动|停止">'+sup_status_desc+'</a> | ' +
'<a href="javascript:;" class="btlink" onclick="restartJob(\''+rdata.data[i]['program']+'\',\''+sup_status+'\')" title="重启">重启</a> | ' +
'<a href="javascript:;" class="btlink" onclick="updateJob(\''+rdata.data[i]['program']+'\')">修改</a> | ' +
'<a href="javascript:;" class="btlink" onclick="delJob(\''+rdata.data[i]['program']+'\')" title="删除">删除</a>' +
'</td>';
@@ -128,6 +129,16 @@ function startOrStop(name,status){
});
}
function restartJob(name,status){
myPost('restart_job',{'name':name,'status':status}, function(data){
var rdata = $.parseJSON(data.data);
layer.msg(rdata.msg,{icon:rdata.status?1:2});
setTimeout(function(){
supList(1,10);
},2000);
});
}
function updateJob(name){
myPost('get_job_info',{'name':name},function(data){
var rdata = $.parseJSON(data.data);
+162
View File
@@ -0,0 +1,162 @@
# ---> Python
# Byte-compiled / optimized / DLL files
__pycache__/
*.py[cod]
*$py.class
# C extensions
*.so
# Distribution / packaging
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
var/
wheels/
share/python-wheels/
*.egg-info/
.installed.cfg
*.egg
MANIFEST
# PyInstaller
# Usually these files are written by a python script from a template
# before PyInstaller builds the exe, so as to inject date/other infos into it.
*.manifest
*.spec
# Installer logs
pip-log.txt
pip-delete-this-directory.txt
# Unit test / coverage reports
htmlcov/
.tox/
.nox/
.coverage
.coverage.*
.cache
nosetests.xml
coverage.xml
*.cover
*.py,cover
.hypothesis/
.pytest_cache/
cover/
# Translations
*.mo
*.pot
# Django stuff:
*.log
local_settings.py
db.sqlite3
db.sqlite3-journal
# Flask stuff:
instance/
.webassets-cache
# Scrapy stuff:
.scrapy
# Sphinx documentation
docs/_build/
# PyBuilder
.pybuilder/
target/
# Jupyter Notebook
.ipynb_checkpoints
# IPython
profile_default/
ipython_config.py
# pyenv
# For a library or package, you might want to ignore these files since the code is
# intended to run in multiple environments; otherwise, check them in:
# .python-version
# pipenv
# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
# However, in case of collaboration, if having platform-specific dependencies or dependencies
# having no cross-platform support, pipenv may install dependencies that don't work, or not
# install all needed dependencies.
#Pipfile.lock
# poetry
# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
# This is especially recommended for binary packages to ensure reproducibility, and is more
# commonly ignored for libraries.
# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
#poetry.lock
# pdm
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
#pdm.lock
# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
# in version control.
# https://pdm.fming.dev/#use-with-ide
.pdm.toml
# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
__pypackages__/
# Celery stuff
celerybeat-schedule
celerybeat.pid
# SageMath parsed files
*.sage.py
# Environments
.env
.venv
env/
venv/
ENV/
env.bak/
venv.bak/
# Spyder project settings
.spyderproject
.spyproject
# Rope project settings
.ropeproject
# mkdocs documentation
/site
# mypy
.mypy_cache/
.dmypy.json
dmypy.json
# Pyre type checker
.pyre/
# pytype static type analyzer
.pytype/
# Cython debug symbols
cython_debug/
# PyCharm
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
# and can be added to the global gitignore or merged into this file. For a more nuclear
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
#.idea/
+73
View File
@@ -0,0 +1,73 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files.
"Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions:
(a) You must give any other recipients of the Work or Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License.
You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
+19
View File
@@ -0,0 +1,19 @@
# plugins_system_safe
系统加固插件
# DEBUG
```
https://code_hosting_007.midoks.me/midoks/plugins_system_safe
ln -s /www/git/midoks/plugins_system_safe /www/server/mdserver-web/plugins/system_safe
ln -s /www/wwwroot/midoks/plugins_system_safe /www/server/mdserver-web/plugins/system_safe
python3 /www/server/mdserver-web/plugins/system_safe/index.py bg_start
systemctl daemon-reload
```
+958
View File
@@ -0,0 +1,958 @@
{
"open": false,
"service": {
"open": true,
"name": "服务加固",
"ps": "保护系统服务,开启后将无法添加和删除服务,部分软件无法安装!",
"paths": [
{
"path": "/etc/rc.d",
"chattr": "i",
"s_mode": 509,
"d_mode": 509
},
{
"path": "/etc/rc.d/init.d",
"chattr": "i",
"s_mode": 493,
"d_mode": 493
},
{
"path": "/etc/rc.d/rc0.d",
"chattr": "i",
"s_mode": 493,
"d_mode": 493
},
{
"path": "/etc/rc.d/rc1.d",
"chattr": "i",
"s_mode": 493,
"d_mode": 493
},
{
"path": "/etc/rc.d/rc2.d",
"chattr": "i",
"s_mode": 493,
"d_mode": 493
},
{
"path": "/etc/rc.d/rc3.d",
"chattr": "i",
"s_mode": 493,
"d_mode": 493
},
{
"path": "/etc/rc.d/rc4.d",
"chattr": "i",
"s_mode": 493,
"d_mode": 493
},
{
"path": "/etc/rc.d/rc5.d",
"chattr": "i",
"s_mode": 493,
"d_mode": 493
},
{
"path": "/etc/rc.d/rc6.d",
"chattr": "i",
"s_mode": 493,
"d_mode": 493
},
{
"path": "/etc/rc.d/rc.local",
"chattr": "i",
"s_mode": 493,
"d_mode": 493
},
{
"path": "/etc/init.d",
"chattr": "i",
"s_mode": 493,
"d_mode": 493
},
{
"path": "/etc/rc0.d",
"chattr": "i",
"s_mode": 493,
"d_mode": 493
},
{
"path": "/etc/rc1.d",
"chattr": "i",
"s_mode": 493,
"d_mode": 493
},
{
"path": "/etc/rc2.d",
"chattr": "i",
"s_mode": 493,
"d_mode": 493
},
{
"path": "/etc/rc3.d",
"chattr": "i",
"s_mode": 493,
"d_mode": 493
},
{
"path": "/etc/rc4.d",
"chattr": "i",
"s_mode": 493,
"d_mode": 493
},
{
"path": "/etc/rc5.d",
"chattr": "i",
"s_mode": 493,
"d_mode": 493
},
{
"path": "/etc/rc6.d",
"chattr": "i",
"s_mode": 493,
"d_mode": 493
},
{
"path": "/etc/rcS.d",
"chattr": "i",
"s_mode": 493,
"d_mode": 493
},
{
"path": "/etc/systemd",
"chattr": "i",
"s_mode": 493,
"d_mode": 493
},
{
"path": "/lib/systemd",
"chattr": "i",
"s_mode": 493,
"d_mode": 493
}
]
},
"home": {
"open": true,
"name": "环境变量加固",
"ps": "保护用户环境变量不被修改,开启后无法自定义用户环境变量!",
"paths": [
{
"path": "/root/.bash_history",
"chattr": "a",
"s_mode": 420,
"d_mode": 420
},
{
"path": "/root/.bashrc",
"chattr": "i",
"s_mode": 420,
"d_mode": 420
},
{
"path": "/root/.bash_logout",
"chattr": "i",
"s_mode": 420,
"d_mode": 420
},
{
"path": "/root/.bash_profile",
"chattr": "i",
"s_mode": 420,
"d_mode": 420
},
{
"path": "/root/.profile",
"chattr": "i",
"s_mode": 420,
"d_mode": 420
},
{
"path": "/etc/profile",
"chattr": "i",
"s_mode": 420,
"d_mode": 420
},
{
"path": "/etc/bash.bashrc",
"chattr": "i",
"s_mode": 420,
"d_mode": 420
},
{
"path": "/etc/sysctl.conf",
"chattr": "i",
"s_mode": 420,
"d_mode": 420
},
{
"path": "/etc/sysctl.d",
"chattr": "i",
"s_mode": 420,
"d_mode": 420
}
]
},
"user": {
"open": true,
"name": "用户加固",
"ps": "保护用户,开启后将无法添加删除用户和修改用户密码!",
"paths": [
{
"path": "/etc/passwd",
"chattr": "i",
"s_mode": 420,
"d_mode": 420
},
{
"path": "/etc/group",
"chattr": "i",
"s_mode": 420,
"d_mode": 420
},
{
"path": "/usr/sbin/groupadd",
"chattr": "i",
"s_mode": 488,
"d_mode": 420
},
{
"path": "/usr/sbin/useradd",
"chattr": "i",
"s_mode": 488,
"d_mode": 420
},
{
"path": "/usr/bin/passwd",
"chattr": "i",
"s_mode": 509,
"d_mode": 420
}
]
},
"bin": {
"open": true,
"name": "关键目录加固",
"ps": "保护系统关键文件不被修改替换!",
"paths": [
{
"path": "/usr/bin",
"chattr": "i",
"s_mode": 365,
"d_mode": 365
},
{
"path": "/usr/sbin",
"chattr": "i",
"s_mode": 365,
"d_mode": 365
},
{
"path": "/etc/bashrc",
"chattr": "i",
"s_mode": 420,
"d_mode": 420
},
{
"path": "/usr/local/bin",
"chattr": "i",
"s_mode": 365,
"d_mode": 365
},
{
"path": "/usr/local/sbin",
"chattr": "i",
"s_mode": 365,
"d_mode": 365
},
{
"path": "/sbin",
"chattr": "i",
"s_mode": 365,
"d_mode": 365
},
{
"path": "/bin",
"chattr": "i",
"s_mode": 365,
"d_mode": 365
}
]
},
"cron": {
"open": true,
"name": "计划任务加固",
"ps": "保护计划任务不被篡改,开启后无法添加删除计划任务!",
"paths": [
{
"path": "/usr/bin/crontab",
"chattr": "i",
"s_mode": 509,
"d_mode": 420
},
{
"path": "/etc/crontab",
"chattr": "i",
"s_mode": 420,
"d_mode": 420
},
{
"path": "/etc/cron.d",
"chattr": "i",
"s_mode": 509,
"d_mode": 509
},
{
"path": "/etc/cron.daily",
"chattr": "i",
"s_mode": 509,
"d_mode": 420
},
{
"path": "/etc/cron.hourly",
"chattr": "i",
"s_mode": 509,
"d_mode": 420
},
{
"path": "/etc/cron.monthly",
"chattr": "i",
"s_mode": 509,
"d_mode": 420
},
{
"path": "/etc/cron.weekly",
"chattr": "i",
"s_mode": 509,
"d_mode": 420
},
{
"path": "/etc/anacrontab",
"chattr": "i",
"s_mode": 384,
"d_mode": 384
},
{
"path": "/var/spool/cron",
"chattr": "i",
"s_mode": 448,
"d_mode": 448
},
{
"path": "/var/spool/cron/root",
"chattr": "i",
"s_mode": 384,
"d_mode": 384
},
{
"path": "/var/spool/anacron",
"chattr": "i",
"s_mode": 509,
"d_mode": 509
}
]
},
"ssh": {
"open": true,
"name": "SSH服务加固",
"ps": "保护SSH不被暴力破解,记录用户登录日志",
"cycle": 120,
"limit": 3600,
"limit_count": 3
},
"process": {
"open": true,
"name": "异常进程监控",
"ps": "监控服务器进程列表,发现异常的进程后立即结束",
"process_white": [
"pip",
"pip3",
"yum",
"apt-get",
"apt",
"redis-cli",
"memcached",
"sshd",
"vm",
"vim",
"htop",
"top",
"sh",
"bash",
"zip",
"gzip",
"rsync",
"tar",
"unzip",
"rar",
"unrar",
"php",
"composer",
"pkill",
"mongo",
"mongod",
"php-fpm",
"php-fpm5.6",
"php-fpm7.0",
"php-fpm7.1",
"php-fpm7.2",
"php-fpm7.3",
"php-fpm7.4",
"php-fpm8.1",
"php-fpm8.2",
"nginx",
"httpd",
"lsof",
"ps",
"vi",
"vim",
"redis-server",
"mysqld",
"mysqld_safe",
"mysql",
"pure-ftpd",
"sparse_dd",
"stunnel",
"squeezed",
"vncterm",
"awk",
"ruby",
"postgres",
"mpathalert",
"vncterm",
"multipathd",
"fe",
"elasticsyslog",
"syslogd",
"v6d",
"xapi",
"screen",
"runsvdir",
"svlogd",
"java",
"udevd",
"ntpd",
"irqbalance",
"qmgr",
"wpa_supplicant",
"mysqld_safe",
"sftp-server",
"lvmetad",
"gitlab-web",
"pure-ftpd",
"auditd",
"master",
"dbus-daemon",
"tapdisk",
"init",
"ksoftirqd",
"kworker",
"kmpathd",
"kmpath_handlerd",
"python",
"kdmflush",
"bioset",
"crond",
"kthreadd",
"migration",
"rcu_sched",
"kjournald",
"gcc",
"gcc++",
"nginx",
"mysqld",
"php-cgi",
"login",
"firewalld",
"iptables",
"systemd",
"network",
"dhclient",
"systemd-journald",
"chrony",
"chronyd",
"chronyc",
"NetworkManager",
"systemd-logind",
"systemd-udevd",
"polkitd",
"tuned",
"rsyslogd",
"AliYunDunUpdate",
"AliYunDun",
"du",
"sendmail",
"gunicorn",
"python2",
"python3",
"python34",
"python2.6",
"runsv",
"dd",
"mkfs",
"fdisk",
"systemd-resolve",
"rpm",
"cc1",
"cc1plus",
"as",
"acme.sh",
"cc",
"du",
"grep",
"awk",
"sed",
"cut",
"free",
"df",
"firewall-cmd",
"ufw",
"echo",
"ls",
"cp",
"mv",
"rm",
"diff",
"ln",
"cat",
"more",
"wtmp",
"date",
"e2fsck",
"gunzip",
"ifconfig",
"ip",
"route",
"ping",
"scp",
"telnet",
"cd",
"comm",
"touch",
"man",
"w",
"who",
"last",
"clock",
"uname",
"su",
"uptime",
"vmstat",
"mount",
"umount",
"mkdir",
"mkswap",
"swapon",
"e2fsck",
"tune2fs",
"groupadd",
"useradd",
"passwd",
"userdel",
"chown",
"chmod",
"chgrp",
"id",
"mail",
"gzip",
"bzip2",
"bunzip2",
"networking",
"ssh",
"find",
"locate",
"slocate",
"dir",
"vdir",
"pwd",
"rename",
"rmdir",
"updatedb",
"whereis",
"which",
"compress",
"ar",
"arj",
"gzexe",
"bzip2",
"cksum",
"cmp",
"col",
"colrm",
"csplit",
"diff3",
"emacs",
"join",
"head",
"sort",
"wc",
"uniq",
"tail",
"sum",
"ulimit",
"pkill",
"kill",
"killall",
"pidof",
"pstree",
"watch",
"pgrep",
"dumpe2fs",
"mke2fs",
"sync",
"lsyncd",
"tune2fs",
"basename",
"file",
"locate/slocate",
"ls/dir/vdir",
"bzcat",
"bzip2recover",
"bzless/bzmore",
"cpio",
"dump",
"lha",
"resotre",
"unarj",
"uncompress",
"zcat",
"zforce",
"zipinfo",
"znew",
"diffstat",
"ed",
"ex",
"expand",
"fmt",
"fold",
"grep/egrep/fgrep",
"ispell",
"jed",
"joe",
"less",
"look",
"od",
"paste",
"pico",
"spell",
"split",
"tac",
"tee",
"tr",
"unexpand",
"alias",
"bg",
"bind",
"declare",
"dirs",
"enable",
"eval",
"exec",
"exit",
"export",
"fc",
"fg",
"hash",
"history",
"jobs",
"logout",
"popd",
"pushd",
"set",
"shopt",
"umask",
"unalias",
"unset",
"accept",
"cancel",
"disable",
"lp",
"lpadmin",
"lpc",
"lpq",
"lpr",
"lprm",
"lpstat",
"pr",
"reject",
"bc",
"cal",
"clear",
"consoletype",
"ctrlaltdel",
"dircolors",
"eject",
"halt",
"hostid",
"hwclock",
"info",
"md5sum",
"letsencrypt",
"mandb",
"mesg",
"mtools",
"mtoolstest",
"poweroff",
"reboot",
"shutdown",
"sleep",
"stat",
"talk",
"wall",
"whatis",
"whoami",
"write",
"cmsgoagent.linu",
"yes",
"chfn",
"chsh",
"finger",
"gpasswd",
"groupdel",
"groupmod",
"groups",
"grpck",
"grpconv",
"grpunconv",
"logname",
"pwck",
"pwconv",
"dd",
"mariadbd",
"pwunconv",
"usermod",
"users",
"nice",
"nohup",
"renice",
"badblocks",
"blockdev",
"chattr",
"convertquota",
"e2image",
"e2label",
"edquota",
"fail2ban-server",
"findfs",
"fsck",
"grub",
"hdparm",
"lilo",
"lsattr",
"mkbootdisk",
"mkinitrd",
"mkisofs",
"mknod",
"mktemp",
"parted",
"quota",
"quotacheck",
"xargs",
"bcm-agent",
"bcm-si",
"quotaoff",
"quotaon",
"quotastat",
"repquota",
"swapoff",
"depmod",
"dmesg",
"insmod",
"iostat",
"ipcs",
"kernelversion",
"lsmod",
"modinfo",
"modprobe",
"mpstat",
"rmmod",
"sar",
"slabtop",
"sysctl",
"tload",
"startx",
"xauth",
"xhost",
"xinit",
"xlsatoms",
"xlsclients",
"xlsfonts",
"xset",
"chroot",
"nmap",
"ntfs-3g",
"sftp",
"slogin",
"sudo",
"awk/gawk",
"expr",
"gdb",
"ldd",
"make",
"nm",
"perl",
"test",
"arch",
"at",
"atq",
"atrm",
"batch",
"chkconfig",
"crontab",
"lastb",
"logrotate",
"logsave",
"logwatch",
"lsusb",
"patch",
"runlevel",
"service",
"telinit",
"dnsdomainname",
"domainname",
"hostname",
"ifcfg",
"ifdown",
"ifup",
"stmpd",
"nisdomainname",
"ypdomainname",
"arp",
"arping",
"arpwatch",
"dig",
"elinks",
"elm",
"ftp",
"host",
"ipcalc",
"lynx",
"ncftp",
"netstat",
"nslookup",
"pine",
"dhclient-script",
"rsh",
"tftp",
"tracepath",
"traceroute",
"wget",
"arptables",
"iptables-save",
"iptables-restore",
"tcpdump",
"ab",
"apachectl",
"exportfs",
"htdigest",
"htpasswd",
"mailq",
"mysqladmin",
"msqldump",
"mysqlimport",
"mysqlshow",
"nfsstat",
"showmount",
"smbclient",
"smbmount",
"smbpasswd",
"squid",
"pickup",
"local",
"localedef",
"bounce",
"smtp",
"smtpd",
"trivial-rewrite",
"xe-daemon",
"cleanup",
"nm-dispatcher",
"lsinitrd",
"barad_agent",
"stop.sh",
"YDService",
"agetty",
"systemctl",
"AliSecureCheckA",
"containerd",
"containerd-shim",
"certbot-auto",
"oneav",
"oneavd",
"oneav_service_m",
"s3fs",
"bosfs",
"cosfs",
"flock",
"raidcheck",
"run-parts",
"man-db.cron",
"YDLive",
"sgagent"
],
"process_white_rule": [
"vif",
"node",
"pm2",
"npm",
"yarn",
"qemu",
"scsi_eh",
"xcp",
"xen",
"docker",
"yunsuo",
"scsi",
"jbd2",
"ext4",
"xfs",
"aliyun",
"kswapd",
"PM2",
"yunsuo",
"mkfs",
"Ali",
"watchdog",
"kworker",
"ksoftirqd",
"migration",
"mysql",
"/www/server/",
"cmsgoagent",
"python",
"python3",
"ifdown",
"node"
],
"process_exclude": [
"php-fpm",
"mysqld",
"mongod",
"dockerd",
"docker-containerd",
"memcached",
"jsvc",
"jsvc.exec",
"nginx",
"node",
"pm2",
"npm",
"yarn",
"httpd",
"gunicorn",
"configure",
"make",
"curl",
"wget",
"anacron",
"mysqldump",
"node",
"php",
"mysql",
"netstat",
"redis",
"postfix"
]
}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 812 B

+24
View File
@@ -0,0 +1,24 @@
<div class="bt-form">
<div class="bt-w-main">
<div class="bt-w-menu">
<p class="bgw" onclick="pluginService('system_safe');">服务</p>
<p onclick="pluginInitD('system_safe');">自启动</p>
<p onclick="ssConfigList();">防护配置</p>
<p onclick="ssLockAddress();">封锁IP</p>
<p onclick="ssLogAudit();">日志审计</p>
<p onclick="ssOpLog();">操作日志</p>
</div>
<div class="bt-w-con pd15">
<div class="soft-man-con"></div>
</div>
</div>
</div>
<script type="text/javascript" src="/plugins/file?name=system_safe&f=js/app.js"></script>
<script type="text/javascript">
resetPluginWinWidth(800);
resetPluginWinHeight(500);
$.getScript("/plugins/file?name=system_safe&f=js/app.js", function(){
pluginService('system_safe');
});
</script>
+20
View File
@@ -0,0 +1,20 @@
{
"sort": 1,
"ps": "提供灵活的系统加固功能,防止系统被植入木马,支持服务器日志审计功能",
"name": "system_safe",
"title": "系统加固",
"shell": "install.sh",
"versions":["1.0"],
"updates":["1.0"],
"tip": "soft",
"icon":"ico.png",
"checks": "server/system_safe",
"path": "server/system_safe",
"display": 1,
"author": "midoks",
"date": "2022-01-12",
"home": "",
"type": 0,
"pid": "4"
}
@@ -0,0 +1,15 @@
[Unit]
Description=system_safe server daemon
After=network.target
[Service]
Type=forking
ExecStart={$SERVER_PATH}/init.d/system_safe start
ExecStop={$SERVER_PATH}/init.d/system_safe stop
ExecReload={$SERVER_PATH}/init.d/system_safe reload
ExecRestart={$SERVER_PATH}/init.d/system_safe restart
KillMode=process
Restart=on-failure
[Install]
WantedBy=multi-user.target
@@ -0,0 +1,94 @@
#!/bin/bash
# chkconfig: 2345 55 25
# description:system_safe
### BEGIN INIT INFO
# Provides: system_safe
# Required-Start: $all
# Required-Stop: $all
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
# Short-Description: starts system_safe
# Description: starts the system_safe
### END INIT INFO
PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
mw_path={$SERVER_PATH}
rootPath=$(dirname "$mw_path")
PATH=$PATH:$mw_path/bin
if [ -f $rootPath/mdserver-web/bin/activate ];then
source $rootPath/mdserver-web/bin/activate
fi
sys_start()
{
isStart=$(ps aux |grep -E "(system_safe)"|grep -v grep|grep -v '/bin/bash'|grep -v 'system_safe/system_safe.py start' | grep -v 'system_safe/system_safe.py reload' | grep -v 'system_safe/system_safe.py restart' | grep -v systemctl | grep -v '/bin/sh' | awk '{print $2}'|xargs)
if [ "$isStart" == '' ];then
echo -e "Starting system_safe service... \c"
cd $rootPath/mdserver-web
nohup python3 plugins/system_safe/system_safe.py bg_start &> $mw_path/service.log &
sleep 0.5
isStart=$(ps aux |grep -E "(system_safe)"|grep -v grep|awk '{print $2}'|xargs)
if [ "$isStart" == '' ];then
echo -e "\033[31mfailed\033[0m"
echo '------------------------------------------------------'
cat $mw_path/service.log
echo '------------------------------------------------------'
echo -e "\033[31mError: system_safe service startup failed.\033[0m"
return;
fi
echo -e "\033[32mdone\033[0m"
else
echo "Starting system_safe service (pid $isStart) already running"
fi
}
sys_stop()
{
echo -e "Stopping system_safe service... \c";
pids=$(ps aux |grep -E "(system_safe)"|grep -v grep|grep -v '/bin/bash'|grep -v systemctl | grep -v 'system_safe/system_safe.py bg_stop'|grep -v 'system_safe/system_safe.py stop' | grep -v 'system_safe/system_safe.py reload' | grep -v 'system_safe/system_safe.py restart' |awk '{print $2}'|xargs)
arr=($pids)
for p in ${arr[@]}
do
kill -9 $p
done
cd $rootPath/mdserver-web
python3 plugins/system_safe/system_safe.py bg_stop
echo -e "\033[32mdone\033[0m"
}
sys_status()
{
isStart=$(ps aux |grep -E "(system_safe)"|grep -v grep|grep -v systemctl|awk '{print $2}'|xargs)
if [ "$isStart" != '' ];then
echo -e "\033[32msystem_safe service (pid $isStart) already running\033[0m"
else
echo -e "\033[31msystem_safe service not running\033[0m"
fi
}
case "$1" in
'start')
sys_start
;;
'stop')
sys_stop
;;
'restart')
sys_stop
sleep 0.2
sys_start
;;
'reload')
sys_stop
sleep 0.2
sys_start
;;
'status')
sys_status
;;
*)
echo "Usage: systemctl {start|stop|restart|reload} system_safe"
;;
esac
+47
View File
@@ -0,0 +1,47 @@
#!/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
SYSOS=`uname`
VERSION=$2
APP_NAME=system_safe
Install_App()
{
echo '正在安装脚本文件...' > $install_tmp
mkdir -p $serverPath/system_safe
echo "$VERSION" > $serverPath/system_safe/version.pl
echo 'install complete' > $install_tmp
#初始化
cd ${serverPath}/mdserver-web && python3 plugins/system_safe/system_safe.py start $VERSION
cd ${serverPath}/mdserver-web && python3 plugins/system_safe/system_safe.py initd_install $VERSION
}
Uninstall_App()
{
if [ -f /usr/lib/systemd/system/${APP_NAME}.service ] || [ -f /lib/systemd/system/${APP_NAME}.service ] ;then
systemctl stop ${APP_NAME}
systemctl disable ${APP_NAME}
rm -rf /usr/lib/systemd/system/${APP_NAME}.service
rm -rf /lib/systemd/system/${APP_NAME}.service
systemctl daemon-reload
fi
rm -rf $serverPath/system_safe
echo "uninstall completed" > $install_tmp
}
action=$1
if [ "${1}" == 'install' ];then
Install_App
else
Uninstall_App
fi
+606
View File
@@ -0,0 +1,606 @@
function ssPost(method,args,callback){
var _args = null;
if (typeof(args) == 'string'){
_args = JSON.stringify(toArrayObject(args));
} else {
_args = JSON.stringify(args);
}
var loadT = layer.msg('正在获取...', { icon: 16, time: 0, shade: 0.3 });
var req_data = {};
req_data['name'] = 'system_safe';
req_data['script'] = 'system_safe';
req_data['func'] = method;
req_data['args'] = _args;
$.post('/plugins/run', 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 ssAsyncPost(method,args){
var _args = null;
if (typeof(args) == 'string'){
_args = JSON.stringify(toArrayObject(args));
} else {
_args = JSON.stringify(args);
}
return syncPost('/plugins/run', {name:'system_safe', func:method, args:_args});
}
function ssPostCallbak(method, args, callback){
var loadT = layer.msg('正在获取...', { icon: 16, time: 0, shade: 0.3 });
var req_data = {};
req_data['name'] = 'system_safe';
req_data['func'] = method;
req_data['script'] = 'system_safe';
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 getSafeConfigPathList(tag){
ssPost('get_safe_data', {tag:tag}, function(rdata){
var rdata = $.parseJSON(rdata.data);
var slist = rdata.data.paths;
var body = '';
for (var i = 0; i < slist.length; i++) {
body += "<tr>";
body += "<td>"+slist[i]['path']+"</td>";
if (slist[i]['chattr'] == 'i'){
body += "<td>只读</td>";
} else if (slist[i]['chattr'] == 'a'){
body += "<td>追加</td>";
} else{
body += "<td>只读</td>";
}
if (rdata.msg['open']){
body += "<td>"+slist[i]['d_mode']+"</td>";
body += "<td><a class='btlink'>已保护</a></td>";
} else {
body += "<td>"+slist[i]['s_mode']+"</td>";
body += "<td><a style='color:red;'>未保护</a></td>";
}
body += "<td style='text-align: right;'><a class='btlink safe_path_delete' row='"+i+"'>删除</a></td>";
body += "</tr>";
}
$('#safe-file-table tbody').html(body);
$('.safe_path_delete').click(function(){
var id = $(this).attr('row');
ssPost('del_safe_path',{tag:tag,index:id}, function(rdata){
var rdata = $.parseJSON(rdata.data);
showMsg(rdata.msg, function(){
getSafeConfigPathList(tag);
},{icon:rdata.status?1:2,shade: [0.3, '#000']},2000);
});
});
});
}
function setSafeConfigPath(tag, alist){
layer.open({
type: 1,
area: ['700px','535px'],
title: '配置【'+alist['name']+'】',
content: "<form class='bt-form pd20'>\
<div style='border-bottom:#ccc 1px solid;margin-bottom:10px;padding-bottom:10px'>\
<input class='bt-input-text' name='s_path' id='s_path' type='text' value='' style='width:370px;margin-right:5px;' placeholder='被保护的文件或目录完整路径'>\
<a class='glyphicon cursor glyphicon-folder-open' onclick='changePath(\"s_path\")' style='color:#edca5c;margin-right:20px;font-size:16px'></a>\
<select class='bt-input-text' name='chattr'>\
<option value='i'>只读</option>\
<option value='a'>追加</option>\
</select>\
<input class='bt-input-text mr5' name='d_mode' type='text' style='width:120px;' placeholder='权限'>\
<button type='button' class='btn btn-success btn-sm va0 pull-right add_safe_config'>添加</button>\
</div>\
<div class='divtable'>\
<div id='safe-file-table' class='table_head_fix' style='max-height:300px;overflow:auto;border:#ddd 1px solid'>\
<table class='table table-hover' style='border:none'>\
<thead>\
<tr>\
<th width='360'>路径</th>\
<th>模式</th>\
<th>权限</th>\
<th>状态</th>\
<th style='text-align: right;'>操作</th>\
</tr>\
</thead>\
<tbody></tbody>\
</table>\
</div>\
</div>\
<ul class='help-info-text c7 ptb10' style='margin-top: 5px;'>\
<li>【只读】无法修改、创建、删除文件和目录</li>\
<li>【追加】只能追加内容,不能删除或修改原有内容</li>\
<li>【权限】设置文件或目录在受保护状态下的权限(非继承),关闭保护后权限自动还原</li>\
<li>【如何填写权限】请填写Linux权限代号,如:644、755、600、555等,如果不填写,则使用文件原来的权限</li>\
</ul>\
</form>",
success:function(){
$('.add_safe_config').click(function(){
var path = $('input[name="s_path"]').val();
var chattr = $('select[name="chattr"]').val();
var d_mode = $('input[name="d_mode"]').val();
ssPost('add_safe_path',{tag:tag,path:path,chattr:chattr,d_mode:d_mode}, function(rdata){
var rdata = $.parseJSON(rdata.data);
showMsg(rdata.msg, function(){
getSafeConfigPathList(tag);
},{icon:rdata.status?1:2,shade: [0.3, '#000']},2000);
});
});
}
});
getSafeConfigPathList(tag);
}
function setSafeConfigSsh(tag, alist){
function setSafeConfigSshData(){
ssPost('get_ssh_data', {}, function(rdata){
var rdata = $.parseJSON(rdata.data);
var info = rdata.data;
$('input[name="s_cycle"]').val(info['cycle']);
$('input[name="s_limit_count"]').val(info['limit_count']);
$('input[name="s_limit"]').val(info['limit']);
});
}
layer.open({
type: 1,
area: ['700px','210px'],
title: '配置【'+alist['name']+'】',
content: "<form class='bt-form pd20'>\
<div style='border-bottom:#ccc 1px solid;margin-bottom:10px;padding-bottom:10px'>\
在(检测周期)\
<input class='bt-input-text' min='30' max='1800' name='s_cycle' type='number' value='120' style='width:80px;margin-right:5px;'>\
秒内,登录错误(检测阈值)\
<input class='bt-input-text' min='30' max='1800' name='s_limit_count' type='number' value='120' style='width:80px;margin-right:5px;'>\
次,封锁(封锁时间)\
<input min='60' class='bt-input-text' name='s_limit' type='number' value='3600' style='width:80px;margin-right:12px;'>\
<button type='button' class='btn btn-success btn-sm va0 pull-right save_safe_ssh'>保存</button>\
</div>\
<ul class='help-info-text c7 ptb10' style='margin-top: 5px;'>\
<li>触发以上策略后,客户端IP将被封锁一段时间</li>\
<li>请在面板日志或操作日志中查看封锁记录</li>\
<li>请在面板日志或操作日志中查看SSH成功登录的记录</li>\
</ul>\
</form>",
success:function(){
setSafeConfigSshData();
$('.save_safe_ssh').click(function(){
var cycle = $('input[name="s_cycle"]').val();
var limit_count = $('input[name="s_limit_count"]').val();
var limit = $('input[name="s_limit"]').val();
ssPost('save_safe_ssh',{cycle:cycle,limit_count:limit_count,limit:limit}, function(rdata){
var rdata = $.parseJSON(rdata.data);
showMsg(rdata.msg, function(){
setSafeConfigSshData();
},{icon:rdata.status?1:2,shade: [0.3, '#000']},2000);
});
});
}
});
}
function setSafeConfigProcessList(tag){
ssPost('get_process_data', {}, function(rdata){
var rdata = $.parseJSON(rdata.data);
var slist = rdata.data.process_white;
var body = '';
for (var i = 0; i < slist.length; i++) {
body += "<tr>\
<td>"+slist[i]+"</td>\
<td style='text-align: right;'><a class='btlink safe_path_delete' row='"+i+"'>删除</a></td>\
</tr>";
}
$('#safe-file-table tbody').html(body);
$('.safe_path_delete').click(function(){
var id = $(this).attr('row');
ssPost('del_safe_proccess_name',{tag:tag,index:id}, function(rdata){
var rdata = $.parseJSON(rdata.data);
showMsg(rdata.msg, function(){
setSafeConfigProcessList(tag);
},{icon:rdata.status?1:2,shade: [0.3, '#000']},2000);
});
});
});
}
function setSafeConfigProcess(tag, alist){
layer.open({
type: 1,
area: ['700px','535px'],
title: '配置【进程白名单】',
content: "<form class='bt-form pd20'>\
<div style='border-bottom:#ccc 1px solid;margin-bottom:10px;padding-bottom:10px'>\
<input class='bt-input-text' name='s_name' id='s_name' type='text' value='' style='width:370px;margin-right:5px;' placeholder='完整的进程名'>\
<button type='button' class='btn btn-success btn-sm va0 pull-right add_process_white'>添加</button>\
</div>\
<div class='divtable'>\
<div id='safe-file-table' class='table_head_fix' style='max-height:300px;overflow:auto;border:#ddd 1px solid'>\
<table class='table table-hover' style='border:none'>\
<thead>\
<tr>\
<th width='360'>进程名称</th>\
<th style='text-align: right;'>操作</th>\
</tr>\
</thead>\
<tbody></tbody>\
</table>\
</div>\
</div>\
<ul class='help-info-text c7 ptb10' style='margin-top: 5px;'>\
<li>【进程名称】请填写完整的进程名称,如: mysqld</li>\
<li>【说明】在白名单列表中的进程将不再检测范围,建议将常用软件的进程添加进白名单</li>\
</ul>\
</form>",
success:function(){
$('.add_process_white').click(function(){
var process_name = $('input[name="s_name"]').val();
ssPost('add_process_white',{process_name:process_name}, function(rdata){
var rdata = $.parseJSON(rdata.data);
showMsg(rdata.msg, function(){
setSafeConfigProcessList(tag);
},{icon:rdata.status?1:2,shade: [0.3, '#000']},2000);
});
});
}
});
setSafeConfigProcessList(tag);
}
function setSafeConfig(tag, alist){
if ($.inArray(tag, ['bin', 'service', 'home', 'user', 'bin', 'cron'])>-1){
setSafeConfigPath(tag,alist);
}
if ($.inArray(tag, ['ssh'])>-1){
setSafeConfigSsh(tag,alist);
}
if ($.inArray(tag, ['process'])>-1){
setSafeConfigProcess(tag,alist);
}
}
//设置安全状态
function setSafeStatus(obj,tag){
var o = $(obj).prev();
var status = $(o).prop('checked');
ssPost('set_safe_status', {tag:tag,status:!status}, function(rdata){
var rdata = $.parseJSON(rdata.data);
if (!rdata.status){
layer.msg(rdata.msg,{icon:0,time:2000,shade: [0.3, '#000']});
$(o).prop('checked',status);
return;
}
layer.msg("配置成功!",{icon:1,time:2000,shade: [0.3, '#000']});
});
}
function ssConfigList(){
ssPost('conf',{},function(rdata){
var rdata = $.parseJSON(rdata.data);
var libs = rdata.data;
// console.log(libs);
var body = '';
for (var i in libs) {
if (i == 'open'){
continue;
}
var checked = '';
if (libs[i].open){
checked = 'checked';
}
body += '<tr>' +
'<td>' + libs[i].name + '</td>' +
'<td>' + libs[i].ps + '</td>' +
'<td>\
<div class="ssh-item">\
<input class="btswitch btswitch-ios" id="sys_service_'+i +'" type="checkbox" '+checked+'>\
<label class="btswitch-btn service_switch" for="sys_service_'+i+'" style="width: 2em; height: 1.1em;padding:1px;"></label>\
</div>\
</td>'+
'<td style="text-align: right;"><a id="conf_sys_service_'+i+'" class="btlink set_safe_config">配置</a></td>' +
'</tr>';
}
var con = '<div class="divtable" id="system_safe_list" style="margin-right:10px;max-height: 320px; overflow: auto; margin-right: 0px;">' +
'<table class="table table-hover" width="100%" cellspacing="0" cellpadding="0" border="0">' +
'<thead>' +
'<tr>' +
'<th>名称</th>' +
'<th>描述</th>' +
'<th width="40">状态</th>' +
'<th style="text-align: right;" width="50">操作</th>' +
'</tr>' +
'</thead>' +
'<tbody>' + body + '</tbody>' +
'</table>' +
'</div>' +
'<ul class="help-info-text c7 pull-left">\
<li>开启系统加固功能后,一些如软件安装等敏感操作将被禁止</li>\
<li>开启【SSH服务加固】之后,用户登录SSH的行为将受到监控,若连续多次登录失败,将采取封IP措施</li>\
<li>【异常进程监控】与【SSH服务加固】会占用一定服务器开销</li>\
<li><span style="color:red;">【注意】如果您需要安装软件或插件,请先将系统加固关闭!</span></li>\
</ul>';
$('.soft-man-con').html(con);
$('#system_safe_list .service_switch').click(function(){
var val = $(this).prev().attr('id');
val = val.replace('sys_service_','');
setSafeStatus(this,val);
});
$('.set_safe_config').click(function(){
var name = $(this).attr('id');
// console.log(name);
name = name.replace('conf_sys_service_','');
setSafeConfig(name, libs[name]);
});
});
}
function ssOpLogList(p){
ssPost('op_log',{p:p},function(rdata){
var rdata = $.parseJSON(rdata.data);
var plist = rdata.data.data;
var body = '';
for (var i = 0; i < plist.length; i++) {
body += "<tr>\
<td><em class='dlt-num'>" + plist[i].addtime + "</em></td>\
<td>" + plist[i].log + "</td>\
</tr>";
}
$("#system_safe_log_list tbody").html(body);
$("#system_safe_log_page").html(rdata.data.page);
});
}
function ssOpLog(){
var con = '<div class="divtable" id="system_safe_log_list" style="margin-right:10px;max-height: 380px; overflow: auto; margin-right: 0px;">\
<table class="table table-hover" width="100%" cellspacing="0" cellpadding="0" border="0">\
<thead><tr><th style="width:135px;">时间</th><th>详情</th></tr></thead>\
<tbody></tbody>\
</table>\
</div>\
<div id="system_safe_log_page" class="dataTables_paginate paging_bootstrap page"></div>';
$('.soft-man-con').html(con);
ssOpLogList(1);
}
function ssLogAuditList(){
ssPost('get_sys_logfiles',{},function(rdata){
var rdata = $.parseJSON(rdata.data);
var plist = rdata.data;
var option = '';
// console.log(plist);
for (var i = 0; i < plist.length; i++) {
option += '<option value="'+plist[i]['name']+'">'+plist[i]['name'] +' - '+plist[i]['title']+'</option>';
}
$("#system_safe_log_audit").html(option);
var log_name = $('#system_safe_log_audit option:first').val();
ssLogAuditFile(log_name);
$('#system_safe_log_audit').change(function(){
var log_name = $('#system_safe_log_audit option:selected').val();
ssLogAuditFile(log_name);
});
});
}
function ssLogAuditFileRenderString(data){
var tbody = '<textarea style="margin: 0px;width: 100%;height: 380px;background-color: #333;color:#fff; padding:0 5px" id="system_safe_log_audit_str"></textarea>';
$("#system_safe_log_audit_list").html(tbody);
var ob = document.getElementById('system_safe_log_audit_str');
ob.scrollTop = ob.scrollHeight;
$("#system_safe_log_audit_str").html(data);
}
function ssLogAuditFileRenderObject(plist){
var pre_html = '<table class="table table-hover" width="100%" cellspacing="0" cellpadding="0" border="0">\
<thead><tr><th>时间</th><th>角色</th><th>事件</th></tr></thead>\
<tbody></tbody>\
</table>';
$("#system_safe_log_audit_list").html(pre_html);
if (plist.length>0){
var tmp = plist[0];
// console.log(tmp);
var thead = '';
tbody += '<tr>'
for (var i in tmp) {
tbody+='<th>'+ i + '</th>';
}
tbody += '</tr>';
$("#system_safe_log_audit_list thead").html(tbody);
}
var tbody = '';
for (var i = 0; i < plist.length; i++) {
tbody += '<tr>';
for (var vv in plist[i]) {
tbody+= '<td>'+ plist[i][vv] + '</td>'
}
tbody += '</tr>';
}
$("#system_safe_log_audit_list tbody").html(tbody);
}
function ssLogAuditFile(log_name){
// ssPost('get_sys_log',{log_name:log_name},function(rdata){
// try{
// var rdata = $.parseJSON(rdata.data);
// if (typeof(rdata.data) == 'object'){
// if (!rdata.status){
// layer.msg(rdata.msg,{icon:0,time:2000,shade: [0.3, '#000']});
// return;
// }
// var plist = rdata.data;
// ssLogAuditFileRenderObject(plist);
// }
// }catch(e){
// if (typeof(rdata.data) == 'string'){
// ssLogAuditFileRenderString(rdata.data);
// }
// }
// });
ssPostCallbak('get_sys_log',{log_name:log_name},function(rdata){
try{
var rdata = $.parseJSON(rdata.data);
if (typeof(rdata.data) == 'object'){
if (!rdata.status){
layer.msg(rdata.msg,{icon:0,time:2000,shade: [0.3, '#000']});
return;
}
var plist = rdata.data;
ssLogAuditFileRenderObject(plist);
}
}catch(e){
if (typeof(rdata.data) == 'string'){
ssLogAuditFileRenderString(rdata.data);
}
}
});
}
function ssLogAudit(){
var con = '<select id="system_safe_log_audit" class="bt-input-text mr20" style="width:50%;margin-bottom: 3px;">\
<option value="0">请选择</option>\
</select>\
<div class="divtable" id="system_safe_log_audit_list" style="margin-right:10px;height: 380px; overflow: auto; margin-right: 0px;"></div>';
$('.soft-man-con').html(con);
ssLogAuditList();
}
function ssLockAddressList(){
ssPost('get_ssh_limit_info',{},function(rdata){
var rdata = $.parseJSON(rdata.data);
var libs = rdata.data;
var tbody = '';
for (var i in libs) {
var end_time = libs[i].end;
var time_title = '手动解封';
if (end_time != '0'){
time_title = '自动解封时间:'+end_time;
}
tbody += '<tr>' +
'<td style="width:100px;">' + libs[i].address + '</td>' +
'<td style="width:100px;">'+time_title+'</td>' +
'<td style="text-align: right;"><a ip="'+libs[i].address+'" class="btlink remove_ssh_limit">立即解封</a></td>' +
'</tr>';
}
$('#system_lock_address tbody').html(tbody);
$('#system_lock_address .remove_ssh_limit').click(function(){
var address = $(this).attr('ip');
ssPost('remove_ssh_limit', {ip:address}, function(rdata){
var rdata = $.parseJSON(rdata.data);
showMsg(rdata.msg, function(){
ssLockAddressList();
},{icon:rdata.status?1:2,shade: [0.3, '#000']},2000);
});
});
});
}
function ssLockAddress(){
var con = '<div style="margin-bottom:10px;">\
<input class="bt-input-text" name="s_address" type="text" value="" style="width:250px;margin-right:12px;" placeholder="IP地址">\
<button class="btn btn-success btn-sm va0 add_lock_address">添加</button>\
</div>\
<div class="divtable" id="system_lock_address" style="margin-right:10px;max-height: 320px; overflow: auto; margin-right: 0px;">' +
'<table class="table table-hover" width="100%" cellspacing="0" cellpadding="0" border="0">' +
'<thead>' +
'<tr>' +
'<th>IP</th>' +
'<th>解封时间</th>' +
'<th style="text-align: right;" width="50">操作</th>' +
'</tr>' +
'</thead>' +
'<tbody></tbody>' +
'</table>' +
'</div>' +
'<ul class="help-info-text c7 pull-left">\
<li>【封锁IP】此处封锁的IP仅针对SSH服务,即被封锁的IP将无法连接SSH</li>\
<li>【添加】手动添加的IP封锁只能手动解封!</li>\
</ul>';
$('.soft-man-con').html(con);
$('.add_lock_address').click(function(){
var address = $('input[name="s_address"]').val();
ssPost('add_ssh_limit', {ip:address}, function(rdata){
var rdata = $.parseJSON(rdata.data);
showMsg(rdata.msg, function(){
ssLockAddressList();
},{icon:rdata.status?1:2,shade: [0.3, '#000']},2000);
});
});
ssLockAddressList();
}
+1140
View File
File diff suppressed because it is too large Load Diff
Binary file not shown.

After

Width:  |  Height:  |  Size: 974 B

+26
View File
@@ -0,0 +1,26 @@
<div class="bt-form">
<div class='plugin_version'></div>
<div class="bt-w-main">
<div class="bt-w-menu">
<p class="bgw" onclick="pluginService('tgbot');">服务</p>
<p onclick="pluginInitD('tgbot');">自启动</p>
<p onclick="botConf();">Bot配置</p>
<p onclick="botExtList();">扩展列表</p>
<p onclick="pluginLogs('tgbot','','run_log');">日志</p>
</div>
<div class="bt-w-con pd15">
<div class="soft-man-con"></div>
</div>
</div>
</div>
<style>
.conf_p span{
width: 70px;
}
</style>
<script type="text/javascript">
resetPluginWinHeight(350);
$.getScript( "/plugins/file?name=tgbot&f=js/tgbot.js", function(){
pluginService('tgbot');
});
</script>
+367
View File
@@ -0,0 +1,367 @@
# coding:utf-8
import sys
import io
import os
import time
import re
import json
import base64
sys.path.append(os.getcwd() + "/class/core")
import mw
app_debug = False
if mw.isAppleSystem():
app_debug = True
def getPluginName():
return 'tgbot'
def getPluginDir():
return mw.getPluginDir() + '/' + getPluginName()
def getServerDir():
return mw.getServerDir() + '/' + getPluginName()
def getInitDFile():
if app_debug:
return '/tmp/' + getPluginName()
return '/etc/init.d/' + getPluginName()
def getConfigData():
cfg_path = getServerDir() + "/data.cfg"
if not os.path.exists(cfg_path):
mw.writeFile(cfg_path, '{}')
t = mw.readFile(cfg_path)
return json.loads(t)
def writeConf(data):
cfg_path = getServerDir() + "/data.cfg"
mw.writeFile(cfg_path, json.dumps(data))
return True
def getExtCfg():
cfg_path = getServerDir() + "/extend.cfg"
if not os.path.exists(cfg_path):
mw.writeFile(cfg_path, '{}')
t = mw.readFile(cfg_path)
return json.loads(t)
def writeExtCfg(data):
cfg_path = getServerDir() + "/extend.cfg"
return mw.writeFile(cfg_path, json.dumps(data))
def getInitDTpl():
path = getPluginDir() + "/init.d/" + getPluginName() + ".tpl"
return path
def getArgs():
args = sys.argv[2:]
tmp = {}
args_len = len(args)
if args_len == 1:
t = args[0].strip('{').strip('}')
if t.strip() == '':
tmp = []
else:
t = t.split(':')
tmp[t[0]] = t[1]
tmp[t[0]] = t[1]
elif args_len > 1:
for i in range(len(args)):
t = args[i].split(':')
tmp[t[0]] = t[1]
return tmp
def checkArgs(data, ck=[]):
for i in range(len(ck)):
if not ck[i] in data:
return (False, mw.returnJson(False, '参数:(' + ck[i] + ')没有!'))
return (True, mw.returnJson(True, 'ok'))
def status():
data = mw.execShell(
"ps -ef|grep tgbot |grep -v grep | grep -v mdserver-web | awk '{print $2}'")
if data[0] == '':
return 'stop'
return 'start'
def initDreplace():
file_tpl = getInitDTpl()
service_path = mw.getServerDir()
app_path = service_path + '/' + getPluginName()
initD_path = getServerDir() + '/init.d'
if not os.path.exists(initD_path):
os.mkdir(initD_path)
file_bin = initD_path + '/' + getPluginName()
# initd replace
# if not os.path.exists(file_bin):
content = mw.readFile(file_tpl)
content = content.replace('{$SERVER_PATH}', service_path + '/mdserver-web')
content = content.replace('{$APP_PATH}', app_path)
mw.writeFile(file_bin, content)
mw.execShell('chmod +x ' + file_bin)
# systemd
systemDir = mw.systemdCfgDir()
systemService = systemDir + '/tgbot.service'
systemServiceTpl = getPluginDir() + '/init.d/tgbot.service.tpl'
if os.path.exists(systemDir) and not os.path.exists(systemService):
service_path = mw.getServerDir()
se_content = mw.readFile(systemServiceTpl)
se_content = se_content.replace('{$APP_PATH}', app_path)
mw.writeFile(systemService, se_content)
mw.execShell('systemctl daemon-reload')
return file_bin
def tbOp(method):
file = initDreplace()
if not mw.isAppleSystem():
data = mw.execShell('systemctl ' + method + ' ' + getPluginName())
if data[1] == '':
return 'ok'
return data[1]
data = mw.execShell(file + ' ' + method)
# print(data)
if data[1] == '':
return 'ok'
return 'ok'
def start():
return tbOp('start')
def stop():
return tbOp('stop')
def restart():
status = tbOp('restart')
return status
def reload():
tgbot_tpl = getPluginDir() + '/startup/tgbot.py'
tgbot_dst = getServerDir() + '/tgbot.py'
content = mw.readFile(tgbot_tpl)
mw.writeFile(tgbot_dst, content)
ext_src = getPluginDir() + '/startup/extend'
ext_dst = getServerDir()
mw.execShell('cp -rf ' + ext_src + ' ' + ext_dst)
return tbOp('restart')
def initdStatus():
if mw.isAppleSystem():
return "Apple Computer does not support"
shell_cmd = 'systemctl status ' + \
getPluginName() + ' | grep loaded | grep "enabled;"'
data = mw.execShell(shell_cmd)
if data[0] == '':
return 'fail'
return 'ok'
def initdInstall():
if mw.isAppleSystem():
return "Apple Computer does not support"
mw.execShell('systemctl enable ' + getPluginName())
return 'ok'
def initdUinstall():
if mw.isAppleSystem():
return "Apple Computer does not support"
mw.execShell('systemctl disable ' + getPluginName())
return 'ok'
def getBotConf():
data = getConfigData()
if 'bot' in data:
return mw.returnJson(True, 'ok', data['bot'])
return mw.returnJson(False, 'ok', {})
def setBotConf():
args = getArgs()
data_args = checkArgs(args, ['app_token'])
if not data_args[0]:
return data_args[1]
data = getConfigData()
args['app_token'] = base64.b64decode(args['app_token']).decode('ascii')
data['bot'] = args
writeConf(data)
return mw.returnJson(True, '保存成功!', [])
def installPreInspection():
i = sys.version_info
if i[0] < 3 or i[1] < 7:
return "telebot在python小于3.7无法正常使用"
return 'ok'
def uninstallPreInspection():
stop()
return "请手动删除<br/> rm -rf {}".format(getServerDir())
def getExtCfgByName(name):
elist = getExtCfg()
for x in elist:
if x['name'] == name:
return x
return None
def botExtList():
args = getArgs()
data_args = checkArgs(args, ['p'])
if not data_args[0]:
return data_args[1]
ext_path = getServerDir() + '/extend'
if not os.path.exists(ext_path):
return mw.returnJson(False, 'ok', [])
elist_source = os.listdir(ext_path)
elist = []
for e in elist_source:
if e.endswith('py'):
elist.append(e)
page = int(args['p'])
page_size = 5
make_ext_list = []
for ex in elist:
tmp = {}
tmp['name'] = ex
edata = getExtCfgByName(ex)
if edata:
tmp['status'] = edata['status']
else:
tmp['status'] = 'stop'
tmp['tag'] = ex.split('_')[0]
make_ext_list.append(tmp)
writeExtCfg(make_ext_list)
dlist_sum = len(make_ext_list)
page_start = int((page - 1) * page_size)
page_end = page_start + page_size
if page_end >= dlist_sum:
ret_data = make_ext_list[page_start:]
else:
ret_data = make_ext_list[page_start:page_end]
data = {}
data['data'] = ret_data
data['args'] = args
data['list'] = mw.getPage(
{'count': dlist_sum, 'p': page, 'row': page_size, 'tojs': 'botExtListP'})
return mw.returnJson(True, 'ok', data)
def setExtStatus():
args = getArgs()
data_args = checkArgs(args, ['name', 'status'])
if not data_args[0]:
return data_args[1]
elist = getExtCfg()
name = args['name']
status = args['status']
for x in range(len(elist)):
if elist[x]['name'] == name:
elist[x]['status'] = status
break
writeExtCfg(elist)
action = '开启'
if status == 'stop':
action = '关闭'
return mw.returnJson(True, action + '[' + name + ']扩展成功')
def runLog():
p = getServerDir() + '/task.log'
return p
if __name__ == "__main__":
func = sys.argv[1]
if func == 'status':
print(status())
elif func == 'start':
print(start())
elif func == 'stop':
print(stop())
elif func == 'restart':
print(restart())
elif func == 'reload':
print(reload())
elif func == 'initd_status':
print(initdStatus())
elif func == 'initd_install':
print(initdInstall())
elif func == 'initd_uninstall':
print(initdUinstall())
elif func == 'install_pre_inspection':
print(installPreInspection())
elif func == 'uninstall_pre_inspection':
print(uninstallPreInspection())
elif func == 'get_bot_conf':
print(getBotConf())
elif func == 'set_bot_conf':
print(setBotConf())
elif func == 'bot_ext_list':
print(botExtList())
elif func == 'set_ext_status':
print(setExtStatus())
elif func == 'run_log':
print(runLog())
else:
print('error')
+20
View File
@@ -0,0 +1,20 @@
{
"sort": 7,
"ps": "简单Telegram机器人",
"name": "tgbot",
"title": "tgbot",
"shell": "install.sh",
"versions":["1.0"],
"tip": "soft",
"checks": "server/tgbot",
"path": "server/tgbot",
"install_pre_inspection":true,
"uninstall_pre_inspection":true,
"display": 1,
"author": "midokos",
"date": "2023-03-06",
"home": "https://core.telegram.org/bots/api",
"depend_doc1":"https://github.com/eternnoir/pyTelegramBotAPI",
"type": 0,
"pid": "5"
}
+14
View File
@@ -0,0 +1,14 @@
[Unit]
Description=Tgbot Service
After=network.target
[Service]
Type=forking
ExecStart={$APP_PATH}/init.d/tgbot start
ExecStop={$APP_PATH}/init.d/tgbot stop
ExecReload={$APP_PATH}/init.d/tgbot reload
KillMode=process
Restart=on-failure
[Install]
WantedBy=multi-user.target
+85
View File
@@ -0,0 +1,85 @@
#!/bin/sh
# chkconfig: 2345 55 25
# description: Tgbot Service
### BEGIN INIT INFO
# Provides: Tgbot
# Required-Start: $all
# Required-Stop: $all
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
# Short-Description: starts Tgbot
# Description: starts the MDW-Web
### END INIT INFO
# Simple Tgbot init.d script conceived to work on Linux systems
# as it does use of the /proc filesystem.
PATH=/usr/local/bin:/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export LANG=en_US.UTF-8
mw_path={$SERVER_PATH}
PATH=$PATH:$mw_path/bin
if [ -f $mw_path/bin/activate ];then
source $mw_path/bin/activate
fi
tg_start(){
isStart=`ps -ef|grep 'tgbot.py' |grep -v grep | awk '{print $2}'`
if [ "$isStart" == '' ];then
echo -e "starting tgbot... \c"
cd $mw_path
python3 {$APP_PATH}/tgbot.py >> {$APP_PATH}/task.log &
isStart=""
while [[ "$isStart" == "" ]];
do
echo -e ".\c"
sleep 0.5
isStart=`ps -ef|grep 'tgbot.py' |grep -v grep | awk '{print $2}'`
let n+=1
if [ $n -gt 20 ];then
break;
fi
done
if [ "$isStart" == '' ];then
echo -e "\033[31mfailed\033[0m"
echo -e "\033[31mError: tgbot service startup failed.\033[0m"
return;
fi
echo -e "\033[32mdone\033[0m"
else
echo "starting tgbot...(pid $(echo $isStart)) already running"
fi
}
tg_stop(){
echo -e "stopping tgbot ... \c";
arr=`ps aux|grep 'tgbot.py'|grep -v grep|awk '{print $2}'`
for p in ${arr[@]}
do
kill -9 $p > /dev/null 2>&1
done
echo -e "\033[32mdone\033[0m"
}
case "$1" in
start)
tg_start
;;
stop)
tg_stop
;;
restart|reload)
tg_stop
sleep 0.3
tg_start
;;
*)
echo "Please use start or stop as first argument"
;;
esac
+59
View File
@@ -0,0 +1,59 @@
#!/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=$2
# pip3 install ccxt
if [ -f ${rootPath}/bin/activate ];then
source ${rootPath}/bin/activate
fi
pip3 install pyTelegramBotAPI
pip3 install telebot
Install_App()
{
echo '正在安装脚本文件...' > $install_tmp
mkdir -p $serverPath/source
mkdir -p $serverPath/tgbot
echo "${VERSION}" > $serverPath/tgbot/version.pl
cp -rf ${rootPath}/plugins/tgbot/startup/* $serverPath/tgbot
cd ${rootPath} && python3 ${rootPath}/plugins/tgbot/index.py start
cd ${rootPath} && python3 ${rootPath}/plugins/tgbot/index.py initd_install
echo '安装完成' > $install_tmp
}
Uninstall_App()
{
if [ -f /usr/lib/systemd/system/tgbot.service ];then
systemctl stop tgbot
systemctl disable tgbot
rm -rf /usr/lib/systemd/system/tgbot.service
systemctl daemon-reload
fi
if [ -f $serverPath/tgbot/initd/tgbot ];then
$serverPath/tgbot/initd/tgbot stop
fi
rm -rf $serverPath/tgbot
echo "Uninstall_redis" > $install_tmp
}
action=$1
if [ "${1}" == 'install' ];then
Install_App
else
Uninstall_App
fi
+148
View File
@@ -0,0 +1,148 @@
function appPost(method, args,callback){
var loadT = layer.msg('正在获取...', { icon: 16, time: 0, shade: 0.3 });
var req_data = {};
req_data['name'] = 'tgbot';
req_data['func'] = method;
if (typeof(args) == 'string'){
req_data['args'] = JSON.stringify(toArrayObject(args));
} else {
req_data['args'] = JSON.stringify(args);
}
$.post('/plugins/run', req_data, function(data) {
layer.close(loadT);
if(typeof(callback) == 'function'){
callback(data);
}
},'json');
}
function appPostCallbak(method, args,callback){
var loadT = layer.msg('正在获取...', { icon: 16, time: 0, shade: 0.3 });
var req_data = {};
req_data['name'] = 'tgbot';
req_data['func'] = method;
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 botConf(){
appPost('get_bot_conf','',function(data){
var rdata = $.parseJSON(data.data);
var app_token = 'app_token';
if(rdata['status']){
db_data = rdata['data'];
app_token = db_data['app_token'];
}
var mlist = '';
mlist += '<p><span>app_token</span><input style="width: 250px;" class="bt-input-text mr5" name="app_token" value="'+app_token+'" type="text"><font>必填写</font></p>'
var option = '<style>.conf_p p{margin-bottom: 2px}</style>\
<div class="conf_p" style="margin-bottom:0">\
' + mlist + '\
<div style="margin-top:10px; padding-right:15px" class="text-right">\
<button class="btn btn-success btn-sm" onclick="submitBotConf()">保存</button>\
</div>\
</div>';
$(".soft-man-con").html(option);
});
}
function submitBotConf(){
var pull_data = {};
pull_data['app_token'] = base64_encode($('input[name="app_token"]').val());
appPost('set_bot_conf',pull_data,function(data){
var rdata = $.parseJSON(data.data);
layer.msg(rdata['msg'],{icon:rdata['status']?1:2,time:2000,shade: [0.3, '#000']});
});
}
function botExtList(){
var body = '<div class="divtable mtb10">\
<table class="table table-hover" width="100%" cellspacing="0" cellpadding="0" border="0">\
<thead>\
<tr>\
<th width="20">脚本</th>\
<th width="120">类型</th>\
<th width="10">状态</th>\
</tr>\
</thead>\
<tbody id="ext_list"></tbody>\
</table>\
<div class="dataTables_paginate paging_bootstrap pagination">\
<ul id="ext_list_page" class="page"></ul>\
</div>\
</div>';
$('.soft-man-con').html(body);
botExtListP(1);
}
function setBotExtStatus(name,status){
appPost('set_ext_status',{'name':name,'status':status}, function(rdata){
var rdata = $.parseJSON(rdata.data);
layer.msg(rdata['msg'],);
showMsg(rdata['msg'], function(){
botExtListP(1);
},{icon:rdata['status']?1:2,shade: [0.3, '#000']},2000);
});
}
function botExtListP(p=1){
appPost('bot_ext_list',{'p':p}, function(rdata){
// console.log(rdata);
var rdata = $.parseJSON(rdata.data);
// console.log(rdata);
var tBody = '';
if (!rdata.status && rdata.data.length == 0 ){
var tBody = '<tr><td colspan="4"><div style="text-align:center;">无数据</div></td></tr>';
} else{
var ldata = rdata.data.data;
for (var i = 0; i < ldata.length; i++) {
tBody += '<tr data-name="'+ldata[i]['name']+'">'
tBody += '<td>'+ldata[i]['name']+'</td>';
tBody += '<td>'+ldata[i]['tag']+'</td>';
if (ldata[i]['status'] == 'start'){
tBody += '<td><span style="color:#20a53a;cursor: pointer;" class="ext_status glyphicon glyphicon-play"></span></td>';
} else{
tBody += '<td><span style="color:red;cursor: pointer;" class="ext_status glyphicon glyphicon-pause"></span></td>';
}
tBody +='<tr>';
}
}
$('#ext_list').html(tBody);
$('#ext_list_page').html(rdata.data.list);
$('#ext_list .ext_status').click(function(){
var name = $(this).parent().parent().data('name');
var status = 'stop';
if ($(this).hasClass('glyphicon-pause')){
status = 'start';
}
setBotExtStatus(name,status);
});
});
}
+27
View File
@@ -0,0 +1,27 @@
# coding:utf-8
import sys
import io
import os
import time
import re
import json
import base64
import threading
sys.path.append(os.getcwd() + "/class/core")
import mw
import telebot
from telebot import types
from telebot.util import quick_markup
def init(bot):
bot.delete_my_commands(scope=None, language_code=None)
bot.set_my_commands(
commands=[
telebot.types.BotCommand("start", "查看帮助信息"),
telebot.types.BotCommand("faq", "查看bbs帖子主题【不要忘记:冒号】"),
],
)
+91
View File
@@ -0,0 +1,91 @@
# coding:utf-8
import sys
import io
import os
import time
import re
import json
import base64
import threading
sys.path.append(os.getcwd() + "/class/core")
import mw
import telebot
from telebot import types
from telebot.util import quick_markup
# 广告推送实例
chat_id = -1001578009023
# chat_id = 5568699210
def send_msg(bot, tag='ad', trigger_time=300):
# 信号只在一个周期内执行一次|start
lock_file = mw.getServerDir() + '/tgbot/lock.json'
if not os.path.exists(lock_file):
mw.writeFile(lock_file, '{}')
lock_data = json.loads(mw.readFile(lock_file))
if tag in lock_data:
diff_time = time.time() - lock_data[tag]['do_time']
if diff_time >= trigger_time:
lock_data[tag]['do_time'] = time.time()
else:
return False, 0, 0
else:
lock_data[tag] = {'do_time': time.time()}
mw.writeFile(lock_file, json.dumps(lock_data))
# 信号只在一个周期内执行一次|end
keyboard = [
[
types.InlineKeyboardButton(
text="海外服务器高防CDN请联系玥玥", url='https://t.me/CDNYUEYUE')
],
[
types.InlineKeyboardButton(
text="CK资源采集", url='https://ckzy1.com/')
],
[
types.InlineKeyboardButton(
text="♻️CMS导航网♻️", url='https://t.me/maccms_jccms')
],
[
types.InlineKeyboardButton(
text="💰流量变现💰", url='https://t.me/taohaozhan')
],
[
types.InlineKeyboardButton(
text="🙎‍♂️代实名🙍‍♀️过人脸🅾️国际阿里云腾讯云(赞助商)", url='https://t.me/gjgzs2022')
],
[
types.InlineKeyboardButton(
text="💎DigitalVirt(赞助商)", url='https://digitalvirt.com/aff.php?aff=154')
],
[
types.InlineKeyboardButton(
text="论坛", url='https://bbs.midoks.me'),
types.InlineKeyboardButton(
text="搜索", url='https://bbs.midoks.me/search.php'),
types.InlineKeyboardButton(
text="@ME", url='tg://user?id=5568699210')
]
]
markup = types.InlineKeyboardMarkup(keyboard)
image_file = mw.getPluginDir() + '/tgbot/static/image/ad.png'
telebot_image = telebot.types.InputFile(image_file)
msg = bot.send_photo(chat_id, telebot_image, reply_markup=markup)
# print(msg.message_id)
time.sleep(5 * 60)
del_msg = bot.delete_message(chat_id=chat_id, message_id=msg.message_id)
# print(del_msg)
def run(bot):
send_msg(bot, 'ad', 1 * 60 * 60)
@@ -0,0 +1,93 @@
# coding:utf-8
import sys
import io
import os
import time
import re
import json
import base64
import threading
sys.path.append(os.getcwd() + "/class/core")
import mw
import telebot
from telebot import types
from telebot.util import quick_markup
# 推送最新的帖子
chat_id = -1001578009023
# chat_id = 5568699210
def get_newest_tid():
api_new = 'https://bbs.midoks.me/plugin.php?id=external_api&f=bbs_newest'
api_next = 'https://bbs.midoks.me/plugin.php?id=external_api&f=bbs_next_tid&tid='
tid_push = mw.getServerDir() + '/tgbot/bbs_newest_push.json'
if not os.path.exists(tid_push):
data = mw.httpGet(api_new)
data = json.loads(data)
if data['code'] == 0:
tid = data['data'][0]['tid']
mw.writeFile(tid_push, tid)
return True, data['data'][0]
tid = mw.readFile(tid_push)
data = mw.httpGet(api_next + tid)
data = json.loads(data)
if data['code'] == 0 and len(data['data']) > 0:
# print(data)
tid = data['data'][0]['tid']
mw.writeFile(tid_push, tid)
return True, data['data'][0]
return False, None
def send_msg(bot, tag='ad', trigger_time=300):
# 信号只在一个周期内执行一次|start
lock_file = mw.getServerDir() + '/tgbot/lock.json'
if not os.path.exists(lock_file):
mw.writeFile(lock_file, '{}')
lock_data = json.loads(mw.readFile(lock_file))
if tag in lock_data:
diff_time = time.time() - lock_data[tag]['do_time']
if diff_time >= trigger_time:
lock_data[tag]['do_time'] = time.time()
else:
return False, 0, 0
else:
lock_data[tag] = {'do_time': time.time()}
mw.writeFile(lock_file, json.dumps(lock_data))
# 信号只在一个周期内执行一次|end
yes, info = get_newest_tid()
if yes:
url = 'https://bbs.midoks.me/thread-' + info['tid'] + '-1-1.html'
keyboard = [
[
types.InlineKeyboardButton(text=info['subject'], url=url)
],
[
types.InlineKeyboardButton(
text="论坛", url='https://bbs.midoks.me'),
types.InlineKeyboardButton(
text="搜索", url='https://bbs.midoks.me/search.php')
]
]
markup = types.InlineKeyboardMarkup(keyboard)
bot.send_message(
chat_id, "由【" + info['author'] + "】发帖!", reply_markup=markup)
def run(bot):
send_msg(bot, 'push_bbs_newest_tid', 300)
if __name__ == "__main__":
print(get_newest_tid())
@@ -0,0 +1,107 @@
# coding:utf-8
import sys
import io
import os
import time
import re
import json
import base64
import threading
sys.path.append(os.getcwd() + "/class/core")
import mw
import telebot
from telebot import types
from telebot.util import quick_markup
# 轮播实例
chat_id = -1001578009023
# chat_id = 5568699210
def send_msg(bot, tag='ad', trigger_time=300):
# 信号只在一个周期内执行一次|start
lock_file = mw.getServerDir() + '/tgbot/lock.json'
if not os.path.exists(lock_file):
mw.writeFile(lock_file, '{}')
lock_data = json.loads(mw.readFile(lock_file))
if tag in lock_data:
diff_time = time.time() - lock_data[tag]['do_time']
if diff_time >= trigger_time:
lock_data[tag]['do_time'] = time.time()
else:
return False, 0, 0
else:
lock_data[tag] = {'do_time': time.time()}
mw.writeFile(lock_file, json.dumps(lock_data))
# 信号只在一个周期内执行一次|end
# https://t.me/gjgzs2022 | 19/m
# ♻️CMS导航网♻️/💰流量变现💰 | 28/m
# CK资源采集 |29/m
# 高防服务器CDN请联系玥玥 |3/m
keyboard = [
[
types.InlineKeyboardButton(
text="海外服务器高防CDN请联系玥玥", url='https://t.me/CDNYUEYUE')
],
[
types.InlineKeyboardButton(
text="CK资源采集", url='https://ckzy1.com/')
],
[
types.InlineKeyboardButton(
text="♻️CMS导航网♻️", url='https://t.me/maccms_jccms')
],
[
types.InlineKeyboardButton(
text="💰流量变现💰", url='https://t.me/taohaozhan')
],
[
types.InlineKeyboardButton(
text="🙎‍♂️代实名🙍‍♀️过人脸🅾️国际阿里云腾讯云(赞助商)", url='https://t.me/gjgzs2022')
],
[
types.InlineKeyboardButton(
text="💎DigitalVirt(赞助商)", url='https://digitalvirt.com/aff.php?aff=154')
],
[
types.InlineKeyboardButton(
text="论坛", url='https://bbs.midoks.me'),
types.InlineKeyboardButton(
text="搜索", url='https://bbs.midoks.me/search.php'),
types.InlineKeyboardButton(
text="@ME", url='tg://user?id=5568699210')
]
]
markup = types.InlineKeyboardMarkup(keyboard)
msg_notice = "由于在解决的问题的时候,不给信息,无法了解情况。以后不再群里回答技术问题。全部去论坛提问。在解决问题的过程中,可能需要面板信息,和SSH信息,如无法提供请不要提问。为了让群里都知晓。轮播一年!\n"
msg_notice += "为了不打扰双方,私聊解决问题先转100U,否则无视!\n"
msg_notice += "现阶段3件事:\n"
msg_notice += "* 炒币\n"
msg_notice += "* 量化交易\n"
msg_notice += "* MW-VIP系统开发\n"
msg_notice += "至少1年时间都没空。非诚勿扰!"
msg = bot.send_message(chat_id, msg_notice, reply_markup=markup)
# print(msg.message_id)
time.sleep(60)
try:
bot.delete_message(
chat_id=chat_id, message_id=msg.message_id)
except Exception as e:
pass
def run(bot):
try:
send_msg(bot, 'notice_msg', 180)
except Exception as e:
writeLog('-----push_notice_msg error start -------')
print(mw.getTracebackInfo())
writeLog('-----push_notice_msg error start -------')
+1
View File
@@ -0,0 +1 @@
push_*.py 识别为推送插件
+220
View File
@@ -0,0 +1,220 @@
import sys
import io
import os
import time
import re
import json
import base64
import threading
sys.path.append(os.getcwd() + "/class/core")
import mw
import telebot
from telebot import types
from telebot.util import quick_markup
def isThisCmd(cmd, msg):
clen = len(cmd)
msg_len = len(msg)
if msg_len < clen:
return False
check_msg = msg[0:clen]
if cmd == check_msg:
return True
return False
def getReadCmd(cmd, msg):
clen = len(cmd)
msg_len = len(msg)
real_msg = msg[clen:]
return real_msg
def getFaqKw(cmd):
matchObj = re.match(r'寻找【(.*?)】问题如下', cmd, re.M | re.I)
data = matchObj.groups()
if len(data) > 0:
return True, data[0]
return False, ''
def searchHttpPage(kw='', p=1, size=1):
import urllib
kw = kw.strip()
kw = urllib.parse.quote_plus(kw)
api = 'https://bbs.midoks.me/plugin.php?id=external_api&f=bbs_search&q=' + kw + \
'&size=' + str(size) + '&p=' + str(p)
# print('url', api)
data = mw.httpGet(api)
# print(data)
data = json.loads(data)
# print(data)
if data['code'] > -1:
alist = data['data']['list']
r = []
for x in alist:
tmp = {}
tmp['tid'] = x['tid']
tmp['subject'] = x['subject']
tmp['url'] = 'https://bbs.midoks.me/thread-' + \
x['tid'] + '-1-1.html'
r.append(tmp)
data['data']['list'] = r
return data
def searchFaq(bot, message, cmd_text):
# cmd_text = 'mw'
data = searchHttpPage(cmd_text, 1, 5)
if data['code'] == 0 and len(data['data']['list']) > 0:
keyboard = []
dlist = data['data']['list']
for x in dlist:
keyboard.append([types.InlineKeyboardButton(
text=x['subject'], url=x['url'])])
keyboard.append([
types.InlineKeyboardButton(
text="下一页", callback_data='bbs_next_page_2'),
types.InlineKeyboardButton(
text="第" + str(data['data']['p']) + "页,共" + str(data['data']['page_num']) + "页", callback_data='bbs_page_total')
])
keyboard.append([types.InlineKeyboardButton(
text="关闭消息", callback_data='bbs_search_close')])
# print(keyboard)
markup = types.InlineKeyboardMarkup(keyboard)
bot.send_message(message.chat.id, "寻找【" +
cmd_text.strip() + "】问题如下:", reply_markup=markup)
else:
keyboard = [
[
types.InlineKeyboardButton(
text="论坛", url='https://bbs.midoks.me'),
types.InlineKeyboardButton(
text="搜索", url='https://bbs.midoks.me/search.php')
],
[
types.InlineKeyboardButton(
text="关闭消息", callback_data='bbs_search_close')
]
]
markup = types.InlineKeyboardMarkup(keyboard)
bot.send_message(
message.chat.id, "未找到合适内容,请在官方论坛[bbs.midoks.me]提问!", reply_markup=markup)
return True
def searchDebug(bot, message, cmd_text):
searchFaq(bot, message, cmd_text)
return True
def answer_callback_query(bot, call):
keyword = call.data
if keyword == 'bbs_search_close':
bot.delete_message(chat_id=call.message.chat.id,
message_id=call.message.message_id)
return
is_bbs_page = False
p = 1
if keyword.startswith('bbs_next_page'):
is_bbs_page = True
p = keyword.replace('bbs_next_page_', '')
if keyword.startswith('bbs_pre_page'):
is_bbs_page = True
p = keyword.replace('bbs_pre_page_', '')
# print("p", p)
if is_bbs_page:
is_match, cmd_text = getFaqKw(call.message.text)
if not is_match:
bot.edit_message_text(
chat_id=call.message.chat.id, message_id=call.message.message_id, text="出现错误!")
return
data = searchHttpPage(cmd_text, int(p), 5)
dlist = data['data']['list']
# print(data)
keyboard = []
for x in dlist:
keyboard.append([types.InlineKeyboardButton(
text=x['subject'], url=x['url'])])
page_nav = []
if int(data['data']['p']) > 1:
page_nav.append(types.InlineKeyboardButton(
text="上一页", callback_data='bbs_pre_page_' + str(int(p) - 1)))
if data['data']['page_num'] != data['data']['p']:
page_nav.append(types.InlineKeyboardButton(
text="下一页", callback_data='bbs_next_page_' + str(int(p) + 1)))
page_nav.append(types.InlineKeyboardButton(
text="第" + str(data['data']['p']) + "页,共" + str(data['data']['page_num']) + "页", callback_data='bbs_page_total'))
keyboard.append(page_nav)
keyboard.append([types.InlineKeyboardButton(
text="关闭消息", callback_data='bbs_search_close')])
markup = types.InlineKeyboardMarkup(keyboard)
bot.edit_message_text(chat_id=call.message.chat.id, message_id=call.message.message_id,
text=call.message.text, reply_markup=markup)
def run(bot, message):
text_body = message.text
# 过滤URL
is_has_url = re.search(
'(https?|ftp|file)://[-A-Za-z0-9+&@#/%?=~_|!:,.;]+[-A-Za-z0-9+&@#/%=~_|]', text_body)
if is_has_url:
return bot
# print(text_body)
if isThisCmd('/faq:', text_body):
cmd_text = getReadCmd('/faq:', text_body)
return searchFaq(bot, message, cmd_text)
# if isThisCmd('/debug', text_body):
# cmd_text = getReadCmd('/debug', text_body)
# return searchDebug(bot, message, cmd_text)
if text_body.find('?') > -1 or text_body.find('?') > -1:
return_msg = "你似乎在寻找【" + text_body + "】答案:\n"
return_msg += "/faq:开始寻找你的问题\n"
keyboard = [
[
types.InlineKeyboardButton(
text="如未找到,可以在论坛补充你的提问!", url='https://bbs.midoks.me'),
]
]
markup = types.InlineKeyboardMarkup(keyboard)
bot.reply_to(message, return_msg, reply_markup=markup)
return bot
if __name__ == "__main__":
# print(isThisCmd('/?:', '/?:如何在安装面板'))
# print(getReadCmd('/?:', '/?:如何在安装面板'))
# print(searchHttpPage('mw'))
print(getFaqKw('寻找【mw】问题如下:'))
+200
View File
@@ -0,0 +1,200 @@
# coding:utf-8
import sys
import io
import os
import time
import re
import json
import base64
import threading
sys.path.append(os.getcwd() + "/class/core")
import mw
import telebot
def getPluginName():
return 'tgbot'
def getPluginDir():
return mw.getPluginDir() + '/' + getPluginName()
def getServerDir():
return mw.getServerDir() + '/' + getPluginName()
sys.path.append(getServerDir() + "/extend")
def getConfigData():
cfg_path = getServerDir() + "/data.cfg"
if not os.path.exists(cfg_path):
mw.writeFile(cfg_path, '{}')
t = mw.readFile(cfg_path)
return json.loads(t)
def writeConf(data):
cfg_path = getServerDir() + "/data.cfg"
mw.writeFile(cfg_path, json.dumps(data))
return True
def getExtCfg():
cfg_path = getServerDir() + "/extend.cfg"
if not os.path.exists(cfg_path):
mw.writeFile(cfg_path, '{}')
t = mw.readFile(cfg_path)
return json.loads(t)
def getStartExtCfgByTag(tag='push'):
# 获取开启的扩展
elist = getExtCfg()
rlist = []
for x in elist:
if x['tag'] == tag and x['status'] == 'start':
rlist.append(x)
return rlist
def writeLog(log_str):
if __name__ == "__main__":
print(log_str)
now = mw.getDateFromNow()
log_file = getServerDir() + '/task.log'
mw.writeFileLog(now + ':' + log_str, log_file, limit_size=5 * 1024)
return True
# start tgbot
cfg = getConfigData()
while True:
cfg = getConfigData()
if 'bot' in cfg and 'app_token' in cfg['bot']:
if cfg['bot']['app_token'] != '' and cfg['bot']['app_token'] != 'app_token':
break
writeLog('等待输入配置,填写app_token')
time.sleep(3)
bot = telebot.TeleBot(cfg['bot']['app_token'])
init_list = getStartExtCfgByTag('init')
for p in init_list:
try:
script = p['name'].split('.')[0]
__import__(script).init(bot)
except Exception as e:
writeLog('-----init error start -------')
writeLog(mw.getTracebackInfo())
writeLog('-----init error end -------')
# @bot.message_handler(commands=['start', 'help'])
# def hanle_start_help(message):
# bot.reply_to(message, "hello world")
# @bot.message_handler(commands=['mw_echo'])
# def hanle_mw_echo(message):
# bot.reply_to(message, message.text)
@bot.message_handler(commands=['chat_id'])
def hanle_get_chat_id(message):
bot.reply_to(message, message.chat.id)
@bot.message_handler(func=lambda message: True)
def all_message(message):
rlist = getStartExtCfgByTag('receive')
for r in rlist:
try:
script = r['name'].split('.')[0]
__import__(script).run(bot, message)
except Exception as e:
writeLog('-----all_message error start -------')
writeLog(mw.getTracebackInfo())
writeLog('-----all_message error end -------')
@bot.callback_query_handler(func=lambda call: True)
def callback_query_handler(call):
rlist = getStartExtCfgByTag('receive')
for r in rlist:
try:
script = r['name'].split('.')[0]
__import__(script).answer_callback_query(bot, call)
except Exception as e:
writeLog('-----callback_query_handler error start -------')
writeLog(mw.getTracebackInfo())
writeLog('-----callback_query_handler error end -------')
def runBotPushTask():
plist = getStartExtCfgByTag('push')
for p in plist:
try:
script = p['name'].split('.')[0]
__import__(script).run(bot)
except Exception as e:
writeLog('-----runBotPushTask error start -------')
writeLog(mw.getTracebackInfo())
writeLog('-----runBotPushTask error end -------')
def botPush():
while True:
runBotPushTask()
time.sleep(1)
def runBotPushOtherTask():
plist = getStartExtCfgByTag('other')
for p in plist:
try:
script = p['name'].split('.')[0]
__import__(script).run(bot)
except Exception as e:
writeLog('-----runBotPushOtherTask error start -------')
writeLog(mw.getTracebackInfo())
writeLog('-----runBotPushOtherTask error end -------')
def botPushOther():
while True:
runBotPushOtherTask()
time.sleep(1)
def runBot(bot):
try:
bot.polling()
except Exception as e:
writeLog('-----runBot error start -------')
writeLog(str(e))
writeLog('-----runBot error end -------')
time.sleep(1)
runBot(bot)
if __name__ == "__main__":
# 机器人推送任务
botPushTask = threading.Thread(target=botPush)
botPushTask.start()
# 机器人其他推送任务
botPushOtherTask = threading.Thread(target=botPushOther)
botPushOtherTask.start()
writeLog('启动成功')
runBot(bot)
# asyncio.run(bot.polling())
Binary file not shown.

After

Width:  |  Height:  |  Size: 291 KiB

+19 -18
View File
@@ -25,7 +25,7 @@ class App():
self.__host_dir = self.getServerDir() + '/host'
if not os.path.exists(self.__host_dir):
os.makedirs(self.__host_dir)
mw.execShell('mkdir -p ' + self.__host_dir)
def getPluginName(self):
return 'webssh'
@@ -145,24 +145,25 @@ class App():
def get_server_list(self):
host_list = []
for name in os.listdir(self.__host_dir):
info_file = self.__host_dir + '/' + name + '/info.json'
if not os.path.exists(info_file):
continue
try:
info_tmp = self.getSshInfo(info_file)
host_info = {}
host_info['host'] = name
host_info['port'] = info_tmp['port']
host_info['ps'] = info_tmp['ps']
# host_info['sort'] = int(info_tmp['sort'])
except Exception as e:
print(e)
# if os.path.exists(info_file):
# os.remove(info_file)
# continue
if os.path.exists(self.__host_dir):
for name in os.listdir(self.__host_dir):
info_file = self.__host_dir + '/' + name + '/info.json'
if not os.path.exists(info_file):
continue
try:
info_tmp = self.getSshInfo(info_file)
host_info = {}
host_info['host'] = name
host_info['port'] = info_tmp['port']
host_info['ps'] = info_tmp['ps']
# host_info['sort'] = int(info_tmp['sort'])
except Exception as e:
print(e)
# if os.path.exists(info_file):
# os.remove(info_file)
# continue
host_list.append(host_info)
host_list.append(host_info)
host_list = sorted(host_list, key=lambda x: x['host'], reverse=False)
return mw.returnJson(True, 'ok!', host_list)
+8
View File
@@ -9,6 +9,14 @@
"css_path":"menu/index.css",
"js_path":"js/webssh.js"
}
},
{
"tag":"global_static",
"global_static": {
"title":"终端管理",
"name":"webssh",
"css_path":"menu/ico.css"
}
}
],
"id":3,
+10
View File
@@ -0,0 +1,10 @@
/* menu start */
.menu .menu_plugin_webssh {
background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAMBJREFUeNpiYKAQMM6cOVMASBuQozk9Pf0AC5AuAOJ6cgwAWt7IhMQvRKIfAPEEYgxhQWL3o9EFpBgwAeifQhKdD7YI5oWPUMEEmAQR4COyATBwAIgdgIacB2IFYkxhQouWB0BsCA3E86QGIsxvDSBXgGIDSxr5ALTgAk4XADWAQt4fiB2BChcAaQVoGoHheLwuAGqagBz/UNsciQ4DEgE/zAUfQM4DOt+eRANAYTMRZADIrxfIdMUFSnMzA0CAAQD0hjqnYxWD2gAAAABJRU5ErkJggg==");
}
.menu .current .menu_plugin_webssh:hover {
background-image: url("data:image/gif;base64,R0lGODlhEAAQAMQXAJ6ipUFITySRPIiMkDFRQuXm5jJKQy1kQCGeOzBXQSl3PiWLPVhfZSxrQCpxP9na2yeEPTREQ8LExmRqb5OXm/Hx8TU9RAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH/C05FVFNDQVBFMi4wAwEAAAAh/wtYTVAgRGF0YVhNUDw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuNi1jMTQ1IDc5LjE2MzQ5OSwgMjAxOC8wOC8xMy0xNjo0MDoyMiAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENDIDIwMTkgKFdpbmRvd3MpIiB4bXBNTTpJbnN0YW5jZUlEPSJ4bXAuaWlkOjMyQjYwMTU3OTA2QTExRUJCN0NFQUUwQjRDMzM4RDJDIiB4bXBNTTpEb2N1bWVudElEPSJ4bXAuZGlkOjMyQjYwMTU4OTA2QTExRUJCN0NFQUUwQjRDMzM4RDJDIj4gPHhtcE1NOkRlcml2ZWRGcm9tIHN0UmVmOmluc3RhbmNlSUQ9InhtcC5paWQ6MzJCNjAxNTU5MDZBMTFFQkI3Q0VBRTBCNEMzMzhEMkMiIHN0UmVmOmRvY3VtZW50SUQ9InhtcC5kaWQ6MzJCNjAxNTY5MDZBMTFFQkI3Q0VBRTBCNEMzMzhEMkMiLz4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz4B//79/Pv6+fj39vX08/Lx8O/u7ezr6uno5+bl5OPi4eDf3t3c29rZ2NfW1dTT0tHQz87NzMvKycjHxsXEw8LBwL++vby7urm4t7a1tLOysbCvrq2sq6qpqKempaSjoqGgn56dnJuamZiXlpWUk5KRkI+OjYyLiomIh4aFhIOCgYB/fn18e3p5eHd2dXRzcnFwb25tbGtqaWhnZmVkY2JhYF9eXVxbWllYV1ZVVFNSUVBPTk1MS0pJSEdGRURDQkFAPz49PDs6OTg3NjU0MzIxMC8uLSwrKikoJyYlJCMiISAfHh0cGxoZGBcWFRQTEhEQDw4NDAsKCQgHBgUEAwIBAAAh+QQFMgAXACwAAAAAEAAQAAAFU6AljmRpBVOqrqtFVXAsy9Q7VMMDzHHN/5UawGQC1CiWg4IoOiINAoGB6BxBENSjyIE4oFKMprYhIFgYkvTQpWWOqm6RcVBI2+/2AuXL6k8CcSYhACH5BAUyABcALAcACQAFAAIAAAUIYJSMBLGcSggAIfkEBTIAFwAsBwAJAAUAAgAABQhgMI0MI51ACAAh+QQFMgAXACwHAAkABQACAAAFCGCUjASxnEoIADs=");
}
/* menu end */
-11
View File
@@ -1,15 +1,4 @@
/* menu start */
.menu .menu_plugin_webssh {
background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAMBJREFUeNpiYKAQMM6cOVMASBuQozk9Pf0AC5AuAOJ6cgwAWt7IhMQvRKIfAPEEYgxhQWL3o9EFpBgwAeifQhKdD7YI5oWPUMEEmAQR4COyATBwAIgdgIacB2IFYkxhQouWB0BsCA3E86QGIsxvDSBXgGIDSxr5ALTgAk4XADWAQt4fiB2BChcAaQVoGoHheLwuAGqagBz/UNsciQ4DEgE/zAUfQM4DOt+eRANAYTMRZADIrxfIdMUFSnMzA0CAAQD0hjqnYxWD2gAAAABJRU5ErkJggg==");
}
.menu .current .menu_plugin_webssh:hover {
background-image: url("data:image/gif;base64,R0lGODlhEAAQAMQXAJ6ipUFITySRPIiMkDFRQuXm5jJKQy1kQCGeOzBXQSl3PiWLPVhfZSxrQCpxP9na2yeEPTREQ8LExmRqb5OXm/Hx8TU9RAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH/C05FVFNDQVBFMi4wAwEAAAAh/wtYTVAgRGF0YVhNUDw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuNi1jMTQ1IDc5LjE2MzQ5OSwgMjAxOC8wOC8xMy0xNjo0MDoyMiAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENDIDIwMTkgKFdpbmRvd3MpIiB4bXBNTTpJbnN0YW5jZUlEPSJ4bXAuaWlkOjMyQjYwMTU3OTA2QTExRUJCN0NFQUUwQjRDMzM4RDJDIiB4bXBNTTpEb2N1bWVudElEPSJ4bXAuZGlkOjMyQjYwMTU4OTA2QTExRUJCN0NFQUUwQjRDMzM4RDJDIj4gPHhtcE1NOkRlcml2ZWRGcm9tIHN0UmVmOmluc3RhbmNlSUQ9InhtcC5paWQ6MzJCNjAxNTU5MDZBMTFFQkI3Q0VBRTBCNEMzMzhEMkMiIHN0UmVmOmRvY3VtZW50SUQ9InhtcC5kaWQ6MzJCNjAxNTY5MDZBMTFFQkI3Q0VBRTBCNEMzMzhEMkMiLz4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz4B//79/Pv6+fj39vX08/Lx8O/u7ezr6uno5+bl5OPi4eDf3t3c29rZ2NfW1dTT0tHQz87NzMvKycjHxsXEw8LBwL++vby7urm4t7a1tLOysbCvrq2sq6qpqKempaSjoqGgn56dnJuamZiXlpWUk5KRkI+OjYyLiomIh4aFhIOCgYB/fn18e3p5eHd2dXRzcnFwb25tbGtqaWhnZmVkY2JhYF9eXVxbWllYV1ZVVFNSUVBPTk1MS0pJSEdGRURDQkFAPz49PDs6OTg3NjU0MzIxMC8uLSwrKikoJyYlJCMiISAfHh0cGxoZGBcWFRQTEhEQDw4NDAsKCQgHBgUEAwIBAAAh+QQFMgAXACwAAAAAEAAQAAAFU6AljmRpBVOqrqtFVXAsy9Q7VMMDzHHN/5UawGQC1CiWg4IoOiINAoGB6BxBENSjyIE4oFKMprYhIFgYkvTQpWWOqm6RcVBI2+/2AuXL6k8CcSYhACH5BAUyABcALAcACQAFAAIAAAUIYJSMBLGcSggAIfkEBTIAFwAsBwAJAAUAAgAABQhgMI0MI51ACAAh+QQFMgAXACwHAAkABQACAAAFCGCUjASxnEoIADs=");
}
/* menu end */
.xterm{
position: inherit;
}
+1
View File
@@ -27,6 +27,7 @@
"gif",
"jpg",
"css",
"svg",
"js",
"ico",
"woff2"
+5 -4
View File
@@ -191,8 +191,10 @@ def pSqliteDb(dbname='web_logs', site_name='unset', name="logs"):
conn = mw.M(dbname).dbPos(db_dir, name)
conn.execute("PRAGMA synchronous = 0")
conn.execute("PRAGMA page_size = 4096")
conn.execute("PRAGMA cache_size = 8000")
conn.execute("PRAGMA page_size = 32768")
conn.execute("PRAGMA journal_mode = wal")
conn.execute("PRAGMA journal_size_limit = 1073741824")
return conn
@@ -263,6 +265,8 @@ def initDreplace():
loadLuaSiteFile()
loadDebugLogFile()
if not mw.isAppleSystem():
mw.execShell("chown -R www:www " + getServerDir())
return 'ok'
@@ -277,9 +281,6 @@ def start():
import tool_task
tool_task.createBgTask()
if not mw.isAppleSystem():
mw.execShell("chown -R www:www " + getServerDir())
# issues:326
luaRestart()
return 'ok'
+14 -2
View File
@@ -1,10 +1,22 @@
{
"sort": 7,
"hook":[
{
"tag":"site_cb",
"site_cb": {
"title":"网站统计",
"name":"webstats",
"add":{"func":"reload"},
"update":{"func":"reload"},
"delete":{"func":"reload"}
}
}
],
"sort": "7",
"ps": "网站统计报表",
"name": "webstats",
"title": "网站统计",
"shell": "install.sh",
"versions":["0.2.4"],
"versions":["0.2.5"],
"tip": "soft",
"install_pre_inspection":true,
"checks": "server/webstats",
+17 -3
View File
@@ -14,12 +14,26 @@ install_tmp=${rootPath}/tmp/mw_install.pl
VERSION=$2
sys_os=`uname`
cn=$(curl -fsSL -m 10 http://ipinfo.io/json | grep "\"country\": \"CN\"")
# cn=$(curl -fsSL -m 10 http://ipinfo.io/json | grep "\"country\": \"CN\"")
# HTTP_PREFIX="https://"
# if [ ! -z "$cn" ];then
# HTTP_PREFIX="https://ghproxy.com/"
# fi
HTTP_PREFIX="https://"
if [ ! -z "$cn" ];then
LOCAL_ADDR=common
ping -c 1 gihub.com > /dev/null 2>&1
if [ "$?" != "0" ];then
LOCAL_ADDR=cn
HTTP_PREFIX="https://ghproxy.com/"
fi
PIPSRC="https://pypi.python.org/simple"
if [ "$LOCAL_ADDR" != "common" ];then
PIPSRC="https://pypi.tuna.tsinghua.edu.cn/simple"
fi
if [ "$sys_os" == "Darwin" ];then
BAK='_bak'
else
@@ -96,7 +110,7 @@ Install_App()
fi
# https://github.com/P3TERX/GeoLite.mmdb
pip install geoip2
pip install geoip2 -i $PIPSRC
# if [ ! -f $serverPath/webstats/GeoLite2-City.mmdb ];then
# wget --no-check-certificate -O $serverPath/webstats/GeoLite2-City.mmdb https://github.com/P3TERX/GeoLite.mmdb/releases/download/2022.10.16/GeoLite2-City.mmdb
# fi
+51 -23
View File
@@ -210,18 +210,18 @@ function _M.get_http_origin(self)
if not headers then return data end
local req_method = ngx.req.get_method()
if req_method ~='GET' then
-- API disabled in the context of log_by_lua*
-- ngx.req.read_body()
-- proxy_pass, fastcgi_pass, uwsgi_pass, and scgi_pass
data = ngx.var.request_body
if not data then
data = ngx.req.get_body_data()
end
if not data then
data = ngx.req.get_post_args(1000000)
end
-- API disabled in the context of log_by_lua
-- if not data then
-- ngx.req.read_body()
-- data = ngx.req.get_post_args(1000000)
-- end
if "string" == type(data) then
headers["payload"] = data
@@ -239,6 +239,7 @@ function _M.cronPre(self)
local time_key = self:get_store_key()
local time_key_next = self:get_store_key_with_time(ngx.time()+3600)
for site_k, site_v in ipairs(sites) do
local input_sn = site_v["name"]
@@ -250,16 +251,24 @@ function _M.cronPre(self)
'spider_stat'
}
local v1 = true
local v2 = true
for _,ws_v in pairs(wc_stat) do
self:_update_stat_pre(db, ws_v, time_key)
self:_update_stat_pre(db, ws_v, time_key_next)
v1 = self:_update_stat_pre(db, ws_v, time_key)
v2 = self:_update_stat_pre(db, ws_v, time_key_next)
end
if db and db:isopen() then
db:execute([[COMMIT]])
db:close()
end
if not v1 or not v2 then
return false
end
end
return true
end
-- 后台任务
@@ -273,7 +282,17 @@ function _M.cron(self)
return true
end
self:cronPre()
-- self:D("dedebide:cron task is busy!")
local ready_ok = self:cronPre()
if not ready_ok then
self:D("cron task is busy!")
return true
end
local cron_key = 'cron_every_1s'
if self:is_working(cron_key) then
return true
end
ngx.update_time()
local begin = ngx.now()
@@ -316,17 +335,13 @@ function _M.cron(self)
end
end
local cron_key = 'cron_every_1s'
if self:is_working(cron_key) then
return true
end
self:lock_working(cron_key)
-- 每秒100条
for i=1,llen do
local data, _ = ngx.shared.mw_total:lpop(total_key)
if not data then
self:unlock_working(cron_key)
break
end
@@ -338,10 +353,17 @@ function _M.cron(self)
local db = dbs[input_sn]
local stat_fields_is = stat_fields[input_sn]
if not db then
ngx.shared.mw_total:rpush(total_key, data)
self:unlock_working(cron_key)
break
end
self:store_logs_line(db, stmts[input_sn]["web_logs"], input_sn, info)
local insert_ok = self:store_logs_line(db, stmts[input_sn]["web_logs"], input_sn, info)
if not insert_ok then
ngx.shared.mw_total:rpush(total_key, data)
self:unlock_working(cron_key)
break
end
local excluded = info["log_kv"]['excluded']
local stat_tmp_fields = info['stat_fields']
@@ -514,10 +536,8 @@ function _M.store_logs_line(self, db, stmt, input_sn, info)
local request_headers = logline["request_headers"]
local excluded = logline["excluded"]
-- self:D("json:"..json.encode(logline))
local time_key = logline["time_key"]
if not excluded then
stmt:bind_names {
time=time,
ip=ip,
@@ -539,13 +559,15 @@ function _M.store_logs_line(self, db, stmt, input_sn, info)
local res, err = stmt:step()
if tostring(res) == "5" then
self:D("step res:"..tostring(res) ..",step err:"..tostring(err))
self:D("the step database connection is busy, so it will be stored later.")
-- self:D("json:"..json.encode(logline))
-- self:D("the step database connection is busy, so it will be stored later | step res:"..tostring(res) ..",step err:"..tostring(err))
if stmt then
stmt:reset()
end
return false
end
stmt:reset()
end
return true
end
@@ -653,9 +675,14 @@ end
function _M._update_stat_pre(self, db, stat_table, key)
local local_sql = string.format("INSERT INTO %s(time) SELECT :time WHERE NOT EXISTS(SELECT time FROM %s WHERE time=:time);", stat_table, stat_table)
local update_stat_stmt = db:prepare(local_sql)
update_stat_stmt:bind_names{time=key}
update_stat_stmt:step()
update_stat_stmt:finalize()
if update_stat_stmt then
update_stat_stmt:bind_names{time=key}
update_stat_stmt:step()
update_stat_stmt:finalize()
return true
end
return false
end
function _M.update_stat_quick(self, db, stat_table, key,columns)
@@ -1084,6 +1111,7 @@ end
function _M.get_client_ip(self)
local client_ip = "unknown"
local cdn = auto_config['cdn']
local request_header = ngx.req.get_headers()
if cdn == true then
for _,v in ipairs(auto_config['cdn_headers']) do
if request_header[v] ~= nil and request_header[v] ~= "" then
+2 -1
View File
@@ -134,6 +134,7 @@ log_by_lua_block {
local function exclude_url()
if not ngx.var.uri then return false end
if not ngx.var.request_uri then return false end
if not auto_config['exclude_url'] then return false end
local the_uri = string.sub(ngx.var.request_uri, 2)
local url_conf = auto_config["exclude_url"]
@@ -441,7 +442,7 @@ log_by_lua_block {
local push_data = json.encode(data)
-- C:D(json.encode(push_data))
local key = C:getTotalKey()
ngx.shared.mw_total:rpush(key, push_data)
ngx.shared.mw_total:rpush(key, push_data)
end
local function store_logs_line(db, stmt, input_server_name, lineno)
+6
View File
@@ -85,6 +85,7 @@ def migrateSiteHotLogs(site_name, query_date):
import shutil
print("coping {} to {} ...".format(hot_db, hot_db_tmp))
mw.writeFile(migrating_flag, "yes")
time.sleep(3)
shutil.copy(hot_db, hot_db_tmp)
if not os.path.exists(hot_db_tmp):
return mw.returnMsg(False, "migrating fail, copy tmp file!")
@@ -182,6 +183,11 @@ def migrateSiteHotLogs(site_name, query_date):
os.remove(hot_db_tmp)
print("{} logs migrate ok.".format(site_name))
if not mw.isAppleSystem():
mw.execShell("chown -R www:www " + getServerDir())
mw.opWeb('restart')
return mw.returnMsg(True, "{} logs migrate ok".format(site_name))

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