Compare commits

...

926 Commits

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

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

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

Signed-off-by: dependabot[bot] <support@github.com>
2022-12-26 20:58:18 +00:00
0d2721523c up 2022-12-25 22:43:34 +08:00
f042665c05 Update install.sh 2022-12-25 18:27:27 +08:00
0118548c24 up 2022-12-24 22:32:14 +08:00
1ba6625525 Update libsodium.sh 2022-12-24 22:29:33 +08:00
293f8ab0af up 2022-12-24 22:21:45 +08:00
dc68868883 Update sodium.sh 2022-12-24 21:57:40 +08:00
30f835b6b8 Update sodium.sh 2022-12-24 21:53:10 +08:00
4d90fdfe4f Update install.sh 2022-12-21 22:28:40 +08:00
bb4067aa6b Update install.sh 2022-12-21 22:25:36 +08:00
5dbf89b872 Update README.md 2022-12-21 22:11:54 +08:00
cc244a8ea5 Update README.md 2022-12-21 21:39:57 +08:00
Mr Chen
975cb988f7 Merge pull request #310 from midoks/dev
0.12.0
2022-12-21 21:38:48 +08:00
5a11d1bff9 Update README.md 2022-12-21 21:36:03 +08:00
bab3501722 0.12.0 2022-12-21 21:26:40 +08:00
872d68e0f1 Update config_api.py 2022-12-21 21:15:16 +08:00
9a5878a90c #309 先注释 2022-12-21 21:01:04 +08:00
e5e226b57a Update rhel.sh 2022-12-21 14:28:20 +08:00
5d12218a3e Update rhel.sh 2022-12-20 13:40:56 +08:00
7b3bbc8bdd Update install_dev.sh 2022-12-20 13:33:59 +08:00
67030e91f6 Update rhel.sh 2022-12-20 13:14:35 +08:00
c03eb0aa40 Update rhel.sh 2022-12-20 13:10:18 +08:00
2b5aaba0b7 Update install_dev.sh 2022-12-20 13:04:55 +08:00
c7a01b8c52 Update rhel.sh 2022-12-20 13:02:45 +08:00
9bfb72ada5 Update rhel.sh 2022-12-20 12:59:47 +08:00
5ab8936f6b Update phplib.conf 2022-12-20 12:01:43 +08:00
43c5161085 Update sodium.sh 2022-12-20 11:53:38 +08:00
7d90a6e99f Update libsodium.sh 2022-12-20 11:35:23 +08:00
9ba8028944 up 2022-12-20 11:26:49 +08:00
36fa3d7630 Update libsodium.sh 2022-12-20 11:20:31 +08:00
b6a2d7e3e8 up 2022-12-20 11:18:21 +08:00
99e73d1bda libsodium 2022-12-20 00:04:24 +08:00
1083b46078 Update rhel.sh 2022-12-19 23:59:04 +08:00
8bd081804d Update index.py 2022-12-19 23:54:28 +08:00
2536a79481 update 2022-12-19 20:28:12 +08:00
eab11680e5 up 2022-12-19 19:54:09 +08:00
651b7d6d1b Update index.py 2022-12-19 18:29:42 +08:00
75e181116e #307 2022-12-19 18:10:57 +08:00
c0a981b5a1 Update crontab.js 2022-12-19 14:38:20 +08:00
61dc601d48 Update crontab_api.py 2022-12-19 14:30:25 +08:00
415a2a1f8f #307 2022-12-19 14:27:29 +08:00
7c06a85ac7 Update crontab_api.py 2022-12-19 14:17:08 +08:00
7c8329bb78 Update crontab_api.py 2022-12-19 14:04:27 +08:00
5c8a50429f Update crontab_api.py 2022-12-19 13:50:04 +08:00
300bdc1eb1 up 2022-12-19 13:47:37 +08:00
a8a9e9033e 文件备份OK 2022-12-18 22:44:59 +08:00
77425dfc48 up 2022-12-18 21:11:15 +08:00
52eae4625f Update site_api.py 2022-12-18 15:48:31 +08:00
dafcf60633 Update site_api.py 2022-12-18 15:47:38 +08:00
79c1e81d2c up 2022-12-18 15:46:12 +08:00
b779a18de6 up 2022-12-18 03:15:39 +08:00
45f22d239a Update install.sh 2022-12-18 01:15:59 +08:00
f7b688d6bf 站点分类问题 #300 2022-12-18 00:54:26 +08:00
6f069c715a php-apt 8.2 2022-12-18 00:10:37 +08:00
98ed286b50 up 2022-12-17 23:57:05 +08:00
e931fa526f up 2022-12-17 23:50:21 +08:00
ff17610732 Update crontab_api.py 2022-12-17 23:23:00 +08:00
13bbe07ac5 Update site_api.py 2022-12-17 22:28:22 +08:00
5380cd805c up 2022-12-17 22:19:59 +08:00
c0b056c18d Update index.py 2022-12-17 22:10:01 +08:00
e287086b36 Update index.py 2022-12-17 21:58:07 +08:00
8647300e16 Update index.py 2022-12-17 21:48:36 +08:00
636f7af8ef up 2022-12-17 21:34:16 +08:00
87cf208dfc mysql 2022-12-17 21:28:31 +08:00
579a84c6f1 Update index.py 2022-12-17 21:14:34 +08:00
54b90f4484 Update index.py 2022-12-17 21:11:20 +08:00
ebdaf5d395 Update index.py 2022-12-17 21:06:12 +08:00
1e5ecabbd1 up 2022-12-17 20:52:35 +08:00
0596dc243e Update index.py 2022-12-17 20:49:18 +08:00
7fa6ec7cd3 up 2022-12-17 20:46:02 +08:00
c21d8eed8b Update index.py 2022-12-17 20:38:31 +08:00
326f9f0e6b Update index.py 2022-12-17 20:33:53 +08:00
60c94f120e Update index.py 2022-12-17 20:32:50 +08:00
55c08a8916 Update index.py 2022-12-17 20:11:21 +08:00
cf944fb976 Update index.py 2022-12-17 20:02:27 +08:00
cda1ea93a8 Update config_api.py 2022-12-17 20:01:39 +08:00
46f02cae0d Update crontab.js 2022-12-17 20:00:10 +08:00
034d00a262 Update config_api.py 2022-12-17 18:59:28 +08:00
fe1a68208b Update crontab.js 2022-12-17 18:59:17 +08:00
f364cc4e2d Update config_api.py 2022-12-17 18:56:42 +08:00
5b35b5f38c Update crontab.js 2022-12-17 18:56:21 +08:00
2151b2c597 Update index.py 2022-12-17 18:48:29 +08:00
4f8e95210f Update index.py 2022-12-17 18:02:20 +08:00
b31297b47f Update crontab_api.py 2022-12-17 17:57:10 +08:00
e492a87160 Update crontab_api.py 2022-12-17 17:42:30 +08:00
47d6eb7993 Update crontab.js 2022-12-17 17:21:04 +08:00
3ddccea897 Update config_api.py 2022-12-17 17:17:35 +08:00
069d594e49 up 2022-12-17 17:16:57 +08:00
7c49609a2b Update config_api.py 2022-12-17 17:10:01 +08:00
70d260dd5a update 2022-12-17 17:00:19 +08:00
58503722c0 Update crontab_api.py 2022-12-17 16:02:51 +08:00
0a98b4dc09 Update crontab_api.py 2022-12-17 15:45:04 +08:00
5ab0f493e4 Update crontab_api.py 2022-12-17 14:06:51 +08:00
30580874fd Update config_api.py 2022-12-17 14:04:01 +08:00
26327d958c Update crontab_api.py 2022-12-17 14:02:36 +08:00
522eddd393 Update crontab.js 2022-12-17 13:30:54 +08:00
935df8d4d1 Update config_api.py 2022-12-17 11:36:13 +08:00
15b4ba9cbb up 2022-12-17 11:34:37 +08:00
c005e1f56d Update debian.sh 2022-12-17 09:58:56 +08:00
edbca430ee Update debian.sh 2022-12-17 09:55:48 +08:00
f8fedb8285 Update crontab.js 2022-12-16 01:09:59 +08:00
d9472709fd up 2022-12-16 01:02:05 +08:00
79bf1eae07 Update config_api.py 2022-12-16 00:23:24 +08:00
fab6df7097 up 2022-12-16 00:12:53 +08:00
b9416d58f9 Update info.json 2022-12-16 00:03:29 +08:00
96d86ce49e Update info.json 2022-12-16 00:01:41 +08:00
46f2d9f029 Update plugins_api.py 2022-12-15 23:47:06 +08:00
9d5fb77123 Update plugins_api.py 2022-12-15 23:41:56 +08:00
d0c903bf11 up 2022-12-15 20:03:00 +08:00
3c1b0e6708 Update crontab_api.py 2022-12-15 20:02:49 +08:00
340b72591a Merge branch 'dev' of github.com:midoks/mdserver-web into dev 2022-12-15 18:10:49 +08:00
8f6a194a02 Update debug.sh 2022-12-15 18:06:11 +08:00
Mr Chen
7e65993978 Merge pull request #304 from hiCasper/dev
Revert changes
2022-12-15 18:03:27 +08:00
hiCasper
ca27a2d49d Revert changes 2022-12-15 17:53:17 +08:00
Mr Chen
b941c9c71b Merge pull request #302 from hiCasper/dev
Dev
2022-12-15 17:05:19 +08:00
hiCasper
e68c7b0346 Change log path 2022-12-14 14:17:44 +08:00
c7c3d8fdb9 Create jquery.qrcode.js 2022-12-13 16:15:06 +08:00
hiCasper
de383e0584 Fix 2022-12-13 15:12:44 +08:00
048463cf9d 修改名字 2022-12-13 14:45:29 +08:00
hiCasper
0c118613d5 Add more install logs 2022-12-13 14:39:04 +08:00
hiCasper
aa8e9f173b Improve OS detection 2022-12-13 14:34:10 +08:00
hiCasper
c21c54198c Better way installing deps 2022-12-13 13:42:42 +08:00
Mr Chen
e8ad9995d7 Merge pull request #299 from hiCasper/dev
Fix problems
2022-12-13 13:31:38 +08:00
hiCasper
7a0b6be401 Merge branch 'dev' of https://github.com/hiCasper/mdserver-web into dev 2022-12-13 13:09:57 +08:00
hiCasper
8b2a127ceb Add dev install hint 2022-12-13 13:09:15 +08:00
hiCasper
8a23fb18ff Rollback 2022-12-13 13:08:48 +08:00
hiCasper
234eca3729 Merge branch 'dev' of https://github.com/midoks/mdserver-web into dev 2022-12-13 13:05:22 +08:00
hiCasper
9c74eb6a77 Add dev version hint 2022-12-13 12:59:43 +08:00
hiCasper
0093af2661 Sync upstream 2022-12-13 12:45:38 +08:00
hiCasper
482d9bdd86 Fix locale error 2022-12-13 02:28:40 +08:00
hiCasper
d8f9b179ad Add hint 2022-12-13 01:49:51 +08:00
hiCasper
d179328976 Revert and fix deps issue 2022-12-13 01:36:25 +08:00
hiCasper
9aaa5e2a65 Log installation to file 2022-12-13 01:36:25 +08:00
hiCasper
821a9ce2d1 Fix acme.sh deps 2022-12-13 00:50:38 +08:00
7b39b28dcd debian系列回退 2022-12-13 00:08:15 +08:00
c881ad7d18 Update site_api.py 2022-12-12 23:30:57 +08:00
82da5c2649 Create sub.conf.tpl 2022-12-10 19:54:17 +08:00
Mr Chen
315ba990ec Merge pull request #292 from hiCasper/dev
Optimize install scripts
2022-12-10 15:23:55 +08:00
hiCasper
992fcb7e6f Optimize OS detection 2022-12-10 14:51:19 +08:00
hiCasper
d7337239f5 Optimize acme.sh install 2022-12-10 14:40:06 +08:00
hiCasper
224b91fe0b Fix install scripts 2022-12-10 14:02:05 +08:00
Mr Chen
589dfe6da2 Merge pull request #291 from hiCasper/dev
安装脚本等优化
2022-12-10 11:58:21 +08:00
hiCasper
b1b47c3c38 Combine install scripts 2022-12-10 03:01:31 +08:00
hiCasper
106f5d897d Skip missing packages 2022-12-10 02:59:16 +08:00
hiCasper
c856d09b3b Speed up install 2022-12-10 02:59:16 +08:00
hiCasper
6bf8792bb6 Fix pip location 2022-12-10 02:59:15 +08:00
hiCasper
75edb074b2 Change pip source 2022-12-10 02:37:33 +08:00
hiCasper
4a5df99f06 Fix dnf dependencies 2022-12-10 02:37:33 +08:00
hiCasper
2c7536a138 Fix typo 2022-12-10 02:09:28 +08:00
hiCasper
a758fc2264 Fix pip source 2022-12-10 02:09:28 +08:00
hiCasper
0d94fe2d26 Fix yum error 2022-12-10 02:09:28 +08:00
hiCasper
630bdd5f96 Fix error 2022-12-10 02:09:27 +08:00
hiCasper
411fc670fc Combine install scripts 2022-12-10 02:09:27 +08:00
hiCasper
cab35c7c28 Fix iptables detection 2022-12-10 02:09:27 +08:00
hiCasper
b3c8a6365f Using whitenoise to serving static files 2022-12-10 02:09:27 +08:00
Mr Chen
b64f02ff2f Merge pull request #290 from midoks/dev
0.11.4
2022-12-10 01:52:51 +08:00
7e9df9640e PHP82正式释出。 2022-12-10 01:48:52 +08:00
6c478be4df Update fileinfo.sh 2022-12-09 22:38:53 +08:00
e919fac1d5 Update fileinfo.sh 2022-12-09 22:38:21 +08:00
4854142f9a Update fileinfo.sh 2022-12-09 22:24:56 +08:00
f37de275ba Update fileinfo.sh 2022-12-09 22:19:17 +08:00
0c8f777452 Update phplib.conf 2022-12-09 20:23:52 +08:00
9235affadf Update phalcon.sh 2022-12-09 20:06:43 +08:00
2001a3b5dc Update install.sh 2022-12-09 19:49:24 +08:00
6ba1e4c19c Update yaf.sh 2022-12-09 19:44:58 +08:00
231048a4a3 Update phalcon.sh 2022-12-09 19:42:08 +08:00
6b9aab3759 Update fileinfo.sh 2022-12-09 19:22:32 +08:00
e8a152b1a3 Update install.sh 2022-12-09 19:15:58 +08:00
1cf8e4ecb7 Update fileinfo.sh 2022-12-09 19:11:55 +08:00
aa04235446 up 2022-12-09 19:03:16 +08:00
3acbc36297 Update tools.py 2022-12-09 18:34:36 +08:00
85aba4f1ce Update tools.py 2022-12-09 18:32:32 +08:00
f33f80cf34 Update mw.tpl 2022-12-09 18:04:33 +08:00
1868b3197d Update tools.py 2022-12-09 18:04:07 +08:00
b8e1fb2ca2 Update install.sh 2022-12-09 15:05:41 +08:00
9aa9c4f799 删除任务时删除日志 2022-12-09 12:16:32 +08:00
344603f1b2 Update redis.sh 2022-12-08 19:06:49 +08:00
71b43e9f2f Update redis.sh 2022-12-08 18:53:46 +08:00
adba50bfc1 Update redis.sh 2022-12-08 18:52:52 +08:00
Mr Chen
d917fa76f6 Merge pull request #286 from midoks/dev
0.11.3
2022-12-08 00:07:48 +08:00
32971ccead 0.11.3 2022-12-08 00:06:50 +08:00
c7a3481b95 #285
观察细致入微。
2022-12-07 22:56:18 +08:00
8915b672e5 Update config_api.py 2022-12-07 19:35:35 +08:00
e472d104d1 Update nginx_panel.conf 2022-12-07 16:33:44 +08:00
48ee5574c7 Update mw.py 2022-12-07 15:31:47 +08:00
706afbd681 Update mw.py 2022-12-07 15:31:32 +08:00
70ca6d0459 Update mw.py 2022-12-07 15:12:18 +08:00
f344a32fa7 Update mw.py 2022-12-07 15:09:43 +08:00
e6123000ac Update nginx_panel.conf 2022-12-07 12:56:30 +08:00
b94361c59f Update config_api.py 2022-12-07 00:37:28 +08:00
4a7efb67d4 Update config_api.py 2022-12-07 00:22:02 +08:00
536b7ac997 Update config_api.py 2022-12-07 00:20:31 +08:00
0f5b055aeb 面板ssl 2022-12-07 00:14:22 +08:00
7d81a59c8f Update config_api.py 2022-12-06 23:42:23 +08:00
2540cd1308 Update config_api.py 2022-12-06 23:40:41 +08:00
5358cd2d38 up 2022-12-06 23:38:09 +08:00
c4bb20ed54 Update files_api.py 2022-12-06 20:37:07 +08:00
7f2f307131 Update files_api.py 2022-12-06 20:30:02 +08:00
7a8ef9fe00 Update files_api.py 2022-12-06 20:19:06 +08:00
e1abc66179 Update setting.py 2022-12-06 18:00:28 +08:00
8bda20c3d0 Update config_api.py 2022-12-06 17:50:49 +08:00
3c01838df1 Update .gitignore 2022-12-06 17:38:42 +08:00
3b459e0349 panel ssl 测试中 2022-12-06 17:35:23 +08:00
d3e7684f04 ssl update 2022-12-06 16:27:24 +08:00
b2e6e7f256 Update firewall_api.py 2022-12-06 01:25:53 +08:00
ca6812561a Update mw.py 2022-12-06 01:03:25 +08:00
44e8079db6 Update README.md 2022-12-05 15:08:39 +08:00
9bd063cc1d Update phalcon.sh 2022-12-04 16:10:26 +08:00
1b0044e28a Update index.py 2022-12-04 15:58:04 +08:00
9b752cf24f up 2022-12-04 15:57:33 +08:00
cf5f98958c Update phplib.conf 2022-12-04 12:19:11 +08:00
c46872dab5 Update phplib.conf 2022-12-04 12:13:14 +08:00
b57edb7b87 加入phalcon 2022-12-04 12:12:26 +08:00
595cc30c6f Update swoole.sh 2022-12-04 11:42:41 +08:00
4b6f5a5f4e Update swoole.sh 2022-12-04 11:29:53 +08:00
741da10906 Update swoole.sh 2022-12-04 11:29:26 +08:00
9965786976 Update README.md 2022-12-04 11:24:15 +08:00
f083696131 Update install.sh 2022-12-04 11:24:09 +08:00
dd2105d9ab 文件搜索优化 2022-12-04 00:57:03 +08:00
c1574f3430 up 2022-12-03 15:56:58 +08:00
6394ead5b8 解除域名绑定成功 2022-12-03 13:27:24 +08:00
951 changed files with 356 additions and 139218 deletions

View File

@@ -11,8 +11,7 @@ Debian x, Centos x, Centos x stream, Ubuntu x?
Ex:
```bash
................start mw success
bash: line 149: ./scripts/init.d/mw: No such file or directory
一个issue,一个问题。多余问题,视情况而答。
```
## 错误截图 ?

18
.gitignore vendored
View File

@@ -106,6 +106,7 @@ venv.bak/
.idea/*.iml
tmp/*
*.swp
*.pem
*.zip
logs/*
data/*.db
@@ -116,13 +117,15 @@ data/json/config.json
data/site.pl
data/admin_path.pl
data/close.pl
ssl/input.pl
data/datadir.pl
data/502Task.pl
data/default.pl
data/backup.pl
data/debug.pl
data/default_site.pl
ssl/input.pl
lib/python*
bin
@@ -148,11 +151,21 @@ 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
plugins/zimg
@@ -161,6 +174,9 @@ plugins/mail
plugins/fastdfs
plugins/v2ray
plugins/frp
plugins/file_search
debug.out

1
CNAME Normal file
View File

@@ -0,0 +1 @@
www.midoks.icu

201
LICENSE
View File

@@ -1,201 +0,0 @@
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 [midoks] [midoks 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.

194
README.md
View File

@@ -1,194 +0,0 @@
<p align="center">
<img alt="logo" src="https://cdn.jsdelivr.net/gh/midoks/mdserver-web@latest/route/static/logo.png" height="140" />
<h3 align="center">mdserver-web</h3>
<p align="center">一款简单Linux面板服务</p>
</p>
### 简介
简单的Linux面板,感谢BT.CN写出如此好的web管理软件。我一看到,就知道这是我一直想要的页面化管理方式。
复制了后台管理界面,按照自己想要的方式写了一版。
![CentOS](https://img.shields.io/badge/LINUX-CentOS-blue?style=for-the-badge&logo=CentOS)
![Ubuntu](https://img.shields.io/badge/LINUX-Ubuntu-blue?style=for-the-badge&logo=Ubuntu)
![Debian](https://img.shields.io/badge/LINUX-Debian-blue?style=for-the-badge&logo=Debian)
![Fedora](https://img.shields.io/badge/LINUX-Fedora-blue?style=for-the-badge&logo=Fedora)
[![Wiki](https://img.shields.io/badge/MW-Wiki-red?style=for-the-badge&logo=wiki)](https://github.com/midoks/mdserver-web/wiki)
[![](https://data.jsdelivr.com/v1/package/gh/midoks/mdserver-web/badge?style=for-the-badge)](https://www.jsdelivr.com/package/gh/midoks/mdserver-web)
* SSH终端工具
* 面板收藏功能
* 网站子目录绑定
* 网站备份功能
* 插件方式管理
基本上可以使用,后续会继续优化!欢迎提供意见!
- 吹水组 - https://t.me/mdserver_web
- [兼容性测试报告](/compatibility.md)
- [常用命令说明](/cmd.md)
### 主要插件介绍
* OpenResty - 轻量级,占有内存少,并发能力强。
* PHP[53-82] - PHP是世界上最好的编程语言。
* MySQL - 一种关系数据库管理系统。
* MariaDB - 是MySQL的一个重要分支。
* MongoDB - 一种非关系NOSQL数据库管理系统。
* phpMyAdmin - 著名Web端MySQL管理工具。
* Memcached - 一个高性能的分布式内存对象缓存系统。
* Redis - 一个高性能的KV数据库。
* PureFtpd - 一款专注于程序健壮和软件安全的免费FTP服务器软件。
* Gogs - 一款极易搭建的自助Git服务。
* Rsyncd - 通用同步服务。
### 插件开发相关
```
插件文档还不完善,如果有不明白的地方提Issue! 我会尽力完善。
如果你自己写了插件,想分享出来,也可以提Issue。
```
- 简单例子 - https://github.com/mw-plugin/simple-plugin
- 插件地址 - https://github.com/mw-plugin
- 开发文档 - https://github.com/midoks/mdserver-web/wiki/插件开发
# Note
```
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]
```
# 特别赞助
- [找资源 - 阿里云盘资源搜索引擎](https://zhaoziyuan.la/)
- [奈飞丝 - 奈飞中国 Netflix MOD首发](https://naifeis.com/index.php#/register?code=k7P7V6Ur)
# AD - VPS推荐 - 🙏
- [ZZZ评测](https://www.zzzvps.com/)
| 服务商 | LOGO | 推广地址 | 优惠码 |
| ------------- |----------|-----------|-------|
| digitalvirt |[![digitalvirt](https://digitalvirt.com/templates/BlueWhite/img/logo-dark.svg)](https://digitalvirt.com/aff.php?aff=154) | https://digitalvirt.com/aff.php?aff=154 | mdserver-web |
# Docker
- 由[DDSRem](https://github.com/DDSRem)开发维护。
- https://hub.docker.com/r/ddsderek/mw-server
```
docker run -itd --name mw-server --privileged=true -p 7200:7200 -p 80:80 -p 443:443 -p 888:888 ddsderek/mw-server:latest
```
### 版本更新 0.11.2
* redis安全强化。
* 添加php命令行设置。
* 域名绑定。
* Gitea优化。
* 网站统计优化。
* centos9下php-gd安装优化。
* php82rc7测试版。
### JSDelivr安装地址
- 初始安装
```
curl -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
```
- 卸载脚本
```
wget -O uninstall.sh https://cdn.jsdelivr.net/gh/midoks/mdserver-web@latest/scripts/uninstall.sh && bash uninstall.sh
```
### 备用地址
- 初始安装
```
curl -fsSL https://raw.githubusercontent.com/midoks/mdserver-web/master/scripts/install.sh | bash
```
- 直接更新
```
curl -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
```
### 通用软件安装[命令行安装]
- 需已经安装mdserver-web
```
curl -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
wget -O uninstall.sh https://raw.githubusercontent.com/midoks/mdserver-web/dev/scripts/uninstall.sh && bash uninstall.sh
curl -fsSL https://gitee.com/midoks/mdserver-web/raw/master/scripts/install_dev.sh | bash
curl -fsSL https://gitee.com/midoks/mdserver-web/raw/dev/scripts/update_dev.sh | bash
```
### 捐赠地址 USDT(TRC20)
TVbNgrpeGBGZVm5gTLa21ADP7RpnPFhjya
### 微信赞助
[![截图](https://cdn.jsdelivr.net/gh/midoks/mdserver-web@latest/route/static/img/weixin_zz.jpg)](https://cdn.jsdelivr.net/gh/midoks/mdserver-web@latest/route/static/img/weixin_zz.jpg)
### 无图不真相
[![截图](https://cdn.jsdelivr.net/gh/midoks/mdserver-web@latest/route/static/mdw.jpg)](https://cdn.jsdelivr.net/gh/midoks/mdserver-web@latest/route/static/mdw.jpg)
### Stargazers over time
[![Stargazers over time](https://starchart.cc/midoks/mdserver-web.svg)](https://starchart.cc/midoks/mdserver-web)
### 感谢开发赞助
[![digitalvirt](https://digitalvirt.com/templates/BlueWhite/img/logo-dark.svg)](https://digitalvirt.com/aff.php?aff=154)
### 授权许可
本项目采用 Apache 开源授权许可证,完整的授权说明已放置在 [LICENSE](https://github.com/midoks/mdserver-web/blob/master/LICENSE) 文件中。

1
ads.txt Normal file
View File

@@ -0,0 +1 @@
google.com, pub-7736695762519033, DIRECT, f08c47fec0942fa0

49
app.py
View File

@@ -1,49 +0,0 @@
# coding:utf-8
# ---------------------------------------------------------------------------------
# MW-Linux面板
# ---------------------------------------------------------------------------------
# copyright (c) 2018-∞(https://github.com/midoks/mdserver-web) All rights reserved.
# ---------------------------------------------------------------------------------
# Author: midoks <midoks@163.com>
# ---------------------------------------------------------------------------------
# ---------------------------------------------------------------------------------
# 入口文件
# ---------------------------------------------------------------------------------
# pip install profiler_online
# 性能测试
# from profiler_online import run_profiler
# run_profiler()
import sys
import io
import os
from route import app, socketio
from gevent.pywsgi import WSGIServer
from geventwebsocket.handler import WebSocketHandler
from gevent import monkey
monkey.patch_all()
try:
if __name__ == "__main__":
PORT = 7200
if os.path.exists('data/port.pl'):
f = open('data/port.pl')
PORT = int(f.read())
f.close()
HOST = '0.0.0.0'
http_server = WSGIServer(
(HOST, PORT), app, handler_class=WebSocketHandler)
http_server.serve_forever()
socketio.run(app, host=HOST, port=PORT)
except Exception as ex:
print(ex)

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,144 +0,0 @@
# coding: utf-8
# ---------------------------------------------------------------------------------
# MW-Linux面板
# ---------------------------------------------------------------------------------
# copyright (c) 2018-∞(https://github.com/midoks/mdserver-web) All rights reserved.
# ---------------------------------------------------------------------------------
# Author: midoks <midoks@163.com>
# ---------------------------------------------------------------------------------
# ---------------------------------------------------------------------------------
# 公共操作
# ---------------------------------------------------------------------------------
import os
import sys
import time
import string
import json
import hashlib
import shlex
import datetime
import subprocess
import re
import hashlib
from random import Random
import mw
import db
from flask import redirect
def init():
initDB()
initUserInfo()
initInitD()
initInitTask()
def local():
result = checkClose()
if result:
return result
# 检查面板是否关闭
def checkClose():
if os.path.exists('data/close.pl'):
return redirect('/close')
def initDB():
try:
sql = db.Sql().dbfile('default')
csql = mw.readFile('data/sql/default.sql')
csql_list = csql.split(';')
for index in range(len(csql_list)):
sql.execute(csql_list[index], ())
except Exception as ex:
print(str(ex))
def doContentReplace(src, dst):
content = mw.readFile(src)
content = content.replace("{$SERVER_PATH}", mw.getRunDir())
mw.writeFile(dst, content)
def initInitD():
# systemctl
# sysCfgDir = mw.systemdCfgDir()
# if os.path.exists(sysCfgDir) and mw.getOsName() == 'centos' and mw.getOsID() == '9':
# systemd_mw = sysCfgDir + '/mw.service'
# systemd_mw_task = sysCfgDir + '/mw-task.service'
# systemd_mw_tpl = mw.getRunDir() + '/scripts/init.d/mw.service.tpl'
# systemd_mw_task_tpl = mw.getRunDir() + '/scripts/init.d/mw-task.service.tpl'
# if os.path.exists(systemd_mw):
# os.remove(systemd_mw)
# if os.path.exists(systemd_mw_task):
# os.remove(systemd_mw_task)
# doContentReplace(systemd_mw_tpl, systemd_mw)
# doContentReplace(systemd_mw_task_tpl, systemd_mw_task)
# mw.execShell('systemctl enable mw')
# mw.execShell('systemctl enable mw-task')
# mw.execShell('systemctl daemon-reload')
script = mw.getRunDir() + '/scripts/init.d/mw.tpl'
script_bin = mw.getRunDir() + '/scripts/init.d/mw'
doContentReplace(script, script_bin)
mw.execShell('chmod +x ' + script_bin)
# 在linux系统中,确保/etc/init.d存在
if not mw.isAppleSystem() and not os.path.exists("/etc/rc.d/init.d"):
mw.execShell('mkdir -p /etc/rc.d/init.d')
if not mw.isAppleSystem() and not os.path.exists("/etc/init.d"):
mw.execShell('mkdir -p /etc/init.d')
# initd
if os.path.exists('/etc/rc.d/init.d'):
initd_bin = '/etc/rc.d/init.d/mw'
if not os.path.exists(initd_bin):
import shutil
shutil.copyfile(script_bin, initd_bin)
mw.execShell('chmod +x ' + initd_bin)
# 加入自启动
mw.execShell('which chkconfig && chkconfig --add mw')
if os.path.exists('/etc/init.d'):
initd_bin = '/etc/init.d/mw'
if not os.path.exists(initd_bin):
import shutil
shutil.copyfile(script_bin, initd_bin)
mw.execShell('chmod +x ' + initd_bin)
# 加入自启动
mw.execShell('which update-rc.d && update-rc.d -f mw defaults')
# 获取系统IPV4
mw.setHostAddr(mw.getLocalIp())
def initInitTask():
# 创建证书同步命令
import cert_api
api = cert_api.cert_api()
api.createCertCron()
def initUserInfo():
data = mw.M('users').where('id=?', (1,)).getField('password')
if data == '21232f297a57a5a743894a0e4a801fc3':
pwd = mw.getRandomString(8).lower()
file_pw = mw.getRunDir() + '/data/default.pl'
mw.writeFile(file_pw, pwd)
mw.M('users').where('id=?', (1,)).setField(
'password', mw.md5(pwd))

View File

@@ -1,653 +0,0 @@
# coding: utf-8
# ---------------------------------------------------------------------------------
# MW-Linux面板
# ---------------------------------------------------------------------------------
# copyright (c) 2018-∞(https://github.com/midoks/mdserver-web) All rights reserved.
# ---------------------------------------------------------------------------------
# Author: midoks <midoks@163.com>
# ---------------------------------------------------------------------------------
# ---------------------------------------------------------------------------------
# 配置操作
# ---------------------------------------------------------------------------------
import psutil
import time
import os
import sys
import mw
import re
import json
import pwd
from flask import session
from flask import request
class config_api:
__version = '0.11.2'
__api_addr = 'data/api.json'
def __init__(self):
pass
def getVersion(self):
return self.__version
##### ----- start ----- ###
# 取面板列表
def getPanelListApi(self):
data = mw.M('panel').field(
'id,title,url,username,password,click,addtime').order('click desc').select()
return mw.getJson(data)
def addPanelInfoApi(self):
title = request.form.get('title', '')
url = request.form.get('url', '')
username = request.form.get('username', '')
password = request.form.get('password', '')
# 校验是还是重复
isAdd = mw.M('panel').where(
'title=? OR url=?', (title, url)).count()
if isAdd:
return mw.returnJson(False, '备注或面板地址重复!')
isRe = mw.M('panel').add('title,url,username,password,click,addtime',
(title, url, username, password, 0, int(time.time())))
if isRe:
return mw.returnJson(True, '添加成功!')
return mw.returnJson(False, '添加失败!')
# 删除面板资料
def delPanelInfoApi(self):
mid = request.form.get('id', '')
isExists = mw.M('panel').where('id=?', (mid,)).count()
if not isExists:
return mw.returnJson(False, '指定面板资料不存在!')
mw.M('panel').where('id=?', (mid,)).delete()
return mw.returnJson(True, '删除成功!')
# 修改面板资料
def setPanelInfoApi(self):
title = request.form.get('title', '')
url = request.form.get('url', '')
username = request.form.get('username', '')
password = request.form.get('password', '')
mid = request.form.get('id', '')
# 校验是还是重复
isSave = mw.M('panel').where(
'(title=? OR url=?) AND id!=?', (title, url, mid)).count()
if isSave:
return mw.returnJson(False, '备注或面板地址重复!')
# 更新到数据库
isRe = mw.M('panel').where('id=?', (mid,)).save(
'title,url,username,password', (title, url, username, password))
if isRe:
return mw.returnJson(True, '修改成功!')
return mw.returnJson(False, '修改失败!')
def setPanelDomainApi(self):
domain = request.form.get('domain', '')
cfg_domain = 'data/bind_domain.pl'
if domain == '':
os.remove(cfg_domain)
return mw.returnJson(True, '清空域名成功!')
reg = r"^([\w\-\*]{1,100}\.){1,4}(\w{1,10}|\w{1,10}\.\w{1,10})$"
if not re.match(reg, domain):
return mw.returnJson(False, '主域名格式不正确')
mw.writeFile(cfg_domain, domain)
return mw.returnJson(True, '设置域名成功!')
def syncDateApi(self):
if mw.isAppleSystem():
return mw.returnJson(True, '开发系统不必同步时间!')
data = mw.execShell('ntpdate -s time.nist.gov')
if data[0] == '':
return mw.returnJson(True, '同步成功!')
return mw.returnJson(False, '同步失败:' + data[0])
def setPasswordApi(self):
password1 = request.form.get('password1', '')
password2 = request.form.get('password2', '')
if password1 != password2:
return mw.returnJson(False, '两次输入的密码不一致,请重新输入!')
if len(password1) < 5:
return mw.returnJson(False, '用户密码不能小于5位!')
mw.M('users').where("username=?", (session['username'],)).setField(
'password', mw.md5(password1.strip()))
return mw.returnJson(True, '密码修改成功!')
def setNameApi(self):
name1 = request.form.get('name1', '')
name2 = request.form.get('name2', '')
if name1 != name2:
return mw.returnJson(False, '两次输入的用户名不一致,请重新输入!')
if len(name1) < 3:
return mw.returnJson(False, '用户名长度不能少于3位')
mw.M('users').where("username=?", (session['username'],)).setField(
'username', name1.strip())
session['username'] = name1
return mw.returnJson(True, '用户修改成功!')
def setWebnameApi(self):
webname = request.form.get('webname', '')
if webname != mw.getConfig('title'):
mw.setConfig('title', webname)
return mw.returnJson(True, '面板别名保存成功!')
def setPortApi(self):
port = request.form.get('port', '')
if port != mw.getHostPort():
import system_api
import firewall_api
if os.path.exists("/lib/systemd/system/firewalld.service"):
if not firewall_api.firewall_api().getFwStatus():
return mw.returnJson(False, 'firewalld必须先启动!')
mw.setHostPort(port)
msg = mw.getInfo('放行端口[{1}]成功', (port,))
mw.writeLog("防火墙管理", msg)
addtime = time.strftime('%Y-%m-%d %X', time.localtime())
mw.M('firewall').add('port,ps,addtime', (port, "配置修改", addtime))
firewall_api.firewall_api().addAcceptPort(port)
firewall_api.firewall_api().firewallReload()
system_api.system_api().restartMw()
return mw.returnJson(True, '端口保存成功!')
def setIpApi(self):
host_ip = request.form.get('host_ip', '')
if host_ip != mw.getHostAddr():
mw.setHostAddr(host_ip)
return mw.returnJson(True, 'IP保存成功!')
def setWwwDirApi(self):
sites_path = request.form.get('sites_path', '')
if sites_path != mw.getWwwDir():
mw.setWwwDir(sites_path)
return mw.returnJson(True, '修改默认建站目录成功!')
def setBackupDirApi(self):
backup_path = request.form.get('backup_path', '')
if backup_path != mw.getBackupDir():
mw.setBackupDir(backup_path)
return mw.returnJson(True, '修改默认备份目录成功!')
def setBasicAuthApi(self):
basic_user = request.form.get('basic_user', '').strip()
basic_pwd = request.form.get('basic_pwd', '').strip()
basic_open = request.form.get('is_open', '').strip()
salt = '_md_salt'
path = 'data/basic_auth.json'
is_open = True
if basic_open == 'false':
if os.path.exists(path):
os.remove(path)
return mw.returnJson(True, '删除BasicAuth成功!')
if basic_user == '' or basic_pwd == '':
return mw.returnJson(True, '用户和密码不能为空!')
ba_conf = None
if os.path.exists(path):
try:
ba_conf = json.loads(public.readFile(path))
except:
os.remove(path)
if not ba_conf:
ba_conf = {
"basic_user": mw.md5(basic_user + salt),
"basic_pwd": mw.md5(basic_pwd + salt),
"open": is_open
}
else:
ba_conf['basic_user'] = mw.md5(basic_user + salt)
ba_conf['basic_pwd'] = mw.md5(basic_pwd + salt)
ba_conf['open'] = is_open
mw.writeFile(path, json.dumps(ba_conf))
os.chmod(path, 384)
mw.writeLog('面板设置', '设置BasicAuth状态为: %s' % is_open)
mw.restartMw()
return mw.returnJson(True, '设置成功!')
def setApi(self):
webname = request.form.get('webname', '')
port = request.form.get('port', '')
host_ip = request.form.get('host_ip', '')
domain = request.form.get('domain', '')
sites_path = request.form.get('sites_path', '')
backup_path = request.form.get('backup_path', '')
if domain != '':
reg = "^([\w\-\*]{1,100}\.){1,4}(\w{1,10}|\w{1,10}\.\w{1,10})$"
if not re.match(reg, domain):
return mw.returnJson(False, '主域名格式不正确')
if int(port) >= 65535 or int(port) < 100:
return mw.returnJson(False, '端口范围不正确!')
if webname != mw.getConfig('title'):
mw.setConfig('title', webname)
if sites_path != mw.getWwwDir():
mw.setWwwDir(sites_path)
if backup_path != mw.getWwwDir():
mw.setBackupDir(backup_path)
if port != mw.getHostPort():
import system_api
import firewall_api
if os.path.exists("/lib/systemd/system/firewalld.service"):
if not firewall_api.firewall_api().getFwStatus():
return mw.returnJson(False, 'firewalld必须先启动!')
mw.setHostPort(port)
msg = mw.getInfo('放行端口[{1}]成功', (port,))
mw.writeLog("防火墙管理", msg)
addtime = time.strftime('%Y-%m-%d %X', time.localtime())
mw.M('firewall').add('port,ps,addtime', (port, "配置修改", addtime))
firewall_api.firewall_api().addAcceptPort(port)
firewall_api.firewall_api().firewallReload()
system_api.system_api().restartMw()
if host_ip != mw.getHostAddr():
mw.setHostAddr(host_ip)
mhost = mw.getHostAddr()
info = {
'uri': '/config',
'host': mhost + ':' + port
}
return mw.returnJson(True, '保存成功!', info)
def setAdminPathApi(self):
admin_path = request.form.get('admin_path', '').strip()
admin_path_checks = ['/', '/close', '/login',
'/do_login', '/site', '/sites',
'/download_file', '/control', '/crontab',
'/firewall', '/files', 'config',
'/soft', '/system', '/code',
'/ssl', '/plugins', '/hook']
if admin_path == '':
admin_path = '/'
if admin_path != '/':
if len(admin_path) < 6:
return mw.returnJson(False, '安全入口地址长度不能小于6位!')
if admin_path in admin_path_checks:
return mw.returnJson(False, '该入口已被面板占用,请使用其它入口!')
if not re.match("^/[\w\./-_]+$", admin_path):
return mw.returnJson(False, '入口地址格式不正确,示例: /mw_rand')
# else:
# domain = mw.readFile('data/bind_domain.pl')
# if not domain:
# domain = ''
# limitip = mw.readFile('data/bind_limitip.pl')
# if not limitip:
# limitip = ''
# if not domain.strip() and not limitip.strip():
# return mw.returnJson(False,
# '警告,关闭安全入口等于直接暴露你的后台地址在外网,十分危险,至少开启以下一种安全方式才能关闭:<a
# style="color:red;"><br>1、绑定访问域名<br>2、绑定授权IP</a>')
admin_path_file = 'data/admin_path.pl'
admin_path_old = '/'
if os.path.exists(admin_path_file):
admin_path_old = mw.readFile(admin_path_file).strip()
if admin_path_old != admin_path:
mw.writeFile(admin_path_file, admin_path)
mw.restartMw()
return mw.returnJson(True, '修改成功!')
def closePanelApi(self):
filename = 'data/close.pl'
if os.path.exists(filename):
os.remove(filename)
return mw.returnJson(True, '开启成功')
mw.writeFile(filename, 'True')
mw.execShell("chmod 600 " + filename)
mw.execShell("chown root.root " + filename)
return mw.returnJson(True, '面板已关闭!')
def openDebugApi(self):
filename = 'data/debug.pl'
if os.path.exists(filename):
os.remove(filename)
return mw.returnJson(True, '开发模式关闭!')
mw.writeFile(filename, 'True')
return mw.returnJson(True, '开发模式开启!')
def setIpv6StatusApi(self):
ipv6_file = 'data/ipv6.pl'
if os.path.exists('data/ipv6.pl'):
os.remove(ipv6_file)
mw.writeLog('面板设置', '关闭面板IPv6兼容!')
else:
mw.writeFile(ipv6_file, 'True')
mw.writeLog('面板设置', '开启面板IPv6兼容!')
mw.restartMw()
return mw.returnJson(True, '设置成功!')
# 获取面板证书
def getPanelSslApi(self):
cert = {}
cert['privateKey'] = mw.readFile('ssl/privateKey.pem')
cert['certPem'] = mw.readFile('ssl/certificate.pem')
cert['rep'] = os.path.exists('ssl/input.pl')
return mw.getJson(cert)
# 保存面板证书
def savePanelSslApi(self):
keyPath = 'ssl/privateKey.pem'
certPath = 'ssl/certificate.pem'
checkCert = '/tmp/cert.pl'
certPem = request.form.get('certPem', '').strip()
privateKey = request.form.get('privateKey', '').strip()
mw.writeFile(checkCert, certPem)
if privateKey:
mw.writeFile(keyPath, privateKey)
if certPem:
mw.writeFile(certPath, certPem)
if not mw.checkCert(checkCert):
return mw.returnJson(False, '证书错误,请检查!')
mw.writeFile('ssl/input.pl', 'True')
return mw.returnJson(True, '证书已保存!')
# 设置面板SSL
def setPanelSslApi(self):
sslConf = mw.getRunDir() + '/data/ssl.pl'
if os.path.exists(sslConf):
os.system('rm -f ' + sslConf)
return mw.returnJson(True, 'SSL已关闭,请使用http协议访问面板!')
else:
os.system('pip install cffi==1.10')
os.system('pip install cryptography==2.1')
os.system('pip install pyOpenSSL==16.2')
try:
if not self.createSSL():
return mw.returnJson(False, '开启失败,无法自动安装pyOpenSSL组件!<p>请尝试手动安装: pip install pyOpenSSL</p>')
mw.writeFile(sslConf, 'True')
except Exception as ex:
return mw.returnJson(False, '开启失败,无法自动安装pyOpenSSL组件!<p>请尝试手动安装: pip install pyOpenSSL</p>')
return mw.returnJson(True, '开启成功,请使用https协议访问面板!')
def getApi(self):
data = {}
return mw.getJson(data)
##### ----- end ----- ###
# 自签证书
def createSSL(self):
if os.path.exists('ssl/input.pl'):
return True
import OpenSSL
key = OpenSSL.crypto.PKey()
key.generate_key(OpenSSL.crypto.TYPE_RSA, 2048)
cert = OpenSSL.crypto.X509()
cert.set_serial_number(0)
cert.get_subject().CN = mw.getLocalIp()
cert.set_issuer(cert.get_subject())
cert.gmtime_adj_notBefore(0)
cert.gmtime_adj_notAfter(86400 * 3650)
cert.set_pubkey(key)
cert.sign(key, 'md5')
cert_ca = OpenSSL.crypto.dump_certificate(
OpenSSL.crypto.FILETYPE_PEM, cert)
private_key = OpenSSL.crypto.dump_privatekey(
OpenSSL.crypto.FILETYPE_PEM, key)
if len(cert_ca) > 100 and len(private_key) > 100:
mw.writeFile('ssl/certificate.pem', cert_ca)
mw.writeFile('ssl/privateKey.pem', private_key)
print(cert_ca, private_key)
return True
return False
# 获取临时登录列表
def getTempLoginApi(self):
if 'tmp_login_expire' in session:
return mw.returnJson(False, '没有权限')
limit = request.form.get('limit', '10').strip()
p = request.form.get('p', '1').strip()
tojs = request.form.get('tojs', '').strip()
tempLoginM = mw.M('temp_login')
tempLoginM.where('state=? and expire<?',
(0, int(time.time()))).setField('state', -1)
start = (int(p) - 1) * (int(limit))
vlist = tempLoginM.limit(str(start) + ',' + str(limit)).order('id desc').field(
'id,addtime,expire,login_time,login_addr,state').select()
data = {}
data['data'] = vlist
count = tempLoginM.count()
page_args = {}
page_args['count'] = count
page_args['tojs'] = 'get_temp_login'
page_args['p'] = p
page_args['row'] = limit
data['page'] = mw.getPage(page_args)
return mw.getJson(data)
# 删除临时登录
def removeTempLoginApi(self):
if 'tmp_login_expire' in session:
return mw.returnJson(False, '没有权限')
sid = request.form.get('id', '10').strip()
if mw.M('temp_login').where('id=?', (sid,)).delete():
mw.writeLog('面板设置', '删除临时登录连接')
return mw.returnJson(True, '删除成功')
return mw.returnJson(False, '删除失败')
def setTempLoginApi(self):
if 'tmp_login_expire' in session:
return mw.returnJson(False, '没有权限')
s_time = int(time.time())
mw.M('temp_login').where(
'state=? and expire>?', (0, s_time)).delete()
token = mw.getRandomString(48)
salt = mw.getRandomString(12)
pdata = {
'token': mw.md5(token + salt),
'salt': salt,
'state': 0,
'login_time': 0,
'login_addr': '',
'expire': s_time + 3600,
'addtime': s_time
}
if not mw.M('temp_login').count():
pdata['id'] = 101
if mw.M('temp_login').insert(pdata):
mw.writeLog('面板设置', '生成临时连接,过期时间:{}'.format(
mw.formatDate(times=pdata['expire'])))
return mw.getJson({'status': True, 'msg': "临时连接已生成", 'token': token, 'expire': pdata['expire']})
return mw.returnJson(False, '连接生成失败')
def getTempLoginLogsApi(self):
if 'tmp_login_expire' in session:
return mw.returnJson(False, '没有权限')
logs_id = request.form.get('id', '').strip()
logs_id = int(logs_id)
data = mw.M('logs').where(
'uid=?', (logs_id,)).order('id desc').field(
'id,type,uid,log,addtime').select()
return mw.returnJson(False, 'ok', data)
def checkPanelToken(self):
api_file = self.__api_addr
if not os.path.exists(api_file):
return False, ''
tmp = mw.readFile(api_file)
data = json.loads(tmp)
if data['open']:
return True, data
else:
return False, ''
def getPanelTokenApi(self):
api_file = self.__api_addr
tmp = mw.readFile(api_file)
if not os.path.exists(api_file):
ready_data = {"open": False, "token": "", "limit_addr": []}
mw.writeFile(api_file, json.dumps(ready_data))
mw.execShell("chmod 600 " + api_file)
tmp = mw.readFile(api_file)
data = json.loads(tmp)
if not 'key' in data:
data['key'] = mw.getRandomString(16)
mw.writeFile(api_file, json.dumps(data))
if 'token_crypt' in data:
data['token'] = mw.deCrypt(data['token'], data['token_crypt'])
else:
token = mw.getRandomString(32)
data['token'] = mw.md5(token)
data['token_crypt'] = mw.enCrypt(
data['token'], token).decode('utf-8')
mw.writeFile(api_file, json.dumps(data))
data['token'] = "***********************************"
data['limit_addr'] = '\n'.join(data['limit_addr'])
del(data['key'])
return mw.returnJson(True, 'ok', data)
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)
data['token_crypt'] = mw.enCrypt(
data['token'], token).decode('utf-8')
mw.writeLog('API配置', '重新生成API-Token')
mw.writeFile(api_file, json.dumps(data))
return mw.returnJson(True, 'ok', token)
elif op_type == '2':
data['open'] = not data['open']
stats = {True: '开启', False: '关闭'}
if not 'token_crypt' in data:
token = mw.getRandomString(32)
data['token'] = mw.md5(token)
data['token_crypt'] = mw.enCrypt(
data['token'], token).decode('utf-8')
token = stats[data['open']] + '成功!'
mw.writeLog('API配置', '%sAPI接口' % stats[data['open']])
mw.writeFile(api_file, json.dumps(data))
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)
mw.writeFile(api_file, json.dumps(data))
return mw.returnJson(True, '保存成功!')
def get(self):
data = {}
data['title'] = mw.getConfig('title')
data['site_path'] = mw.getWwwDir()
data['backup_path'] = mw.getBackupDir()
sformat = 'date +"%Y-%m-%d %H:%M:%S %Z %z"'
data['systemdate'] = mw.execShell(sformat)[0].strip()
data['port'] = mw.getHostPort()
data['ip'] = mw.getHostAddr()
admin_path_file = 'data/admin_path.pl'
if not os.path.exists(admin_path_file):
data['admin_path'] = '/'
else:
data['admin_path'] = mw.readFile(admin_path_file)
ipv6_file = 'data/ipv6.pl'
if os.path.exists(ipv6_file):
data['ipv6'] = 'checked'
else:
data['ipv6'] = ''
debug_file = 'data/debug.pl'
if os.path.exists(debug_file):
data['debug'] = 'checked'
else:
data['debug'] = ''
ssl_file = 'data/ssl.pl'
if os.path.exists('data/ssl.pl'):
data['ssl'] = 'checked'
else:
data['ssl'] = ''
basic_auth = 'data/basic_auth.json'
if os.path.exists(basic_auth):
bac = mw.readFile(basic_auth)
bac = json.loads(bac)
if bac['open']:
data['basic_auth'] = 'checked'
else:
data['basic_auth'] = ''
cfg_domain = 'data/bind_domain.pl'
if os.path.exists(cfg_domain):
domain = mw.readFile(cfg_domain)
data['bind_domain'] = domain.strip()
else:
data['bind_domain'] = ''
api_token = self.__api_addr
if os.path.exists(api_token):
bac = mw.readFile(api_token)
bac = json.loads(bac)
if bac['open']:
data['api_token'] = 'checked'
else:
data['api_token'] = ''
data['site_count'] = mw.M('sites').count()
data['username'] = mw.M('users').where(
"id=?", (1,)).getField('username')
return data

View File

@@ -1,639 +0,0 @@
# coding: utf-8
# ---------------------------------------------------------------------------------
# MW-Linux面板
# ---------------------------------------------------------------------------------
# copyright (c) 2018-∞(https://github.com/midoks/mdserver-web) All rights reserved.
# ---------------------------------------------------------------------------------
# Author: midoks <midoks@163.com>
# ---------------------------------------------------------------------------------
# ---------------------------------------------------------------------------------
# 计划任务操作
# ---------------------------------------------------------------------------------
import psutil
import time
import os
import sys
import mw
import re
import json
import pwd
from flask import request
class crontab_api:
field = 'id,name,type,where1,where_hour,where_minute,echo,addtime,status,save,backup_to,stype,sname,sbody,urladdress'
def __init__(self):
pass
##### ----- start ----- ###
def listApi(self):
p = request.args.get('p', 1)
psize = 10
startPage = (int(p) - 1) * psize
pageInfo = str(startPage) + ',' + str(psize)
_list = mw.M('crontab').where('', ()).field(
self.field).limit(pageInfo).order('id desc').select()
data = []
for i in range(len(_list)):
tmp = _list[i]
if _list[i]['type'] == "day":
tmp['type'] = '每天'
tmp['cycle'] = mw.getInfo('每天, {1}点{2}分 执行', (str(
_list[i]['where_hour']), str(_list[i]['where_minute'])))
elif _list[i]['type'] == "day-n":
tmp['type'] = mw.getInfo(
'每{1}天', (str(_list[i]['where1']),))
tmp['cycle'] = mw.getInfo('每隔{1}天, {2}点{3}分 执行', (str(
_list[i]['where1']), str(_list[i]['where_hour']), str(_list[i]['where_minute'])))
elif _list[i]['type'] == "hour":
tmp['type'] = '每小时'
tmp['cycle'] = mw.getInfo(
'每小时, 第{1}分钟 执行', (str(_list[i]['where_minute']),))
elif _list[i]['type'] == "hour-n":
tmp['type'] = mw.getInfo(
'每{1}小时', (str(_list[i]['where1']),))
tmp['cycle'] = mw.getInfo('每{1}小时, 第{2}分钟 执行', (str(
_list[i]['where1']), str(_list[i]['where_minute'])))
elif _list[i]['type'] == "minute-n":
tmp['type'] = mw.getInfo(
'每{1}分钟', (str(_list[i]['where1']),))
tmp['cycle'] = mw.getInfo(
'每隔{1}分钟执行', (str(_list[i]['where1']),))
elif _list[i]['type'] == "week":
tmp['type'] = '每周'
if not _list[i]['where1']:
_list[i]['where1'] = '0'
tmp['cycle'] = mw.getInfo('每周{1}, {2}点{3}分执行', (self.toWeek(int(
_list[i]['where1'])), str(_list[i]['where_hour']), str(_list[i]['where_minute'])))
elif _list[i]['type'] == "month":
tmp['type'] = '每月'
tmp['cycle'] = mw.getInfo('每月, {1}日 {2}点{3}分执行', (str(_list[i]['where1']), str(
_list[i]['where_hour']), str(_list[i]['where_minute'])))
data.append(tmp)
rdata = {}
rdata['data'] = data
count = mw.M('crontab').where('', ()).count()
_page = {}
_page['count'] = count
_page['p'] = p
_page['row'] = psize
_page['tojs'] = "getCronData"
rdata['list'] = mw.getPage(_page)
rdata['p'] = p
# backup hock
bh_file = mw.getPanelDataDir() + "/hook_backup.json"
if os.path.exists(bh_file):
hb_data = mw.readFile(bh_file)
hb_data = json.loads(hb_data)
rdata['backup_hook'] = hb_data
return mw.getJson(rdata)
# 设置计划任务状态
def setCronStatusApi(self):
mid = request.form.get('id', '')
cronInfo = mw.M('crontab').where(
'id=?', (mid,)).field(self.field).find()
status = 1
if cronInfo['status'] == status:
status = 0
self.removeForCrond(cronInfo['echo'])
else:
cronInfo['status'] = 1
self.syncToCrond(cronInfo)
mw.M('crontab').where('id=?', (mid,)).setField('status', status)
mw.writeLog(
'计划任务', '修改计划任务[' + cronInfo['name'] + ']状态为[' + str(status) + ']')
return mw.returnJson(True, '设置成功')
# 获取指定任务数据
def getCrondFindApi(self):
sid = request.form.get('id', '')
data = mw.M('crontab').where(
'id=?', (sid,)).field(self.field).find()
return mw.getJson(data)
# 参数校验
def cronCheck(self, params):
if params['stype'] == 'site' or params['stype'] == 'database' or params['stype'] == 'logs':
if params['save'] == '':
return False, '保留份数不能为空!'
if params['type'] == 'day':
if params['hour'] == '':
return False, '小时不能为空!'
if params['minute'] == '':
return False, '分钟不能为空!'
if params['type'] == 'day-n':
if params['where1'] == '':
return False, '天不能为空!'
if params['hour'] == '':
return False, '小时不能为空!'
if params['minute'] == '':
return False, '分钟不能为空!'
if params['type'] == 'hour':
if params['minute'] == '':
return False, '分钟不能为空!'
if params['type'] == 'hour-n':
if params['where1'] == '':
return False, '小时不能为空!'
if params['minute'] == '':
return False, '分钟不能为空!'
if params['type'] == 'minute-n':
if params['where1'] == '':
return False, '分钟不能为空!'
if params['type'] == 'week':
if params['hour'] == '':
return False, '小时不能为空!'
if params['minute'] == '':
return False, '分钟不能为空!'
if params['type'] == 'month':
if params['where1'] == '':
return False, '日不能为空!'
if params['hour'] == '':
return False, '小时不能为空!'
if params['minute'] == '':
return False, '分钟不能为空!'
return True, 'OK'
def modifyCrondApi(self):
sid = request.form.get('id', '')
iname = request.form.get('name', '')
field_type = request.form.get('type', '')
week = request.form.get('week', '')
where1 = request.form.get('where1', '')
hour = request.form.get('hour', '')
minute = request.form.get('minute', '')
save = request.form.get('save', '')
backup_to = request.form.get('backup_to', '')
stype = request.form.get('stype', '')
sname = request.form.get('sname', '')
sbody = request.form.get('sbody', '')
urladdress = request.form.get('urladdress', '')
if len(iname) < 1:
return mw.returnJson(False, '任务名称不能为空!')
params = {
'name': iname,
'type': field_type,
'week': week,
'where1': where1,
'hour': hour,
'minute': minute,
'save': save,
'backup_to': backup_to,
'stype': stype,
'sname': sname,
'sbody': sbody,
'urladdress': urladdress,
}
is_check_pass, msg = self.cronCheck(params)
if not is_check_pass:
return mw.returnJson(is_check_pass, msg)
cuonConfig, get, name = self.getCrondCycle(params)
cronInfo = mw.M('crontab').where(
'id=?', (sid,)).field(self.field).find()
del(cronInfo['id'])
del(cronInfo['addtime'])
cronInfo['name'] = get['name']
cronInfo['type'] = get['type']
cronInfo['where1'] = get['where1']
cronInfo['where_hour'] = get['hour']
cronInfo['where_minute'] = get['minute']
cronInfo['save'] = get['save']
cronInfo['backup_to'] = get['backup_to']
cronInfo['sbody'] = get['sbody']
cronInfo['urladdress'] = get['urladdress']
addData = mw.M('crontab').where('id=?', (sid,)).save('name,type,where1,where_hour,where_minute,save,backup_to,sbody,urladdress', (get[
'name'], field_type, get['where1'], get['hour'], get['minute'], get['save'], get['backup_to'], get['sbody'], get['urladdress']))
self.removeForCrond(cronInfo['echo'])
self.syncToCrond(cronInfo)
mw.writeLog('计划任务', '修改计划任务[' + cronInfo['name'] + ']成功')
return mw.returnJson(True, '修改成功')
def logsApi(self):
sid = request.form.get('id', '')
echo = mw.M('crontab').where("id=?", (sid,)).field('echo').find()
logFile = mw.getServerDir() + '/cron/' + echo['echo'] + '.log'
if not os.path.exists(logFile):
return mw.returnJson(False, '当前日志为空!')
log = mw.getLastLine(logFile, 500)
return mw.returnJson(True, log)
def addApi(self):
iname = request.form.get('name', '')
field_type = request.form.get('type', '')
week = request.form.get('week', '')
where1 = request.form.get('where1', '')
hour = request.form.get('hour', '')
minute = request.form.get('minute', '')
save = request.form.get('save', '')
backup_to = request.form.get('backupTo', '')
stype = request.form.get('sType', '')
sname = request.form.get('sName', '')
sbody = request.form.get('sBody', '')
urladdress = request.form.get('urladdress', '')
if len(iname) < 1:
return mw.returnJson(False, '任务名称不能为空!')
params = {
'name': iname,
'type': field_type,
'week': week,
'where1': where1,
'hour': hour,
'minute': minute,
'save': save,
'backup_to': backup_to,
'stype': stype,
'sname': sname,
'sbody': sbody,
'urladdress': urladdress,
}
is_check_pass, msg = self.cronCheck(params)
if not is_check_pass:
return mw.returnJson(is_check_pass, msg)
addData = self.add(params)
if addData > 0:
return mw.returnJson(True, '添加成功')
return mw.returnJson(False, '添加失败')
def add(self, params):
iname = params["name"]
field_type = params["type"]
week = params["week"]
where1 = params["where1"]
hour = params["hour"]
minute = params["minute"]
save = params["save"]
backup_to = params["backup_to"]
stype = params["stype"]
sname = params["sname"]
sbody = params["sbody"]
urladdress = params["urladdress"]
# print params
cronConfig, get, name = self.getCrondCycle(params)
cronPath = mw.getServerDir() + '/cron'
cronName = self.getShell(params)
if type(cronName) == dict:
return cronName
cronConfig += ' ' + cronPath + '/' + cronName + \
' >> ' + cronPath + '/' + cronName + '.log 2>&1'
# print(cronConfig)
if not mw.isAppleSystem():
wRes = self.writeShell(cronConfig)
if type(wRes) != bool:
return wRes
self.crondReload()
add_time = time.strftime('%Y-%m-%d %X', time.localtime())
task_id = mw.M('crontab').add('name,type,where1,where_hour,where_minute,echo,addtime,status,save,backup_to,stype,sname,sbody,urladdress',
(iname, field_type, where1, hour, minute, cronName, add_time, 1, save, backup_to, stype, sname, sbody, urladdress,))
return task_id
def startTaskApi(self):
sid = request.form.get('id', '')
echo = mw.M('crontab').where('id=?', (sid,)).getField('echo')
execstr = mw.getServerDir() + '/cron/' + echo
os.system('chmod +x ' + execstr)
os.system('nohup ' + execstr + ' >> ' + execstr + '.log 2>&1 &')
return mw.returnJson(True, '任务已执行!')
def delApi(self):
task_id = request.form.get('id', '')
try:
data = self.delete(task_id)
if not data[0]:
return mw.returnJson(False, data[1])
return mw.returnJson(True, '删除成功')
except Exception as e:
return mw.returnJson(False, '删除失败:' + str(e))
def delete(self, tid):
find = mw.M('crontab').where("id=?", (tid,)).field('name,echo').find()
if not self.removeForCrond(find['echo']):
return (False, '无法写入文件,请检查是否开启了系统加固功能!')
cronPath = mw.getServerDir() + '/cron'
sfile = cronPath + '/' + find['echo']
if os.path.exists(sfile):
os.remove(sfile)
sfile = cronPath + '/' + find['echo'] + '.log'
if os.path.exists(sfile):
os.remove(sfile)
mw.M('crontab').where("id=?", (tid,)).delete()
mw.writeLog('计划任务', mw.getInfo('删除计划任务[{1}]成功!', (find['name'],)))
return (True, "OK")
def delLogsApi(self):
sid = request.form.get('id', '')
try:
echo = mw.M('crontab').where("id=?", (sid,)).getField('echo')
logFile = mw.getServerDir() + '/cron/' + echo + '.log'
os.remove(logFile)
return mw.returnJson(True, '任务日志已清空!')
except:
return mw.returnJson(False, '任务日志清空失败!')
# 取数据列表
def getDataListApi(self):
stype = request.form.get('type', '')
bak_data = []
if stype == 'sites' or stype == 'databases':
hookPath = mw.getPanelDataDir() + "/hook_backup.json"
if os.path.exists(hookPath):
t = mw.readFile(hookPath)
bak_data = json.loads(t)
if stype == 'databases':
db_list = {}
db_list['orderOpt'] = bak_data
path = mw.getServerDir() + '/mysql'
if not os.path.exists(path + '/mysql.db'):
db_list['data'] = []
else:
db_list['data'] = mw.M('databases').dbPos(
path, 'mysql').field('name,ps').select()
return mw.getJson(db_list)
data = {}
data['orderOpt'] = bak_data
data['data'] = mw.M(stype).field('name,ps').select()
return mw.getJson(data)
##### ----- start ----- ###
# 转换大写星期
def toWeek(self, num):
wheres = {
0: '日',
1: '一',
2: '二',
3: '三',
4: '四',
5: '五',
6: '六'
}
try:
return wheres[num]
except:
return ''
def getCrondCycle(self, params):
cuonConfig = ''
name = ''
if params['type'] == "day":
cuonConfig = self.getDay(params)
name = '每天'
elif params['type'] == "day-n":
cuonConfig = self.getDay_N(params)
name = mw.getInfo('每{1}天', (params['where1'],))
elif params['type'] == "hour":
cuonConfig = self.getHour(params)
name = '每小时'
elif params['type'] == "hour-n":
cuonConfig = self.getHour_N(params)
name = '每小时'
elif params['type'] == "minute-n":
cuonConfig = self.minute_N(params)
elif params['type'] == "week":
params['where1'] = params['week']
cuonConfig = self.week(params)
elif params['type'] == "month":
cuonConfig = self.month(params)
return cuonConfig, params, name
# 取任务构造Day
def getDay(self, param):
cuonConfig = "{0} {1} * * * ".format(param['minute'], param['hour'])
return cuonConfig
# 取任务构造Day_n
def getDay_N(self, param):
cuonConfig = "{0} {1} */{2} * * ".format(
param['minute'], param['hour'], param['where1'])
return cuonConfig
# 取任务构造Hour
def getHour(self, param):
cuonConfig = "{0} * * * * ".format(param['minute'])
return cuonConfig
# 取任务构造Hour-N
def getHour_N(self, param):
cuonConfig = "{0} */{1} * * * ".format(
param['minute'], param['where1'])
return cuonConfig
# 取任务构造Minute-N
def minute_N(self, param):
cuonConfig = "*/{0} * * * * ".format(param['where1'])
return cuonConfig
# 取任务构造week
def week(self, param):
cuonConfig = "{0} {1} * * {2}".format(
param['minute'], param['hour'], param['week'])
return cuonConfig
# 取任务构造Month
def month(self, param):
cuonConfig = "{0} {1} {2} * * ".format(
param['minute'], param['hour'], param['where1'])
return cuonConfig
# 取执行脚本
def getShell(self, param):
# try:
stype = param['stype']
if stype == 'toFile':
shell = param.sFile
else:
head = "#!/bin/bash\nPATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin\nexport PATH\n"
source_bin_activate = '''
export LANG=en_US.UTF-8
MW_PATH=%s/bin/activate
if [ -f $MW_PATH ];then
source $MW_PATH
fi
''' % (mw.getRunDir(),)
head = head + source_bin_activate + "\n"
log = '.log'
script_dir = mw.getServerDir() + "/mdserver-web/scripts"
wheres = {
'path': head + "python3 " + script_dir + "/backup.py path " + param['sname'] + " " + str(param['save']),
'site': head + "python3 " + script_dir + "/backup.py site " + param['sname'] + " " + str(param['save']),
'database': head + "python3 " + script_dir + "/backup.py database " + param['sname'] + " " + str(param['save']),
'logs': head + "python3 " + script_dir + "/logs_backup.py " + param['sname'] + log + " " + str(param['save']),
'rememory': head + "/bin/bash " + script_dir + '/rememory.sh'
}
if param['backup_to'] != 'localhost':
cfile = mw.getPluginDir() + "/" + \
param['backup_to'] + "/index.py"
wheres = {
'path': head + "python3 " + cfile + " path " + param['sname'] + " " + str(param['save']),
'site': head + "python3 " + cfile + " site " + param['sname'] + " " + str(param['save']),
'database': head + "python3 " + cfile + " database " + param['sname'] + " " + str(param['save']),
'logs': head + "python3 " + script_dir + "/logs_backup.py " + param['sname'] + log + " " + str(param['save']),
'rememory': head + "/bin/bash " + script_dir + '/rememory.sh'
}
try:
shell = wheres[stype]
except:
if stype == 'toUrl':
shell = head + "curl -sS --connect-timeout 10 -m 60 '" + \
param['urladdress'] + "'"
else:
shell = head + param['sbody'].replace("\r\n", "\n")
shell += '''
echo "----------------------------------------------------------------------------"
endDate=`date +"%Y-%m-%d %H:%M:%S"`
echo "★[$endDate] Successful"
echo "----------------------------------------------------------------------------"
'''
cronPath = mw.getServerDir() + '/cron'
if not os.path.exists(cronPath):
mw.execShell('mkdir -p ' + cronPath)
if not 'echo' in param:
cronName = mw.md5(mw.md5(str(time.time()) + '_mw'))
else:
cronName = param['echo']
file = cronPath + '/' + cronName
mw.writeFile(file, self.checkScript(shell))
mw.execShell('chmod 750 ' + file)
return cronName
# 检查脚本
def checkScript(self, shell):
keys = ['shutdown', 'init 0', 'mkfs', 'passwd',
'chpasswd', '--stdin', 'mkfs.ext', 'mke2fs']
for key in keys:
shell = shell.replace(key, '[***]')
return shell
# 将Shell脚本写到文件
def writeShell(self, config):
u_file = '/var/spool/cron/crontabs/root'
if not os.path.exists(u_file):
file = '/var/spool/cron/root'
if mw.isAppleSystem():
file = '/etc/crontab'
else:
file = u_file
if not os.path.exists(file):
mw.writeFile(file, '')
conf = mw.readFile(file)
conf += str(config) + "\n"
if mw.writeFile(file, conf):
if not os.path.exists(u_file):
mw.execShell("chmod 600 '" + file +
"' && chown root.root " + file)
else:
mw.execShell("chmod 600 '" + file +
"' && chown root.crontab " + file)
return True
return mw.returnJson(False, '文件写入失败,请检查是否开启系统加固功能!')
# 重载配置
def crondReload(self):
if mw.isAppleSystem():
if os.path.exists('/etc/crontab'):
pass
# mw.execShell('/usr/sbin/cron restart')
else:
if os.path.exists('/etc/init.d/crond'):
mw.execShell('/etc/init.d/crond reload')
elif os.path.exists('/etc/init.d/cron'):
mw.execShell('service cron restart')
else:
mw.execShell("systemctl reload crond")
# 从crond删除
def removeForCrond(self, echo):
u_file = '/var/spool/cron/crontabs/root'
if not os.path.exists(u_file):
file = '/var/spool/cron/root'
if mw.isAppleSystem():
file = '/etc/crontab'
if not os.path.exists(file):
return False
else:
file = u_file
if mw.isAppleSystem():
return True
conf = mw.readFile(file)
rep = ".+" + str(echo) + ".+\n"
conf = re.sub(rep, "", conf)
if not mw.writeFile(file, conf):
return False
self.crondReload()
return True
def syncToCrond(self, cronInfo):
if 'status' in cronInfo:
if cronInfo['status'] == 0:
return False
if 'where_hour' in cronInfo:
cronInfo['hour'] = cronInfo['where_hour']
cronInfo['minute'] = cronInfo['where_minute']
cronInfo['week'] = cronInfo['where1']
cuonConfig, cronInfo, name = self.getCrondCycle(cronInfo)
cronPath = mw.getServerDir() + '/cron'
cronName = self.getShell(cronInfo)
if type(cronName) == dict:
return cronName
cuonConfig += ' ' + cronPath + '/' + cronName + \
' >> ' + cronPath + '/' + cronName + '.log 2>&1'
wRes = self.writeShell(cuonConfig)
if type(wRes) != bool:
return False
self.crondReload()

View File

@@ -1,393 +0,0 @@
# coding: utf-8
# ---------------------------------------------------------------------------------
# MW-Linux面板
# ---------------------------------------------------------------------------------
# copyright (c) 2018-∞(https://github.com/midoks/mdserver-web) All rights reserved.
# ---------------------------------------------------------------------------------
# Author: midoks <midoks@163.com>
# ---------------------------------------------------------------------------------
# ---------------------------------------------------------------------------------
# sqlite3操作
# ---------------------------------------------------------------------------------
import sqlite3
import os
import sys
class Sql():
#------------------------------
# 数据库操作类 For sqlite3
#------------------------------
__DB_FILE = None # 数据库文件
__DB_CONN = None # 数据库连接对象
__DB_TABLE = "" # 被操作的表名称
__OPT_WHERE = "" # where条件
__OPT_LIMIT = "" # limit条件
__OPT_GROUP = "" # group条件
__OPT_ORDER = "" # order条件
__OPT_FIELD = "*" # field条件
__OPT_PARAM = () # where值
def __init__(self):
self.__DB_FILE = 'data/default.db'
def __getConn(self):
# 取数据库对象
try:
if self.__DB_CONN == None:
self.__DB_CONN = sqlite3.connect(self.__DB_FILE)
self.__DB_CONN.text_factory = str
except Exception as ex:
return "error: " + str(ex)
def autoTextFactory(self):
if sys.version_info[0] == 3:
self.__DB_CONN.text_factory = lambda x: str(
x, encoding="utf-8", errors='ignore')
else:
self.__DB_CONN.text_factory = lambda x: unicode(
x, "utf-8", "ignore")
def dbfile(self, name):
self.__DB_FILE = 'data/' + name + '.db'
return self
def dbPos(self, path, name):
self.__DB_FILE = path + '/' + name + '.db'
return self
def table(self, table):
# 设置表名
self.__DB_TABLE = table
return self
def where(self, where, param):
# WHERE条件
if where:
self.__OPT_WHERE = " WHERE " + where
self.__OPT_PARAM = param
return self
def andWhere(self, where, param):
# WHERE条件
if where:
self.__OPT_WHERE = self.__OPT_WHERE + " and " + where
# print(param)
# print(self.__OPT_PARAM)
self.__OPT_PARAM = self.__OPT_PARAM + param
return self
def order(self, order):
# ORDER条件
if len(order):
self.__OPT_ORDER = " ORDER BY " + order
else:
self.__OPT_ORDER = ""
return self
def group(self, group):
if len(group):
self.__OPT_GROUP = " GROUP BY " + group
else:
self.__OPT_GROUP = ""
return self
def limit(self, limit):
# LIMIT条件
if len(limit):
self.__OPT_LIMIT = " LIMIT " + limit
else:
self.__OPT_LIMIT = ""
return self
def field(self, field):
# FIELD条件
if len(field):
self.__OPT_FIELD = field
return self
def select(self):
# 查询数据集
self.__getConn()
try:
sql = "SELECT " + self.__OPT_FIELD + " FROM " + self.__DB_TABLE + \
self.__OPT_WHERE + self.__OPT_GROUP + self.__OPT_ORDER + self.__OPT_LIMIT
# print(sql)
result = self.__DB_CONN.execute(sql, self.__OPT_PARAM)
data = result.fetchall()
# 构造字曲系列
if self.__OPT_FIELD != "*":
field = self.__OPT_FIELD.split(',')
tmp = []
for row in data:
i = 0
tmp1 = {}
for key in field:
tmp1[key] = row[i]
i += 1
tmp.append(tmp1)
del(tmp1)
data = tmp
del(tmp)
else:
# 将元组转换成列表
tmp = map(list, data)
data = tmp
del(tmp)
self.__close()
return data
except Exception as ex:
return "error: " + str(ex)
def inquiry(self, input_field=''):
# 查询数据集
# 不清空查询参数
self.__getConn()
try:
sql = "SELECT " + self.__OPT_FIELD + " FROM " + self.__DB_TABLE + \
self.__OPT_WHERE + self.__OPT_GROUP + self.__OPT_ORDER + self.__OPT_LIMIT
# print(sql, self.__OPT_PARAM)
result = self.__DB_CONN.execute(sql, self.__OPT_PARAM)
data = result.fetchall()
# 构造字曲系列
if self.__OPT_FIELD != "*":
if input_field != "":
field = input_field.split(',')
else:
field = self.__OPT_FIELD.split(',')
tmp = []
for row in data:
i = 0
tmp1 = {}
for key in field:
tmp1[key] = row[i]
i += 1
tmp.append(tmp1)
del(tmp1)
data = tmp
del(tmp)
else:
# 将元组转换成列表
tmp = map(list, data)
data = tmp
del(tmp)
return data
except Exception as ex:
return "error: " + str(ex)
def getField(self, keyName):
# 取回指定字段
result = self.field(keyName).select()
# print(result)
if len(result) == 1:
return result[0][keyName]
return result
def setField(self, keyName, keyValue):
# 更新指定字段
return self.save(keyName, (keyValue,))
def find(self):
# 取一行数据
result = self.limit("1").select()
if len(result) == 1:
return result[0]
return result
def count(self):
# 取行数
key = "COUNT(*)"
data = self.field(key).select()
try:
return int(data[0][key])
except:
return 0
def add(self, keys, param):
# 插入数据
self.__getConn()
try:
values = ""
for key in keys.split(','):
values += "?,"
values = self.checkInput(values[0:len(values) - 1])
sql = "INSERT INTO " + self.__DB_TABLE + \
"(" + keys + ") " + "VALUES(" + values + ")"
result = self.__DB_CONN.execute(sql, param)
last_id = result.lastrowid
self.__close()
self.__DB_CONN.commit()
return last_id
except Exception as ex:
return "error: " + str(ex)
# 插入数据
def insert(self, pdata):
if not pdata:
return False
keys, param = self.__format_pdata(pdata)
return self.add(keys, param)
# 更新数据
def update(self, pdata):
if not pdata:
return False
keys, param = self.__format_pdata(pdata)
return self.save(keys, param)
# 构造数据
def __format_pdata(self, pdata):
keys = pdata.keys()
keys_str = ','.join(keys)
param = []
for k in keys:
param.append(pdata[k])
return keys_str, tuple(param)
def checkInput(self, data):
if not data:
return data
if type(data) != str:
return data
checkList = [
{'d': '<', 'r': '<'},
{'d': '>', 'r': '>'},
{'d': '\'', 'r': '‘'},
{'d': '"', 'r': '“'},
{'d': '&', 'r': '&'},
{'d': '#', 'r': '#'},
{'d': '<', 'r': '<'}
]
for v in checkList:
data = data.replace(v['d'], v['r'])
return data
def addAll(self, keys, param):
# 插入数据
self.__getConn()
try:
values = ""
for key in keys.split(','):
values += "?,"
values = values[0:len(values) - 1]
sql = "INSERT INTO " + self.__DB_TABLE + \
"(" + keys + ") " + "VALUES(" + values + ")"
result = self.__DB_CONN.execute(sql, param)
return True
except Exception as ex:
return "error: " + str(ex)
def commit(self):
self.__close()
self.__DB_CONN.commit()
def save(self, keys, param):
# 更新数据
self.__getConn()
try:
opt = ""
for key in keys.split(','):
opt += key + "=?,"
opt = opt[0:len(opt) - 1]
sql = "UPDATE " + self.__DB_TABLE + " SET " + opt + self.__OPT_WHERE
# import mw
# mw.writeFile('/tmp/test.pl', sql)
# 处理拼接WHERE与UPDATE参数
tmp = list(param)
for arg in self.__OPT_PARAM:
tmp.append(arg)
self.__OPT_PARAM = tuple(tmp)
result = self.__DB_CONN.execute(sql, self.__OPT_PARAM)
self.__close()
self.__DB_CONN.commit()
return result.rowcount
except Exception as ex:
return "error: " + str(ex)
def delete(self, id=None):
# 删除数据
self.__getConn()
try:
if id:
self.__OPT_WHERE = " WHERE id=?"
self.__OPT_PARAM = (id,)
sql = "DELETE FROM " + self.__DB_TABLE + self.__OPT_WHERE
result = self.__DB_CONN.execute(sql, self.__OPT_PARAM)
self.__close()
self.__DB_CONN.commit()
return result.rowcount
except Exception as ex:
return "error: " + str(ex)
def originExecute(self, sql, param=()):
self.__getConn()
try:
result = self.__DB_CONN.execute(sql, param)
self.__DB_CONN.commit()
return result
except Exception as ex:
return "error: " + str(ex)
def execute(self, sql, param=()):
# 执行SQL语句返回受影响行
self.__getConn()
# print sql, param
try:
result = self.__DB_CONN.execute(sql, param)
self.__DB_CONN.commit()
return result.rowcount
except Exception as ex:
return "error: " + str(ex)
def query(self, sql, param):
# 执行SQL语句返回数据集
self.__getConn()
try:
result = self.__DB_CONN.execute(sql, param)
# 将元组转换成列表
# data = map(list, result)
return result
except Exception as ex:
return "error: " + str(ex)
def create(self, name):
# 创建数据表
self.__getConn()
import mw
script = mw.readFile('data/' + name + '.sql')
result = self.__DB_CONN.executescript(script)
self.__DB_CONN.commit()
return result.rowcount
def fofile(self, filename):
# 执行脚本
self.__getConn()
import mw
script = mw.readFile(filename)
result = self.__DB_CONN.executescript(script)
self.__DB_CONN.commit()
return result.rowcount
def __close(self):
# 清理条件属性
self.__OPT_WHERE = ""
self.__OPT_FIELD = "*"
self.__OPT_ORDER = ""
self.__OPT_LIMIT = ""
self.__OPT_PARAM = ()
def close(self):
# 释放资源
try:
self.__DB_CONN.close()
self.__DB_CONN = None
except:
pass

View File

@@ -1,896 +0,0 @@
# coding: utf-8
# ---------------------------------------------------------------------------------
# MW-Linux面板
# ---------------------------------------------------------------------------------
# copyright (c) 2018-∞(https://github.com/midoks/mdserver-web) All rights reserved.
# ---------------------------------------------------------------------------------
# Author: midoks <midoks@163.com>
# ---------------------------------------------------------------------------------
# ---------------------------------------------------------------------------------
# 文件类操作
# ---------------------------------------------------------------------------------
import psutil
import time
import os
import sys
import mw
import re
import json
import pwd
import shutil
from flask import request
from flask import send_file, send_from_directory
from flask import make_response
from flask import session
class files_api:
rPath = None
def __init__(self):
self.rPath = mw.getRootDir() + '/recycle_bin/'
##### ----- start ----- ###
def getBodyApi(self):
path = request.form.get('path', '')
return self.getBody(path)
def getLastBodyApi(self):
path = request.form.get('path', '')
line = request.form.get('line', '100')
if not os.path.exists(path):
return mw.returnJson(False, '文件不存在', (path,))
try:
data = mw.getLastLine(path, int(line))
return mw.returnJson(True, 'OK', data)
except Exception as ex:
return mw.returnJson(False, '无法正确读取文件!' + str(ex))
def saveBodyApi(self):
path = request.form.get('path', '')
data = request.form.get('data', '')
encoding = request.form.get('encoding', '')
return self.saveBody(path, data, encoding)
def downloadApi(self):
filename = request.args.get('filename', '')
if not os.path.exists(filename):
return ''
response = make_response(send_from_directory(
os.path.dirname(filename), os.path.basename(filename), as_attachment=True))
return response
def zipApi(self):
sfile = request.form.get('sfile', '')
dfile = request.form.get('dfile', '')
stype = request.form.get('type', '')
path = request.form.get('path', '')
return self.zip(sfile, dfile, stype, path)
def unzipApi(self):
sfile = request.form.get('sfile', '')
dfile = request.form.get('dfile', '')
stype = request.form.get('type', '')
path = request.form.get('path', '')
return self.unzip(sfile, dfile, stype, path)
# 移动文件或目录
def mvFileApi(self):
sfile = request.form.get('sfile', '')
dfile = request.form.get('dfile', '')
if not self.checkFileName(dfile):
return mw.returnJson(False, '文件名中不能包含特殊字符!')
if not os.path.exists(sfile):
return mw.returnJson(False, '指定文件不存在!')
if not self.checkDir(sfile):
return mw.returnJson(False, 'FILE_DANGER')
import shutil
try:
shutil.move(sfile, dfile)
msg = mw.getInfo('移动文件或目录[{1}]到[{2}]成功!', (sfile, dfile,))
mw.writeLog('文件管理', msg)
return mw.returnJson(True, '移动文件或目录成功!')
except:
return mw.returnJson(False, '移动文件或目录失败!')
def deleteApi(self):
path = request.form.get('path', '')
return self.delete(path)
def fileAccessApi(self):
filename = request.form.get('filename', '')
data = self.getAccess(filename)
return mw.getJson(data)
def setFileAccessApi(self):
if mw.isAppleSystem():
return mw.returnJson(True, '开发机不设置!')
filename = request.form.get('filename', '')
user = request.form.get('user', '')
access = request.form.get('access', '755')
sall = '-R'
try:
if not self.checkDir(filename):
return mw.returnJson(False, '请不要花样作死')
if not os.path.exists(filename):
return mw.returnJson(False, '指定文件不存在!')
os.system('chmod ' + sall + ' ' + access + " '" + filename + "'")
os.system('chown ' + sall + ' ' + user +
':' + user + " '" + filename + "'")
msg = mw.getInfo(
'设置[{1}]权限为[{2}]所有者为[{3}]', (filename, access, user,))
mw.writeLog('文件管理', msg)
return mw.returnJson(True, '设置成功!')
except:
return mw.returnJson(False, '设置失败!')
def getDirSizeApi(self):
path = request.form.get('path', '')
tmp = self.getDirSize(path)
return mw.returnJson(True, tmp[0].split()[0])
def getDirApi(self):
path = request.form.get('path', '')
if not os.path.exists(path):
path = mw.getRootDir() + "/wwwroot"
search = request.args.get('search', '').strip().lower()
page = request.args.get('p', '1').strip().lower()
row = request.args.get('showRow', '10')
disk = request.form.get('disk', '')
if disk == 'True':
row = 1000
return self.getDir(path, int(page), int(row), search)
def createFileApi(self):
file = request.form.get('path', '')
try:
if not self.checkFileName(file):
return mw.returnJson(False, '文件名中不能包含特殊字符!')
if os.path.exists(file):
return mw.returnJson(False, '指定文件已存在!')
_path = os.path.dirname(file)
if not os.path.exists(_path):
os.makedirs(_path)
open(file, 'w+').close()
self.setFileAccept(file)
msg = mw.getInfo('创建文件[{1}]成功!', (file,))
mw.writeLog('文件管理', msg)
return mw.returnJson(True, '文件创建成功!')
except Exception as e:
return mw.returnJson(True, '文件创建失败!')
def createDirApi(self):
path = request.form.get('path', '')
try:
if not self.checkFileName(path):
return mw.returnJson(False, '目录名中不能包含特殊字符!')
if os.path.exists(path):
return mw.returnJson(False, '指定目录已存在!')
os.makedirs(path)
self.setFileAccept(path)
msg = mw.getInfo('创建目录[{1}]成功!', (path,))
mw.writeLog('文件管理', msg)
return mw.returnJson(True, '目录创建成功!')
except Exception as e:
return mw.returnJson(False, '目录创建失败!')
def downloadFileApi(self):
import db
import time
url = request.form.get('url', '')
path = request.form.get('path', '')
filename = request.form.get('filename', '')
execstr = url + '|mw|' + path + '/' + filename
execstr = execstr.strip()
mw.M('tasks').add('name,type,status,addtime,execstr',
('下载文件[' + filename + ']', 'download', '-1', time.strftime('%Y-%m-%d %H:%M:%S'), execstr))
# self.setFileAccept(path + '/' + filename)
mw.triggerTask()
return mw.returnJson(True, '已将下载任务添加到队列!')
# 删除进程下的所有进程
def removeTaskRecursion(self, pid):
cmd = "ps -ef|grep " + pid + \
" | grep -v grep |sed -n '2,1p' | awk '{print $2}'"
sub_pid = mw.execShell(cmd)
if sub_pid[0].strip() == '':
return 'ok'
self.removeTaskRecursion(sub_pid[0].strip())
mw.execShell('kill -9 ' + sub_pid[0].strip())
return sub_pid[0].strip()
def removeTaskApi(self):
import system_api
mid = request.form.get('id', '')
try:
name = mw.M('tasks').where('id=?', (mid,)).getField('name')
status = mw.M('tasks').where('id=?', (mid,)).getField('status')
mw.M('tasks').delete(mid)
if status == '-1':
task_pid = mw.execShell(
"ps aux | grep 'task.py' | grep -v grep |awk '{print $2}'")
task_list = task_pid[0].strip().split("\n")
for i in range(len(task_list)):
self.removeTaskRecursion(task_list[i])
mw.triggerTask()
system_api.system_api().restartTask()
except:
system_api.system_api().restartTask()
return mw.returnJson(True, '任务已删除!')
# 上传文件
def uploadFileApi(self):
from werkzeug.utils import secure_filename
from flask import request
path = request.args.get('path', '')
if not os.path.exists(path):
os.makedirs(path)
f = request.files['zunfile']
filename = os.path.join(path, f.filename)
s_path = path
if os.path.exists(filename):
s_path = filename
p_stat = os.stat(s_path)
f.save(filename)
os.chown(filename, p_stat.st_uid, p_stat.st_gid)
os.chmod(filename, p_stat.st_mode)
msg = mw.getInfo('上传文件[{1}] 到 [{2}]成功!', (filename, path))
mw.writeLog('文件管理', msg)
return mw.returnMsg(True, '上传成功!')
def getRecycleBinApi(self):
rPath = self.rPath
if not os.path.exists(rPath):
os.system('mkdir -p ' + rPath)
data = {}
data['dirs'] = []
data['files'] = []
data['status'] = os.path.exists('data/recycle_bin.pl')
data['status_db'] = os.path.exists('data/recycle_bin_db.pl')
for file in os.listdir(rPath):
try:
tmp = {}
fname = rPath + file
tmp1 = file.split('_mw_')
tmp2 = tmp1[len(tmp1) - 1].split('_t_')
tmp['rname'] = file
tmp['dname'] = file.replace('_mw_', '/').split('_t_')[0]
tmp['name'] = tmp2[0]
tmp['time'] = int(float(tmp2[1]))
if os.path.islink(fname):
filePath = os.readlink(fname)
link = ' -> ' + filePath
if os.path.exists(filePath):
tmp['size'] = os.path.getsize(filePath)
else:
tmp['size'] = 0
else:
tmp['size'] = os.path.getsize(fname)
if os.path.isdir(fname):
data['dirs'].append(tmp)
else:
data['files'].append(tmp)
except Exception as e:
continue
return mw.returnJson(True, 'OK', data)
# 回收站开关
def recycleBinApi(self):
c = 'data/recycle_bin.pl'
db = request.form.get('db', '')
if db != '':
c = 'data/recycle_bin_db.pl'
if os.path.exists(c):
os.remove(c)
mw.writeLog('文件管理', '已关闭回收站功能!')
return mw.returnJson(True, '已关闭回收站功能!')
else:
mw.writeFile(c, 'True')
mw.writeLog('文件管理', '已开启回收站功能!')
return mw.returnJson(True, '已开启回收站功能!')
def reRecycleBinApi(self):
rPath = self.rPath
path = request.form.get('path', '')
dFile = path.replace('_mw_', '/').split('_t_')[0]
try:
import shutil
shutil.move(rPath + path, dFile)
msg = mw.getInfo('移动文件[{1}]到回收站成功!', (dFile,))
mw.writeLog('文件管理', msg)
return mw.returnJson(True, '恢复成功!')
except Exception as e:
msg = mw.getInfo('从回收站恢复[{1}]失败!', (dFile,))
mw.writeLog('文件管理', msg)
return mw.returnJson(False, '恢复失败!')
def delRecycleBinApi(self):
rPath = self.rPath
path = request.form.get('path', '')
empty = request.form.get('empty', '')
dFile = path.split('_t_')[0]
if not self.checkDir(path):
return mw.returnJson(False, '敏感目录,请不要花样作死!')
mw.execShell('which chattr && chattr -R -i ' + rPath + path)
if os.path.isdir(rPath + path):
import shutil
shutil.rmtree(rPath + path)
else:
os.remove(rPath + path)
tfile = path.replace('_mw_', '/').split('_t_')[0]
msg = mw.getInfo('已彻底从回收站删除{1}!', (tfile,))
mw.writeLog('文件管理', msg)
return mw.returnJson(True, msg)
# 获取进度
def getSpeedApi(self):
data = mw.getSpeed()
return mw.returnJson(True, '已清空回收站!', data)
def closeRecycleBinApi(self):
rPath = self.rPath
mw.execShell('which chattr && chattr -R -i ' + rPath)
rlist = os.listdir(rPath)
i = 0
l = len(rlist)
for name in rlist:
i += 1
path = rPath + name
mw.writeSpeed(name, i, l)
if os.path.isdir(path):
shutil.rmtree(path)
else:
os.remove(path)
mw.writeSpeed(None, 0, 0)
mw.writeLog('文件管理', '已清空回收站!')
return mw.returnJson(True, '已清空回收站!')
def deleteDirApi(self):
path = request.form.get('path', '')
if not os.path.exists(path):
return mw.returnJson(False, '指定文件不存在!')
# 检查是否为.user.ini
if path.find('.user.ini'):
os.system("which chattr && chattr -i '" + path + "'")
try:
if os.path.exists('data/recycle_bin.pl'):
if self.mvRecycleBin(path):
return mw.returnJson(True, '已将文件移动到回收站!')
mw.execShell('rm -rf ' + path)
mw.writeLog('文件管理', '删除文件成功!', (path,))
return mw.returnJson(True, '删除文件成功!')
except:
return mw.returnJson(False, '删除文件失败!')
def closeLogsApi(self):
logPath = mw.getLogsDir()
os.system('rm -f ' + logPath + '/*')
os.system('kill -USR1 `cat ' + mw.getServerDir() +
'openresty/nginx/logs/nginx.pid`')
mw.writeLog('文件管理', '网站日志已被清空!')
tmp = self.getDirSize(logPath)
return mw.returnJson(True, tmp[0].split()[0])
def setBatchDataApi(self):
path = request.form.get('path', '')
stype = request.form.get('type', '')
access = request.form.get('access', '')
user = request.form.get('user', '')
data = request.form.get('data')
if stype == '1' or stype == '2':
session['selected'] = {
'path': path,
'type': stype,
'access': access,
'user': user,
'data': data
}
return mw.returnJson(True, '标记成功,请在目标目录点击粘贴所有按钮!')
elif stype == '3':
for key in json.loads(data):
try:
filename = path + '/' + key
if not self.checkDir(filename):
return mw.returnJson(False, 'FILE_DANGER')
os.system('chmod -R ' + access + " '" + filename + "'")
os.system('chown -R ' + user + ':' +
user + " '" + filename + "'")
except:
continue
mw.writeLog('文件管理', '批量设置权限成功!')
return mw.returnJson(True, '批量设置权限成功!')
else:
import shutil
isRecyle = os.path.exists('data/recycle_bin.pl')
data = json.loads(data)
l = len(data)
i = 0
for key in data:
try:
filename = path + '/' + key
topath = filename
if not os.path.exists(filename):
continue
i += 1
mw.writeSpeed(key, i, l)
if os.path.isdir(filename):
if not self.checkDir(filename):
return mw.returnJson(False, '请不要花样作死!')
if isRecyle:
self.mvRecycleBin(topath)
else:
shutil.rmtree(filename)
else:
if key == '.user.ini':
os.system('which chattr && chattr -i ' + filename)
if isRecyle:
self.mvRecycleBin(topath)
else:
os.remove(filename)
except:
continue
mw.writeSpeed(None, 0, 0)
mw.writeLog('文件管理', '批量删除成功!')
return mw.returnJson(True, '批量删除成功!')
def checkExistsFilesApi(self):
dfile = request.form.get('dfile', '')
filename = request.form.get('filename', '')
data = []
filesx = []
if filename == '':
filesx = json.loads(session['selected']['data'])
else:
filesx.append(filename)
for fn in filesx:
if fn == '.':
continue
filename = dfile + '/' + fn
if os.path.exists(filename):
tmp = {}
stat = os.stat(filename)
tmp['filename'] = fn
tmp['size'] = os.path.getsize(filename)
tmp['mtime'] = str(int(stat.st_mtime))
data.append(tmp)
return mw.returnJson(True, 'ok', data)
def batchPasteApi(self):
path = request.form.get('path', '')
stype = request.form.get('type', '')
# filename = request.form.get('filename', '')
import shutil
if not self.checkDir(path):
return mw.returnJson(False, '请不要花样作死!')
i = 0
myfiles = json.loads(session['selected']['data'])
l = len(myfiles)
if stype == '1':
for key in myfiles:
i += 1
mw.writeSpeed(key, i, l)
try:
sfile = session['selected'][
'path'] + '/' + key
dfile = path + '/' + key
if os.path.isdir(sfile):
shutil.copytree(sfile, dfile)
else:
shutil.copyfile(sfile, dfile)
stat = os.stat(sfile)
os.chown(dfile, stat.st_uid, stat.st_gid)
except:
continue
msg = mw.getInfo('从[{1}]批量复制到[{2}]成功',
(session['selected']['path'], path,))
mw.writeLog('文件管理', msg)
else:
for key in myfiles:
try:
i += 1
mw.writeSpeed(key, i, l)
sfile = session['selected'][
'path'] + '/' + key
dfile = path + '/' + key
shutil.move(sfile, dfile)
except:
continue
msg = mw.getInfo('从[{1}]批量移动到[{2}]成功',
(session['selected']['path'], path,))
mw.writeLog('文件管理', msg)
mw.writeSpeed(None, 0, 0)
errorCount = len(myfiles) - i
del(session['selected'])
msg = mw.getInfo('批量操作成功[{1}],失败[{2}]', (str(i), str(errorCount)))
return mw.returnJson(True, msg)
def copyFileApi(self):
sfile = request.form.get('sfile', '')
dfile = request.form.get('dfile', '')
if sfile == dfile:
return mw.returnJson(False, '源与目的一致!')
if not os.path.exists(sfile):
return mw.returnJson(False, '指定文件不存在!')
if os.path.isdir(sfile):
return self.copyDir(sfile, dfile)
try:
import shutil
shutil.copyfile(sfile, dfile)
msg = mw.getInfo('复制文件[{1}]到[{2}]成功!', (sfile, dfile,))
mw.writeLog('文件管理', msg)
stat = os.stat(sfile)
os.chown(dfile, stat.st_uid, stat.st_gid)
return mw.returnJson(True, '文件复制成功!')
except:
return mw.returnJson(False, '文件复制失败!')
##### ----- end ----- ###
def copyDir(self, sfile, dfile):
if not os.path.exists(sfile):
return mw.returnJson(False, '指定目录不存在!')
if os.path.exists(dfile):
return mw.returnJson(False, '指定目录已存在!')
import shutil
try:
shutil.copytree(sfile, dfile)
stat = os.stat(sfile)
os.chown(dfile, stat.st_uid, stat.st_gid)
msg = mw.getInfo('复制目录[{1}]到[{2}]成功!', (sfile, dfile))
mw.writeLog('文件管理', msg)
return mw.returnJson(True, '目录复制成功!')
except:
return mw.returnJson(False, '目录复制失败!')
# 检查敏感目录
def checkDir(self, path):
# path = str(path, encoding='utf-8')
path = path.replace('//', '/')
if path[-1:] == '/':
path = path[:-1]
nDirs = ('',
'/',
'/*',
'/www',
'/root',
'/boot',
'/bin',
'/etc',
'/home',
'/dev',
'/sbin',
'/var',
'/usr',
'/tmp',
'/sys',
'/proc',
'/media',
'/mnt',
'/opt',
'/lib',
'/srv',
'/selinux',
'/www/server',
mw.getRootDir())
return not path in nDirs
def getDirSize(self, path):
if mw.getOs() == 'darwin':
tmp = mw.execShell('du -sh ' + path)
else:
tmp = mw.execShell('du -sbh ' + path)
return tmp
def checkFileName(self, filename):
# 检测文件名
nots = ['\\', '&', '*', '|', ';']
if filename.find('/') != -1:
filename = filename.split('/')[-1]
for n in nots:
if n in filename:
return False
return True
def setFileAccept(self, filename):
auth = 'www:www'
if mw.getOs() == 'darwin':
user = mw.execShell(
"who | sed -n '2, 1p' |awk '{print $1}'")[0].strip()
auth = user + ':staff'
os.system('chown -R ' + auth + ' ' + filename)
os.system('chmod -R 755 ' + filename)
# 移动到回收站
def mvRecycleBin(self, path):
rPath = self.rPath
if not os.path.exists(rPath):
os.system('mkdir -p ' + rPath)
rFile = rPath + path.replace('/', '_mw_') + '_t_' + str(time.time())
try:
import shutil
shutil.move(path, rFile)
mw.writeLog('文件管理', mw.getInfo(
'移动文件[{1}]到回收站成功!', (path)))
return True
except:
mw.writeLog('文件管理', mw.getInfo(
'移动文件[{1}]到回收站失败!', (path)))
return False
def getBody(self, path):
if not os.path.exists(path):
return mw.returnJson(False, '文件不存在', (path,))
if os.path.getsize(path) > 2097152:
return mw.returnJson(False, '不能在线编辑大于2MB的文件!')
fp = open(path, 'rb')
data = {}
data['status'] = True
try:
if fp:
from chardet.universaldetector import UniversalDetector
detector = UniversalDetector()
srcBody = b""
for line in fp.readlines():
detector.feed(line)
srcBody += line
detector.close()
char = detector.result
data['encoding'] = char['encoding']
if char['encoding'] == 'GB2312' or not char['encoding'] or char[
'encoding'] == 'TIS-620' or char['encoding'] == 'ISO-8859-9':
data['encoding'] = 'GBK'
if char['encoding'] == 'ascii' or char[
'encoding'] == 'ISO-8859-1':
data['encoding'] = 'utf-8'
if char['encoding'] == 'Big5':
data['encoding'] = 'BIG5'
if not data['encoding'] in ['GBK', 'utf-8', 'BIG5']:
data['encoding'] = 'utf-8'
try:
if sys.version_info[0] == 2:
data['data'] = srcBody.decode(
data['encoding']).encode('utf-8', errors='ignore')
else:
data['data'] = srcBody.decode(data['encoding'])
except:
data['encoding'] = char['encoding']
if sys.version_info[0] == 2:
data['data'] = srcBody.decode(
data['encoding']).encode('utf-8', errors='ignore')
else:
data['data'] = srcBody.decode(data['encoding'])
return mw.returnJson(True, 'OK', data)
else:
if sys.version_info[0] == 2:
data['data'] = srcBody.decode('utf-8').encode('utf-8')
else:
data['data'] = srcBody.decode('utf-8')
data['encoding'] = 'utf-8'
return mw.returnJson(True, 'OK', data)
except Exception as ex:
return mw.returnJson(False, '文件编码不被兼容,无法正确读取文件!' + str(ex))
def saveBody(self, path, data, encoding='utf-8'):
if not os.path.exists(path):
return mw.returnJson(False, '文件不存在')
try:
if encoding == 'ascii':
encoding = 'utf-8'
if sys.version_info[0] == 2:
data = data.encode(encoding, errors='ignore')
fp = open(path, 'w+')
else:
data = data.encode(
encoding, errors='ignore').decode(encoding)
fp = open(path, 'w+', encoding=encoding)
fp.write(data)
fp.close()
if path.find("web_conf") > 0:
mw.restartWeb()
mw.writeLog('文件管理', '文件保存成功', (path,))
return mw.returnJson(True, '文件保存成功')
except Exception as ex:
return mw.returnJson(False, '文件保存错误:' + str(ex))
def zip(self, sfile, dfile, stype, path):
if sfile.find(',') == -1:
if not os.path.exists(path + '/' + sfile):
return mw.returnMsg(False, '指定文件不存在!')
try:
tmps = mw.getRunDir() + '/tmp/panelExec.log'
if stype == 'zip':
mw.execShell("cd '" + path + "' && zip '" + dfile +
"' -r '" + sfile + "' > " + tmps + " 2>&1")
else:
sfiles = ''
for sfile in sfile.split(','):
if not sfile:
continue
sfiles += " '" + sfile + "'"
mw.execShell("cd '" + path + "' && tar -zcvf '" +
dfile + "' " + sfiles + " > " + tmps + " 2>&1")
self.setFileAccept(dfile)
mw.writeLog("文件管理", '文件压缩成功!', (sfile, dfile))
return mw.returnJson(True, '文件压缩成功!')
except:
return mw.returnJson(False, '文件压缩失败!')
def unzip(self, sfile, dfile, stype, path):
if not os.path.exists(sfile):
return mw.returnMsg(False, '指定文件不存在!')
try:
tmps = mw.getRunDir() + '/tmp/panelExec.log'
if stype == 'zip':
mw.execShell("cd " + path + " && unzip -o -d '" + dfile +
"' '" + sfile + "' > " + tmps + " 2>&1 &")
else:
sfiles = ''
for sfile in sfile.split(','):
if not sfile:
continue
sfiles += " '" + sfile + "'"
mw.execShell("cd " + path + " && tar -zxvf " + sfiles +
" -C " + dfile + " > " + tmps + " 2>&1 &")
self.setFileAccept(dfile)
mw.writeLog("文件管理", '文件解压成功!', (sfile, dfile))
return mw.returnJson(True, '文件解压成功!')
except:
return mw.returnJson(False, '文件解压失败!')
def delete(self, path):
if not os.path.exists(path):
return mw.returnJson(False, '指定文件不存在!')
# 检查是否为.user.ini
if path.find('.user.ini') >= 0:
mw.execShell("which chattr && chattr -i '" + path + "'")
try:
if os.path.exists('data/recycle_bin.pl'):
if self.mvRecycleBin(path):
return mw.returnJson(True, '已将文件移动到回收站!')
os.remove(path)
mw.writeLog('文件管理', mw.getInfo(
'删除文件[{1}]成功!', (path)))
return mw.returnJson(True, '删除文件成功!')
except:
return mw.returnJson(False, '删除文件失败!')
def getAccess(self, filename):
data = {}
try:
stat = os.stat(filename)
data['chmod'] = str(oct(stat.st_mode)[-3:])
data['chown'] = pwd.getpwuid(stat.st_uid).pw_name
except:
data['chmod'] = 755
data['chown'] = 'www'
return data
# 计算文件数量
def getCount(self, path, search):
i = 0
for name in os.listdir(path):
if search:
if name.lower().find(search) == -1:
continue
# if name[0:1] == '.':
# continue
i += 1
return i
def getDir(self, path, page=1, page_size=10, search=None):
data = {}
dirnames = []
filenames = []
info = {}
info['count'] = self.getCount(path, search)
info['row'] = page_size
info['p'] = page
info['tojs'] = 'getFiles'
pageObj = mw.getPageObject(info, '1,2,3,4,5,6,7,8')
data['PAGE'] = pageObj[0]
i = 0
n = 0
for filename in os.listdir(path):
if search:
if filename.lower().find(search) == -1:
continue
i += 1
if n >= pageObj[1].ROW:
break
if i < pageObj[1].SHIFT:
continue
try:
filePath = path + '/' + filename
link = ''
if os.path.islink(filePath):
filePath = os.readlink(filePath)
link = ' -> ' + filePath
if not os.path.exists(filePath):
filePath = path + '/' + filePath
if not os.path.exists(filePath):
continue
stat = os.stat(filePath)
accept = str(oct(stat.st_mode)[-3:])
mtime = str(int(stat.st_mtime))
user = ''
try:
user = pwd.getpwuid(stat.st_uid).pw_name
except Exception as ee:
user = str(stat.st_uid)
size = str(stat.st_size)
if os.path.isdir(filePath):
dirnames.append(filename + ';' + size + ';' +
mtime + ';' + accept + ';' + user + ';' + link)
else:
filenames.append(filename + ';' + size + ';' +
mtime + ';' + accept + ';' + user + ';' + link)
n += 1
except Exception as e:
continue
data['DIR'] = sorted(dirnames)
data['FILES'] = sorted(filenames)
data['PATH'] = path.replace('//', '/')
return mw.getJson(data)

View File

@@ -1,463 +0,0 @@
# coding: utf-8
# ---------------------------------------------------------------------------------
# MW-Linux面板
# ---------------------------------------------------------------------------------
# copyright (c) 2018-∞(https://github.com/midoks/mdserver-web) All rights reserved.
# ---------------------------------------------------------------------------------
# Author: midoks <midoks@163.com>
# ---------------------------------------------------------------------------------
# ---------------------------------------------------------------------------------
# 防火墙操作
# ---------------------------------------------------------------------------------
import psutil
import time
import os
import sys
import mw
import re
import json
import pwd
from flask import request
class firewall_api:
__isFirewalld = False
__isUfw = False
__isMac = False
def __init__(self):
if os.path.exists('/usr/sbin/firewalld'):
self.__isFirewalld = True
if os.path.exists('/usr/sbin/ufw'):
self.__isUfw = True
if mw.isAppleSystem():
self.__isMac = True
##### ----- start ----- ###
# 添加屏蔽IP
def addDropAddressApi(self):
import re
port = request.form.get('port', '').strip()
ps = request.form.get('ps', '').strip()
rep = "^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}(\/\d{1,2})?$"
if not re.search(rep, port):
return mw.returnJson(False, '您输入的IP地址不合法!')
address = port
if mw.M('firewall').where("port=?", (address,)).count() > 0:
return mw.returnJson(False, '您要放屏蔽的IP已存在屏蔽列表,无需重复处理!')
if self.__isUfw:
mw.execShell('ufw deny from ' + address + ' to any')
else:
if self.__isFirewalld:
cmd = 'firewall-cmd --permanent --add-rich-rule=\'rule family=ipv4 source address="' + \
address + '" drop\''
mw.execShell(cmd)
else:
cmd = 'iptables -I INPUT -s ' + address + ' -j DROP'
mw.execShell(cmd)
msg = mw.getInfo('屏蔽IP[{1}]成功!', (address,))
mw.writeLog("防火墙管理", msg)
addtime = time.strftime('%Y-%m-%d %X', time.localtime())
mw.M('firewall').add('port,ps,addtime', (address, ps, addtime))
self.firewallReload()
return mw.returnJson(True, '添加成功!')
# 添加放行端口
def addAcceptPortApi(self):
if not self.getFwStatus():
return mw.returnJson(False, '防火墙启动时,才能添加规则!')
port = request.form.get('port', '').strip()
ps = request.form.get('ps', '').strip()
stype = request.form.get('type', '').strip()
data = self.addAcceptPortArgs(port, ps, stype)
return mw.getJson(data)
# 添加放行端口
def addAcceptPortArgs(self, port, ps, stype):
import re
import time
if not self.getFwStatus():
self.setFw(0)
rep = "^\d{1,5}(:\d{1,5})?$"
if not re.search(rep, port):
return mw.returnData(False, '端口范围不正确!')
if mw.M('firewall').where("port=?", (port,)).count() > 0:
return mw.returnData(False, '您要放行的端口已存在,无需重复放行!')
msg = mw.getInfo('放行端口[{1}]成功', (port,))
mw.writeLog("防火墙管理", msg)
addtime = time.strftime('%Y-%m-%d %X', time.localtime())
mw.M('firewall').add('port,ps,addtime', (port, ps, addtime))
self.addAcceptPort(port)
self.firewallReload()
return mw.returnData(True, '添加放行(' + port + ')端口成功!')
# 删除IP屏蔽
def delDropAddressApi(self):
if not self.getFwStatus():
return mw.returnJson(False, '防火墙启动时,才能删除规则!')
port = request.form.get('port', '').strip()
ps = request.form.get('ps', '').strip()
sid = request.form.get('id', '').strip()
address = port
if self.__isUfw:
mw.execShell('ufw delete deny from ' + address + ' to any')
else:
if self.__isFirewalld:
mw.execShell(
'firewall-cmd --permanent --remove-rich-rule=\'rule family=ipv4 source address="' + address + '" drop\'')
elif self.__isMac:
pass
else:
cmd = 'iptables -D INPUT -s ' + address + ' -j DROP'
mw.execShell(cmd)
msg = mw.getInfo('解除IP[{1}]的屏蔽!', (address,))
mw.writeLog("防火墙管理", msg)
mw.M('firewall').where("id=?", (sid,)).delete()
self.firewallReload()
return mw.returnJson(True, '删除成功!')
# 删除放行端口
def delAcceptPortApi(self):
port = request.form.get('port', '').strip()
sid = request.form.get('id', '').strip()
mw_port = mw.readFile('data/port.pl')
try:
if(port == mw_port):
return mw.returnJson(False, '失败,不能删除当前面板端口!')
if self.__isUfw:
mw.execShell('ufw delete allow ' + port + '/tcp')
else:
if self.__isFirewalld:
mw.execShell(
'firewall-cmd --permanent --zone=public --remove-port=' + port + '/tcp')
mw.execShell(
'firewall-cmd --permanent --zone=public --remove-port=' + port + '/udp')
else:
mw.execShell(
'iptables -D INPUT -p tcp -m state --state NEW -m tcp --dport ' + port + ' -j ACCEPT')
msg = mw.getInfo('删除防火墙放行端口[{1}]成功!', (port,))
mw.writeLog("防火墙管理", msg)
mw.M('firewall').where("id=?", (sid,)).delete()
self.firewallReload()
return mw.returnJson(True, '删除成功!')
except Exception as e:
return mw.returnJson(False, '删除失败!:' + str(e))
def getWwwPathApi(self):
path = mw.getLogsDir()
return mw.getJson({'path': path})
def getListApi(self):
p = request.form.get('p', '1').strip()
limit = request.form.get('limit', '10').strip()
return self.getList(int(p), int(limit))
def getLogListApi(self):
p = request.form.get('p', '1').strip()
limit = request.form.get('limit', '10').strip()
search = request.form.get('search', '').strip()
return self.getLogList(int(p), int(limit), search)
def getSshInfoApi(self):
data = {}
file = '/etc/ssh/sshd_config'
conf = mw.readFile(file)
rep = "#*Port\s+([0-9]+)\s*\n"
port = re.search(rep, conf).groups(0)[0]
isPing = True
try:
if mw.isAppleSystem():
isPing = True
else:
file = '/etc/sysctl.conf'
sys_conf = mw.readFile(file)
rep = "#*net\.ipv4\.icmp_echo_ignore_all\s*=\s*([0-9]+)"
tmp = re.search(rep, sys_conf).groups(0)[0]
if tmp == '1':
isPing = False
except:
isPing = True
# sshd 检测
status = True
cmd = "service sshd status | grep -P '(dead|stop)'|grep -v grep"
ssh_status = mw.execShell(cmd)
if ssh_status[0] != '':
status = False
cmd = "systemctl status sshd.service | grep 'dead'|grep -v grep"
ssh_status = mw.execShell(cmd)
if ssh_status[0] != '':
status = False
data['pass_prohibit_status'] = False
# 密码登陆配置检查
pass_rep = "#PasswordAuthentication\s+(\w*)\s*\n"
pass_status = re.search(pass_rep, conf)
if pass_status:
data['pass_prohibit_status'] = True
if not data['pass_prohibit_status']:
pass_rep = "PasswordAuthentication\s+(\w*)\s*\n"
pass_status = re.search(pass_rep, conf)
if pass_status and pass_status.groups(0)[0].strip() == 'no':
data['pass_prohibit_status'] = True
data['port'] = port
data['status'] = status
data['ping'] = isPing
if mw.isAppleSystem():
data['firewall_status'] = False
else:
data['firewall_status'] = self.getFwStatus()
return mw.getJson(data)
def setSshPortApi(self):
port = request.form.get('port', '1').strip()
if int(port) < 22 or int(port) > 65535:
return mw.returnJson(False, '端口范围必需在22-65535之间!')
ports = ['21', '25', '80', '443', '888']
if port in ports:
return mw.returnJson(False, '(' + port + ')' + '特殊端口不可设置!')
file = '/etc/ssh/sshd_config'
conf = mw.readFile(file)
rep = "#*Port\s+([0-9]+)\s*\n"
conf = re.sub(rep, "Port " + port + "\n", conf)
mw.writeFile(file, conf)
if self.__isFirewalld:
mw.execShell('setenforce 0')
mw.execShell(
'sed -i "s#SELINUX=enforcing#SELINUX=disabled#" /etc/selinux/config')
mw.execShell("systemctl restart sshd.service")
elif self.__isUfw:
mw.execShell('ufw allow ' + port + '/tcp')
mw.execShell("service ssh restart")
else:
mw.execShell(
'iptables -I INPUT -p tcp -m state --state NEW -m tcp --dport ' + port + ' -j ACCEPT')
mw.execShell("/etc/init.d/sshd restart")
self.firewallReload()
# mw.M('firewall').where(
# "ps=?", ('SSH远程管理服务',)).setField('port', port)
msg = "改SSH端口为[{}]成功!".format(port)
mw.writeLog("防火墙管理", msg)
return mw.returnJson(True, '修改成功!')
def setSshStatusApi(self):
if mw.isAppleSystem():
return mw.returnJson(True, '开发机不能操作!')
status = request.form.get('status', '1').strip()
msg = 'SSH服务已启用'
act = 'start'
if status == "1":
msg = 'SSH服务已停用'
act = 'stop'
ssh_service = mw.systemdCfgDir() + '/sshd.service'
if os.path.exists(ssh_service):
mw.execShell("systemctl " + act + " sshd.service")
else:
mw.execShell('service sshd ' + act)
if os.path.exists('/etc/init.d/sshd'):
mw.execShell('/etc/init.d/sshd ' + act)
mw.writeLog("防火墙管理", msg)
return mw.returnJson(True, '操作成功!')
def setSshPassStatusApi(self):
if mw.isAppleSystem():
return mw.returnJson(True, '开发机不能操作!')
status = request.form.get('status', '1').strip()
msg = '禁止密码登陆成功'
if status == "1":
msg = '开启密码登陆成功'
file = '/etc/ssh/sshd_config'
if not os.path.exists(file):
return mw.returnJson(False, '无法设置!')
conf = mw.readFile(file)
if status == '1':
rep = "(#)?PasswordAuthentication\s+(\w*)\s*\n"
conf = re.sub(rep, "PasswordAuthentication yes\n", conf)
else:
rep = "(#)?PasswordAuthentication\s+(\w*)\s*\n"
conf = re.sub(rep, "PasswordAuthentication no\n", conf)
mw.writeFile(file, conf)
mw.execShell("systemctl restart sshd.service")
mw.writeLog("SSH管理", msg)
return mw.returnJson(True, msg)
def setPingApi(self):
if mw.isAppleSystem():
return mw.returnJson(True, '开发机不能操作!')
status = request.form.get('status')
filename = '/etc/sysctl.conf'
conf = mw.readFile(filename)
if conf.find('net.ipv4.icmp_echo') != -1:
rep = u"net\.ipv4\.icmp_echo.*"
conf = re.sub(rep, 'net.ipv4.icmp_echo_ignore_all=' + status, conf)
else:
conf += "\nnet.ipv4.icmp_echo_ignore_all=" + status
mw.writeFile(filename, conf)
mw.execShell('sysctl -p')
return mw.returnJson(True, '设置成功!')
def setFwApi(self):
if mw.isAppleSystem():
return mw.returnJson(True, '开发机不能设置!')
status = request.form.get('status', '1')
return mw.getJson(self.setFw(status))
def setFw(self, status):
if status == '1':
if self.__isUfw:
mw.execShell('/usr/sbin/ufw disable')
if self.__isFirewalld:
mw.execShell('systemctl stop firewalld.service')
mw.execShell('systemctl disable firewalld.service')
elif self.__isMac:
pass
else:
mw.execShell('/etc/init.d/iptables save')
mw.execShell('/etc/init.d/iptables stop')
else:
if self.__isUfw:
mw.execShell("echo 'y'|sudo ufw enable")
if self.__isFirewalld:
mw.execShell('systemctl start firewalld.service')
mw.execShell('systemctl enable firewalld.service')
elif self.__isMac:
pass
else:
mw.execShell('/etc/init.d/iptables save')
mw.execShell('/etc/init.d/iptables restart')
return mw.returnData(True, '设置成功!')
def delPanelLogsApi(self):
mw.M('logs').where('id>?', (0,)).delete()
mw.writeLog('面板设置', '面板操作日志已清空!')
return mw.returnJson(True, '面板操作日志已清空!')
##### ----- start ----- ###
def getList(self, page, limit):
start = (page - 1) * limit
_list = mw.M('firewall').field('id,port,ps,addtime').limit(
str(start) + ',' + str(limit)).order('id desc').select()
data = {}
data['data'] = _list
count = mw.M('firewall').count()
_page = {}
_page['count'] = count
_page['tojs'] = 'showAccept'
_page['p'] = page
data['page'] = mw.getPage(_page)
return mw.getJson(data)
def getLogList(self, page, limit, search=''):
find_search = ''
if search != '':
find_search = "type like '%" + search + "%' or log like '%" + \
search + "%' or addtime like '%" + search + "%'"
start = (page - 1) * limit
_list = mw.M('logs').where(find_search, ()).field(
'id,type,log,addtime').limit(str(start) + ',' + str(limit)).order('id desc').select()
data = {}
data['data'] = _list
count = mw.M('logs').where(find_search, ()).count()
_page = {}
_page['count'] = count
_page['tojs'] = 'getLogs'
_page['p'] = page
data['page'] = mw.getPage(_page)
return mw.getJson(data)
def addAcceptPort(self, port):
if self.__isUfw:
mw.execShell('ufw allow ' + port + '/tcp')
elif self.__isFirewalld:
port = port.replace(':', '-')
cmd = 'firewall-cmd --permanent --zone=public --add-port=' + port + '/tcp'
mw.execShell(cmd)
elif self.__isMac:
pass
else:
cmd = 'iptables -I INPUT -p tcp -m state --state NEW -m tcp --dport ' + port + ' -j ACCEPT'
mw.execShell(cmd)
def firewallReload(self):
if self.__isUfw:
mw.execShell('/usr/sbin/ufw reload')
return
if self.__isFirewalld:
mw.execShell('firewall-cmd --reload')
elif self.__isMac:
pass
else:
mw.execShell('/etc/init.d/iptables save')
mw.execShell('/etc/init.d/iptables restart')
def getFwStatus(self):
if self.__isUfw:
cmd = "/usr/sbin/ufw status| grep Status | awk -F ':' '{print $2}'"
data = mw.execShell(cmd)
if data[0].strip() == 'inactive':
return False
return True
if self.__isFirewalld:
cmd = "ps -ef|grep firewalld |grep -v grep | awk '{print $2}'"
data = mw.execShell(cmd)
if data[0] == '':
return False
return True
elif self.__isMac:
return False
else:
cmd = "ps -ef|grep iptables |grep -v grep | awk '{print $2}'"
data = mw.execShell(cmd)
if data[0] == '':
return False
return True

File diff suppressed because it is too large Load Diff

View File

@@ -1,237 +0,0 @@
# coding: utf-8
# ---------------------------------------------------------------------------------
# MW-Linux面板
# ---------------------------------------------------------------------------------
# copyright (c) 2018-∞(https://github.com/midoks/mdserver-web) All rights reserved.
# ---------------------------------------------------------------------------------
# Author: midoks <midoks@163.com>
# ---------------------------------------------------------------------------------
# ---------------------------------------------------------------------------------
# 分页库操作
# ---------------------------------------------------------------------------------
import math
import string
class Page():
#--------------------------
# 分页类 - JS回调版
#--------------------------
__PREV = '上一页'
__NEXT = '下一页'
__START = '首页'
__END = '尾页'
__COUNT_START = '共'
__COUNT_END = '条数据'
__FO = '从'
__LINE = '条'
__LIST_NUM = 4
SHIFT = None # 偏移量
ROW = None # 每页行数
__C_PAGE = None # 当前页
__COUNT_PAGE = None # 总页数
__COUNT_ROW = None # 总行数
__URI = None # URI
__RTURN_JS = False # 是否返回JS回调
__START_NUM = None # 起始行
__END_NUM = None # 结束行
def __init__(self):
if False:
self.__PREV = tmp['PREV']
self.__NEXT = tmp['NEXT']
self.__START = tmp['START']
self.__END = tmp['END']
self.__COUNT_START = tmp['COUNT_START']
self.__COUNT_END = tmp['COUNT_END']
self.__FO = tmp['FO']
self.__LINE = tmp['LINE']
def GetPage(self, pageInfo, limit='1,2,3,4,5,6,7,8'):
# 取分页信息
# @param pageInfo 传入分页参数字典
# @param limit 返回系列
self.__RTURN_JS = pageInfo['return_js']
self.__COUNT_ROW = pageInfo['count']
self.ROW = pageInfo['row']
self.__C_PAGE = self.__GetCpage(pageInfo['p'])
self.__START_NUM = self.__StartRow()
self.__END_NUM = self.__EndRow()
self.__COUNT_PAGE = self.__GetCountPage()
self.__URI = self.__SetUri(pageInfo['uri'])
self.SHIFT = self.__START_NUM - 1
keys = limit.split(',')
pages = {}
# 起始页
pages['1'] = self.__GetStart()
# 上一页
pages['2'] = self.__GetPrev()
# 分页
pages['3'] = self.__GetPages()
# 下一页
pages['4'] = self.__GetNext()
# 尾页
pages['5'] = self.__GetEnd()
# 当前显示页与总页数
pages['6'] = "<span class='Pnumber'>" + \
str(self.__C_PAGE) + "/" + \
str(self.__COUNT_PAGE) + "</span>"
# 本页显示开始与结束行
pages['7'] = "<span class='Pline'>" + str(self.__FO) + \
str(self.__START_NUM) + "-" + \
str(self.__END_NUM) + str(self.__LINE) + "</span>"
# 行数
pages['8'] = "<span class='Pcount'>" + str(self.__COUNT_START) + \
str(self.__COUNT_ROW) + str(self.__COUNT_END) + "</span>"
# 构造返回数据
retuls = '<div>'
for value in keys:
retuls += pages[value]
retuls += '</div>'
# 返回分页数据
return retuls
def __GetEnd(self):
# 构造尾页
endStr = ""
if self.__C_PAGE >= self.__COUNT_PAGE:
endStr = ''
else:
if self.__RTURN_JS == "":
endStr = "<a class='Pend' href='" + self.__URI + "p=" + \
str(self.__COUNT_PAGE) + "'>" + str(self.__END) + "</a>"
else:
endStr = "<a class='Pend' onclick='" + self.__RTURN_JS + \
"(" + str(self.__COUNT_PAGE) + ")'>" + \
str(self.__END) + "</a>"
return endStr
def __GetNext(self):
# 构造下一页
nextStr = ""
if self.__C_PAGE >= self.__COUNT_PAGE:
nextStr = ''
else:
if self.__RTURN_JS == "":
nextStr = "<a class='Pnext' href='" + self.__URI + "p=" + \
str(self.__C_PAGE + 1) + "'>" + str(self.__NEXT) + "</a>"
else:
nextStr = "<a class='Pnext' onclick='" + self.__RTURN_JS + \
"(" + str(self.__C_PAGE + 1) + ")'>" + \
str(self.__NEXT) + "</a>"
return nextStr
def __GetPages(self):
# 构造分页
pages = ''
num = 0
# 当前页之前
if (self.__COUNT_PAGE - self.__C_PAGE) < self.__LIST_NUM:
num = self.__LIST_NUM + \
(self.__LIST_NUM - (self.__COUNT_PAGE - self.__C_PAGE))
else:
num = self.__LIST_NUM
n = 0
for i in range(num):
n = num - i
page = self.__C_PAGE - n
if page > 0:
if self.__RTURN_JS == "":
pages += "<a class='Pnum' href='" + self.__URI + \
"p=" + str(page) + "'>" + str(page) + "</a>"
else:
pages += "<a class='Pnum' onclick='" + self.__RTURN_JS + \
"(" + str(page) + ")'>" + str(page) + "</a>"
# 当前页
if self.__C_PAGE > 0:
pages += "<span class='Pcurrent'>" + \
str(self.__C_PAGE) + "</span>"
# 当前页之后
if self.__C_PAGE <= self.__LIST_NUM:
num = self.__LIST_NUM + (self.__LIST_NUM - self.__C_PAGE) + 1
else:
num = self.__LIST_NUM
for i in range(num):
if i == 0:
continue
page = self.__C_PAGE + i
if page > self.__COUNT_PAGE:
break
if self.__RTURN_JS == "":
pages += "<a class='Pnum' href='" + self.__URI + \
"p=" + str(page) + "'>" + str(page) + "</a>"
else:
pages += "<a class='Pnum' onclick='" + self.__RTURN_JS + \
"(" + str(page) + ")'>" + str(page) + "</a>"
return pages
def __GetPrev(self):
# 构造上一页
startStr = ''
if self.__C_PAGE == 1:
startStr = ''
else:
if self.__RTURN_JS == "":
startStr = "<a class='Ppren' href='" + self.__URI + "p=" + \
str(self.__C_PAGE - 1) + "'>" + str(self.__PREV) + "</a>"
else:
startStr = "<a class='Ppren' onclick='" + self.__RTURN_JS + \
"(" + str(self.__C_PAGE - 1) + ")'>" + \
str(self.__PREV) + "</a>"
return startStr
def __GetStart(self):
# 构造起始分页
startStr = ''
if self.__C_PAGE == 1:
startStr = ''
else:
if self.__RTURN_JS == "":
startStr = "<a class='Pstart' href='" + \
self.__URI + "p=1'>" + str(self.__START) + "</a>"
else:
startStr = "<a class='Pstart' onclick='" + \
self.__RTURN_JS + "(1)'>" + str(self.__START) + "</a>"
return startStr
def __GetCpage(self, p):
# 取当前页
if p:
return p
return 1
def __StartRow(self):
# 从多少行开始
return (self.__C_PAGE - 1) * self.ROW + 1
def __EndRow(self):
# 从多少行结束
if self.ROW > self.__COUNT_ROW:
return self.__COUNT_ROW
return self.__C_PAGE * self.ROW
def __GetCountPage(self):
# 取总页数
return int(math.ceil(self.__COUNT_ROW / float(self.ROW)))
def __SetUri(self, sinput):
# 构造URI
uri = '?'
for key in sinput:
if key == 'p':
continue
uri += key + '=' + sinput[key] + '&'
return str(uri)

View File

@@ -1,985 +0,0 @@
# coding: utf-8
# ---------------------------------------------------------------------------------
# MW-Linux面板
# ---------------------------------------------------------------------------------
# copyright (c) 2018-∞(https://github.com/midoks/mdserver-web) All rights reserved.
# ---------------------------------------------------------------------------------
# Author: midoks <midoks@163.com>
# ---------------------------------------------------------------------------------
# ---------------------------------------------------------------------------------
# 插件操作
# ---------------------------------------------------------------------------------
import psutil
import time
import os
import mw
import re
import json
import sys
# reload(sys)
# sys.setdefaultencoding('utf8')
import threading
import multiprocessing
from flask import request
class pa_thread(threading.Thread):
def __init__(self, func, args, name=''):
threading.Thread.__init__(self)
self.name = name
self.func = func
self.args = args
self.result = self.func(*self.args)
def getResult(self):
try:
return self.result
except Exception:
return None
class plugins_api:
__tasks = None
__plugin_dir = 'plugins'
__type = 'data/json/type.json'
__index = 'data/json/index.json'
setupPath = None
def __init__(self):
self.setupPath = 'server'
self.__plugin_dir = mw.getRunDir() + '/plugins'
self.__type = mw.getRunDir() + '/data/json/type.json'
self.__index = mw.getRunDir() + '/data/json/index.json'
##### ----- start ----- ###
def listApi(self):
sType = request.args.get('type', '0')
sPage = request.args.get('p', '1')
if not mw.isNumber(sPage):
sPage = 1
if not mw.isNumber(sType):
sType = 0
# print sPage
data = self.getPluginList(sType, int(sPage))
return mw.getJson(data)
def fileApi(self):
name = request.args.get('name', '')
if name.strip() == '':
return ''
f = request.args.get('f', '')
if f.strip() == '':
return ''
file = mw.getPluginDir() + '/' + name + '/' + f
if not os.path.exists(file):
return ''
c = open(file, 'rb').read()
return c
def indexListApi(self):
data = self.getIndexList()
return mw.getJson(data)
def indexSortApi(self):
sort = request.form.get('ssort', '')
if sort.strip() == '':
return mw.returnJson(False, '排序数据不能为空!')
data = self.setIndexSort(sort)
if data:
return mw.returnJson(True, '成功!')
return mw.returnJson(False, '失败!')
def initApi(self):
plugin_names = {
'openresty': '1.21.4.1',
'php': '56',
'swap': '1.1',
'mysql': '5.7',
'phpmyadmin': '4.4.15',
}
pn_dir = mw.getPluginDir()
pn_server_dir = mw.getServerDir()
pn_list = []
for pn in plugin_names:
info = {}
pn_json = pn_dir + '/' + pn + '/info.json'
pn_server = pn_server_dir + '/' + pn
if not os.path.exists(pn_server):
tmp = mw.readFile(pn_json)
tmp = json.loads(tmp)
info['title'] = tmp['title']
info['name'] = tmp['name']
info['versions'] = tmp['versions']
info['default_ver'] = plugin_names[pn]
pn_list.append(info)
else:
return mw.returnJson(False, 'ok')
return mw.returnJson(True, 'ok', pn_list)
def initInstallApi(self):
pn_list = request.form.get('list', '')
try:
pn_list = json.loads(pn_list)
for pn in pn_list:
name = pn['name']
version = pn['version']
infoJsonPos = self.__plugin_dir + '/' + name + '/' + 'info.json'
pluginInfo = json.loads(mw.readFile(infoJsonPos))
self.hookInstall(pluginInfo)
execstr = 'cd ' + mw.getPluginDir() + '/' + name + ' && bash ' + \
pluginInfo['shell'] + ' install ' + version
taskAdd = ('安装[' + name + '-' + version + ']',
'execshell', '0', time.strftime('%Y-%m-%d %H:%M:%S'), execstr)
mw.M('tasks').add('name,type,status,addtime, execstr', taskAdd)
os.mkdir(mw.getServerDir() + '/php')
# 任务执行相关
mw.triggerTask()
return mw.returnJson(True, '添加成功')
except Exception as e:
return mw.returnJson(False, mw.getTracebackInfo())
def installApi(self):
rundir = mw.getRunDir()
name = request.form.get('name', '')
version = request.form.get('version', '')
mmsg = '安装'
if hasattr(request.form, 'upgrade'):
mtype = 'update'
mmsg = 'upgrade'
if name.strip() == '':
return mw.returnJson(False, '缺少插件名称!', ())
if version.strip() == '':
return mw.returnJson(False, '缺少版本信息!', ())
infoJsonPos = self.__plugin_dir + '/' + name + '/' + 'info.json'
# print infoJsonPos
if not os.path.exists(infoJsonPos):
return mw.returnJson(False, '配置文件不存在!', ())
pluginInfo = json.loads(mw.readFile(infoJsonPos))
self.hookInstall(pluginInfo)
execstr = 'cd ' + mw.getPluginDir() + '/' + name + ' && bash ' + \
pluginInfo['shell'] + ' install ' + version
if mw.isAppleSystem():
print(execstr)
taskAdd = (mmsg + '[' + name + '-' + version + ']',
'execshell', '0', time.strftime('%Y-%m-%d %H:%M:%S'), execstr)
mw.M('tasks').add('name,type,status,addtime, execstr', taskAdd)
# 任务执行相关
mw.triggerTask()
return mw.returnJson(True, '已将安装任务添加到队列!')
def hookInstallFile(self, hook_name, info):
hookPath = mw.getPanelDataDir() + "/hook_" + hook_name + ".json"
data = []
if os.path.exists(hookPath):
t = mw.readFile(hookPath)
data = json.loads(t)
isNeedAdd = True
for x in range(len(data)):
if data[x]['title'] == info['title'] and data[x]['name'] == info['name']:
isNeedAdd = False
if isNeedAdd:
tmp = {}
tmp['title'] = info['title']
tmp['name'] = info['name']
data.append(tmp)
mw.writeFile(hookPath, json.dumps(data))
def hookUninstallFile(self, hook_name, info):
hookPath = mw.getPanelDataDir() + "/hook_" + hook_name + ".json"
data = []
if os.path.exists(hookPath):
t = mw.readFile(hookPath)
data = json.loads(t)
for idx in range(len(data)):
if data[idx]['title'] == info['title'] and data[idx]['name'] == info['name']:
data.remove(data[idx])
mw.writeFile(hookPath, json.dumps(data))
def hookInstall(self, info):
if 'hook' in info:
hooks = info['hook']
for x in hooks:
if x in ['backup']:
self.hookInstallFile(x, info)
return True
return False
def hookUninstall(self, info):
if 'hook' in info:
hooks = info['hook']
for x in hooks:
if x in ['backup']:
self.hookUninstallFile(x, info)
return True
return False
def uninstallOldApi(self):
rundir = mw.getRunDir()
name = request.form.get('name', '')
version = request.form.get('version', '')
if name.strip() == '':
return mw.returnJson(False, "缺少插件名称!", ())
if version.strip() == '':
return mw.returnJson(False, "缺少版本信息!", ())
infoJsonPos = self.__plugin_dir + '/' + name + '/' + 'info.json'
if not os.path.exists(infoJsonPos):
return mw.returnJson(False, "配置文件不存在!", ())
pluginInfo = json.loads(mw.readFile(infoJsonPos))
execstr = "cd " + os.getcwd() + "/plugins/" + \
name + " && /bin/bash " + pluginInfo["shell"] \
+ " uninstall " + version
taskAdd = (None, '卸载[' + name + '-' + version + ']',
'execshell', '0', time.strftime('%Y-%m-%d %H:%M:%S'), execstr)
mw.M('tasks').add('id,name,type,status,addtime, execstr', taskAdd)
return mw.returnJson(True, '已将卸载任务添加到队列!')
# 卸载时间短,不加入任务中...
def uninstallApi(self):
rundir = mw.getRunDir()
name = request.form.get('name', '')
version = request.form.get('version', '')
if name.strip() == '':
return mw.returnJson(False, "缺少插件名称!", ())
if version.strip() == '':
return mw.returnJson(False, "缺少版本信息!", ())
infoJsonPos = self.__plugin_dir + '/' + name + '/' + 'info.json'
if not os.path.exists(infoJsonPos):
return mw.returnJson(False, "配置文件不存在!", ())
pluginInfo = json.loads(mw.readFile(infoJsonPos))
self.hookUninstall(pluginInfo)
execstr = "cd " + os.getcwd() + "/plugins/" + \
name + " && /bin/bash " + pluginInfo["shell"] \
+ " uninstall " + version
data = mw.execShell(execstr)
if mw.isDebugMode():
print(execstr)
print(data[0], data[1])
self.removeIndex(name, version)
return mw.returnJson(True, '卸载执行成功!')
# if data[1] == '':
# return mw.returnJson(True, '已将卸载成功!')
# else:
# return mw.returnJson(False, '卸载出现错误信息!' + data[1])
def checkApi(self):
name = request.form.get('name', '')
if name.strip() == '':
return mw.returnJson(False, "缺少插件名称!", ())
infoJsonPos = self.__plugin_dir + '/' + name + '/' + 'info.json'
if not os.path.exists(infoJsonPos):
return mw.returnJson(False, "配置文件不存在!", ())
return mw.returnJson(True, "插件存在!", ())
def setIndexApi(self):
name = request.form.get('name', '')
status = request.form.get('status', '0')
version = request.form.get('version', '')
if status == '1':
return self.addIndex(name, version)
return self.removeIndex(name, version)
def settingApi(self):
name = request.args.get('name', '')
html = self.__plugin_dir + '/' + name + '/index.html'
return mw.readFile(html)
def runApi(self):
name = request.form.get('name', '')
func = request.form.get('func', '')
version = request.form.get('version', '')
args = request.form.get('args', '')
script = request.form.get('script', 'index')
data = self.run(name, func, version, args, script)
if data[1] == '':
r = mw.returnJson(True, "OK", data[0].strip())
else:
r = mw.returnJson(False, data[1].strip())
return r
def callbackApi(self):
name = request.form.get('name', '')
func = request.form.get('func', '')
args = request.form.get('args', '')
script = request.form.get('script', 'index')
data = self.callback(name, func, args, script)
if data[0]:
return mw.returnJson(True, "OK", data[1])
return mw.returnJson(False, data[1])
def updateZipApi(self):
tmp_path = mw.getRootDir() + '/temp'
if not os.path.exists(tmp_path):
os.makedirs(tmp_path)
mw.execShell("rm -rf " + tmp_path + '/*')
tmp_file = tmp_path + '/plugin_tmp.zip'
from werkzeug.utils import secure_filename
from flask import request
f = request.files['plugin_zip']
if f.filename[-4:] != '.zip':
return mw.returnJson(False, '仅支持zip文件!')
f.save(tmp_file)
mw.execShell('cd ' + tmp_path + ' && unzip ' + tmp_file)
os.remove(tmp_file)
p_info = tmp_path + '/info.json'
if not os.path.exists(p_info):
d_path = None
for df in os.walk(tmp_path):
if len(df[2]) < 3:
continue
if not 'info.json' in df[2]:
continue
if not 'install.sh' in df[2]:
continue
if not os.path.exists(df[0] + '/info.json'):
continue
d_path = df[0]
if d_path:
tmp_path = d_path
p_info = tmp_path + '/info.json'
try:
data = json.loads(mw.readFile(p_info))
data['size'] = mw.getPathSize(tmp_path)
if not 'author' in data:
data['author'] = '未知'
if not 'home' in data:
data['home'] = 'https://github.com/midoks/mdserver-web'
plugin_path = mw.getPluginDir() + data['name'] + '/info.json'
data['old_version'] = '0'
data['tmp_path'] = tmp_path
if os.path.exists(plugin_path):
try:
old_info = json.loads(mw.ReadFile(plugin_path))
data['old_version'] = old_info['versions']
except:
pass
except:
mw.execShell("rm -rf " + tmp_path)
return mw.returnJson(False, '在压缩包中没有找到插件信息,请检查插件包!')
protectPlist = ('openresty', 'mysql', 'php', 'redis', 'memcached'
'mongodb', 'swap', 'gogs', 'pureftp')
if data['name'] in protectPlist:
return mw.returnJson(False, '[' + data['name'] + '],重要插件不可修改!')
return mw.getJson(data)
def inputZipApi(self):
plugin_name = request.form.get('plugin_name', '')
tmp_path = request.form.get('tmp_path', '')
if not os.path.exists(tmp_path):
return mw.returnJson(False, '临时文件不存在,请重新上传!')
plugin_path = mw.getPluginDir() + '/' + plugin_name
if not os.path.exists(plugin_path):
print(mw.execShell('mkdir -p ' + plugin_path))
mw.execShell("\cp -rf " + tmp_path + '/* ' + plugin_path + '/')
mw.execShell('chmod -R 755 ' + plugin_path)
p_info = mw.readFile(plugin_path + '/info.json')
if p_info:
mw.writeLog('软件管理', '安装第三方插件[%s]' %
json.loads(p_info)['title'])
return mw.returnJson(True, '安装成功!')
mw.execShell("rm -rf " + plugin_path)
return mw.returnJson(False, '安装失败!')
##### ----- end ----- ###
# 进程是否存在
def processExists(self, pname, exe=None):
try:
if not self.pids:
self.pids = psutil.pids()
for pid in self.pids:
try:
p = psutil.Process(pid)
if p.name() == pname:
if not exe:
return True
else:
if p.exe() == exe:
return True
except:
pass
return False
except:
return True
# 检查是否正在安装
def checkSetupTask(self, sName, sVer, sCoexist):
if not self.__tasks:
self.__tasks = mw.M('tasks').where(
"status!=?", ('1',)).field('status,name').select()
isTask = '1'
for task in self.__tasks:
tmpt = mw.getStrBetween('[', ']', task['name'])
if not tmpt:
continue
task_sign = tmpt.split('-')
task_len = len(task_sign)
task_name = task_sign[0].lower()
task_ver = task_sign[1]
if task_len > 2:
nameArr = task_sign[0:task_len - 1]
task_name = '-'.join(nameArr).lower()
task_ver = task_sign[task_len - 1]
if sCoexist:
if task_name == sName and task_ver == sVer:
isTask = task['status']
else:
if task_name == sName:
isTask = task['status']
return isTask
def checkStatus(self, info):
if not info['setup']:
return False
data = self.run(info['name'], 'status', info['setup_version'])
if data[0] == 'start':
return True
return False
def checkStatusProcess(self, info, i, return_dict):
if not info['setup']:
return_dict[i] = False
return
data = self.run(info['name'], 'status', info['setup_version'])
if data[0] == 'start':
return_dict[i] = True
else:
return_dict[i] = False
def checkStatusThreads(self, info, i):
if not info['setup']:
return False
data = self.run(info['name'], 'status', info['setup_version'])
if data[0] == 'start':
return True
else:
return False
# 还无法正常运行,先保留
def checkStatusThreadsCallback(self, info, i):
if not info['setup']:
return False
try:
data = self.callback(info['name'], 'status', info['setup_version'])
except Exception as e:
data = self.callback(info['name'], 'status')
# data = self.run(info['name'], 'status', info['setup_version'])
if data[0] == 'start':
return True
else:
return False
def checkStatusMThreads(self, plugins_info):
try:
threads = []
ntmp_list = range(len(plugins_info))
for i in ntmp_list:
t = pa_thread(self.checkStatusThreads,
(plugins_info[i], i))
threads.append(t)
for i in ntmp_list:
threads[i].start()
for i in ntmp_list:
threads[i].join()
for i in ntmp_list:
t = threads[i].getResult()
plugins_info[i]['status'] = t
except Exception as e:
print('checkStatusMThreads:', str(e))
return plugins_info
def checkStatusMProcess(self, plugins_info):
manager = multiprocessing.Manager()
return_dict = manager.dict()
jobs = []
for i in range(len(plugins_info)):
p = multiprocessing.Process(
target=self.checkStatusProcess, args=(plugins_info[i], i, return_dict))
jobs.append(p)
p.start()
for proc in jobs:
proc.join()
returnData = return_dict.values()
for i in ntmp_list:
plugins_info[i]['status'] = returnData[i]
return plugins_info
def checkDisplayIndex(self, name, version):
if not os.path.exists(self.__index):
mw.writeFile(self.__index, '[]')
indexList = json.loads(mw.readFile(self.__index))
if type(version) == list:
for index in range(len(version)):
vname = name + '-' + version[index]
if vname in indexList:
return True
else:
vname = name + '-' + version
if vname in indexList:
return True
return False
def getVersion(self, path):
version_f = path + '/version.pl'
if os.path.exists(version_f):
return mw.readFile(version_f).strip()
return ''
# 构造本地插件信息
def getPluginInfo(self, info):
checks = ''
path = ''
coexist = False
if info["checks"][0:1] == '/':
checks = info["checks"]
else:
checks = mw.getRootDir() + '/' + info['checks']
if 'path' in info:
path = info['path']
if path[0:1] != '/':
path = mw.getRootDir() + '/' + path
if 'coexist' in info and info['coexist']:
coexist = True
pInfo = {
"id": 10000,
"pid": info['pid'],
"type": 1000,
"name": info['name'],
"title": info['title'],
"ps": info['ps'],
"dependnet": "",
"mutex": "",
"path": path,
"install_checks": checks,
"uninsatll_checks": checks,
"coexist": coexist,
"versions": info['versions'],
# "updates": info['updates'],
"display": False,
"setup": False,
"setup_version": "",
"status": False,
"install_pre_inspection": False,
"uninstall_pre_inspection": False,
}
if checks.find('VERSION') > -1:
pInfo['install_checks'] = checks.replace(
'VERSION', info['versions'])
if path.find('VERSION') > -1:
pInfo['path'] = path.replace(
'VERSION', info['versions'])
pInfo['task'] = self.checkSetupTask(
pInfo['name'], info['versions'], coexist)
pInfo['display'] = self.checkDisplayIndex(
info['name'], pInfo['versions'])
pInfo['setup'] = os.path.exists(pInfo['install_checks'])
if coexist and pInfo['setup']:
pInfo['setup_version'] = info['versions']
else:
pInfo['setup_version'] = self.getVersion(pInfo['install_checks'])
# pluginInfo['status'] = self.checkStatus(pluginInfo)
if 'install_pre_inspection' in info:
pInfo['install_pre_inspection'] = info['install_pre_inspection']
if 'uninstall_pre_inspection' in info:
pInfo['uninstall_pre_inspection'] = info[
'uninstall_pre_inspection']
pInfo['status'] = False
return pInfo
def makeCoexist(self, data):
plugins_info = []
for index in range(len(data['versions'])):
tmp = data.copy()
tmp['title'] = tmp['title'] + \
'-' + data['versions'][index]
tmp['versions'] = data['versions'][index]
pg = self.getPluginInfo(tmp)
plugins_info.append(pg)
return plugins_info
def makeList(self, data, sType='0'):
plugins_info = []
if (data['pid'] == sType):
if type(data['versions']) == list and 'coexist' in data and data['coexist']:
tmp_data = self.makeCoexist(data)
for index in range(len(tmp_data)):
plugins_info.append(tmp_data[index])
else:
pg = self.getPluginInfo(data)
plugins_info.append(pg)
return plugins_info
if sType == '0':
if type(data['versions']) == list and 'coexist' in data and data['coexist']:
tmp_data = self.makeCoexist(data)
for index in range(len(tmp_data)):
plugins_info.append(tmp_data[index])
else:
pg = self.getPluginInfo(data)
plugins_info.append(pg)
# print plugins_info, data
return plugins_info
def getAllList(self, sType='0'):
plugins_info = []
for dirinfo in os.listdir(self.__plugin_dir):
if dirinfo[0:1] == '.':
continue
path = self.__plugin_dir + '/' + dirinfo
json_file = path + '/info.json'
if os.path.exists(json_file):
try:
data = json.loads(mw.readFile(json_file))
tmp_data = self.makeList(data, sType)
for index in range(len(tmp_data)):
plugins_info.append(tmp_data[index])
except Exception as e:
print(e)
return plugins_info
def getAllListPage(self, sType='0', page=1, pageSize=10):
plugins_info = []
for dirinfo in os.listdir(self.__plugin_dir):
if dirinfo[0:1] == '.':
continue
path = self.__plugin_dir + '/' + dirinfo
if os.path.isdir(path):
json_file = path + '/info.json'
if os.path.exists(json_file):
try:
data = json.loads(mw.readFile(json_file))
tmp_data = self.makeList(data, sType)
for index in range(len(tmp_data)):
plugins_info.append(tmp_data[index])
except Exception as e:
print(e)
start = (page - 1) * pageSize
end = start + pageSize
_plugins_info = plugins_info[start:end]
_plugins_info = self.checkStatusMThreads(_plugins_info)
return (_plugins_info, len(plugins_info))
def makeListThread(self, data, sType='0'):
plugins_info = []
if (data['pid'] == sType):
if type(data['versions']) == list and data.has_key('coexist') and data['coexist']:
tmp_data = self.makeCoexist(data)
for index in range(len(tmp_data)):
plugins_info.append(tmp_data[index])
else:
pg = self.getPluginInfo(data)
plugins_info.append(pg)
return plugins_info
if sType == '0':
if type(data['versions']) == list and data.has_key('coexist') and data['coexist']:
tmp_data = self.makeCoexist(data)
for index in range(len(tmp_data)):
plugins_info.append(tmp_data[index])
else:
pg = self.getPluginInfo(data)
plugins_info.append(pg)
# print plugins_info, data
return plugins_info
def getAllListThread(self, sType='0'):
plugins_info = []
tmp_list = []
threads = []
for dirinfo in os.listdir(self.__plugin_dir):
if dirinfo[0:1] == '.':
continue
path = self.__plugin_dir + '/' + dirinfo
if os.path.isdir(path):
json_file = path + '/info.json'
if os.path.exists(json_file):
data = json.loads(mw.readFile(json_file))
if sType == '0':
tmp_list.append(data)
if (data['pid'] == sType):
tmp_list.append(data)
ntmp_list = range(len(tmp_list))
for i in ntmp_list:
t = pa_thread(self.makeListThread, (tmp_list[i], sType))
threads.append(t)
for i in ntmp_list:
threads[i].start()
for i in ntmp_list:
threads[i].join()
for i in ntmp_list:
t = threads[i].getResult()
for index in range(len(t)):
plugins_info.append(t[index])
return plugins_info
def makeListProcess(self, data, sType, i, return_dict):
plugins_info = []
if (data['pid'] == sType):
if type(data['versions']) == list and data.has_key('coexist') and data['coexist']:
tmp_data = self.makeCoexist(data)
for index in range(len(tmp_data)):
plugins_info.append(tmp_data[index])
else:
pg = self.getPluginInfo(data)
plugins_info.append(pg)
# return plugins_info
if sType == '0':
if type(data['versions']) == list and data.has_key('coexist') and data['coexist']:
tmp_data = self.makeCoexist(data)
for index in range(len(tmp_data)):
plugins_info.append(tmp_data[index])
else:
pg = self.getPluginInfo(data)
plugins_info.append(pg)
return_dict[i] = plugins_info
# return plugins_info
def getAllListProcess(self, sType='0'):
plugins_info = []
tmp_list = []
manager = multiprocessing.Manager()
return_dict = manager.dict()
jobs = []
for dirinfo in os.listdir(self.__plugin_dir):
if dirinfo[0:1] == '.':
continue
path = self.__plugin_dir + '/' + dirinfo
if os.path.isdir(path):
json_file = path + '/info.json'
if os.path.exists(json_file):
data = json.loads(mw.readFile(json_file))
if sType == '0':
tmp_list.append(data)
if (data['pid'] == sType):
tmp_list.append(data)
ntmp_list = range(len(tmp_list))
for i in ntmp_list:
p = multiprocessing.Process(
target=self.makeListProcess, args=(tmp_list[i], sType, i, return_dict))
jobs.append(p)
p.start()
for proc in jobs:
proc.join()
returnData = return_dict.values()
for i in ntmp_list:
for index in range(len(returnData[i])):
plugins_info.append(returnData[i][index])
return plugins_info
def getPluginList(self, sType, sPage=1, sPageSize=10):
# print sType, sPage, sPageSize
ret = {}
ret['type'] = json.loads(mw.readFile(self.__type))
# plugins_info = self.getAllListThread(sType)
# plugins_info = self.getAllListProcess(sType)
data = self.getAllListPage(sType, sPage, sPageSize)
ret['data'] = data[0]
args = {}
args['count'] = data[1]
args['p'] = sPage
args['tojs'] = 'getSList'
args['row'] = sPageSize
ret['list'] = mw.getPage(args)
return ret
def getIndexList(self):
if not os.path.exists(self.__index):
mw.writeFile(self.__index, '[]')
indexList = json.loads(mw.readFile(self.__index))
plist = []
for i in indexList:
tmp = i.split('-')
tmp_len = len(tmp)
plugin_name = tmp[0]
plugin_ver = tmp[1]
if tmp_len > 2:
tmpArr = tmp[0:tmp_len - 1]
plugin_name = '-'.join(tmpArr)
plugin_ver = tmp[tmp_len - 1]
json_file = self.__plugin_dir + '/' + plugin_name + '/info.json'
if os.path.exists(json_file):
content = mw.readFile(json_file)
try:
data = json.loads(content)
data = self.makeList(data)
for index in range(len(data)):
if data[index]['versions'] == plugin_ver or plugin_ver in data[index]['versions']:
data[index]['display'] = True
plist.append(data[index])
continue
except Exception as e:
print('getIndexList:', e)
# 使用gevent模式时,无法使用多进程
# plist = self.checkStatusMProcess(plist)
plist = self.checkStatusMThreads(plist)
return plist
def setIndexSort(self, sort):
data = sort.split('|')
mw.writeFile(self.__index, json.dumps(data))
return True
def addIndex(self, name, version):
if not os.path.exists(self.__index):
mw.writeFile(self.__index, '[]')
indexList = json.loads(mw.readFile(self.__index))
vname = name + '-' + version
if vname in indexList:
return mw.returnJson(False, '请不要重复添加!')
if len(indexList) > 12:
return mw.returnJson(False, '首页最多只能显示12个软件!')
indexList.append(vname)
mw.writeFile(self.__index, json.dumps(indexList))
return mw.returnJson(True, '添加成功!')
def removeIndex(self, name, version):
if not os.path.exists(self.__index):
mw.writeFile(self.__index, '[]')
indexList = json.loads(mw.readFile(self.__index))
vname = name + '-' + version
if not vname in indexList:
return mw.returnJson(True, '删除成功!')
indexList.remove(vname)
mw.writeFile(self.__index, json.dumps(indexList))
return mw.returnJson(True, '删除成功!')
# shell 调用
def run(self, name, func, version='', args='', script='index'):
path = self.__plugin_dir + \
'/' + name + '/' + script + '.py'
py = 'python3 ' + path
if args == '':
py_cmd = py + ' ' + func + ' ' + version
else:
py_cmd = py + ' ' + func + ' ' + version + ' ' + args
if not os.path.exists(path):
return ('', '')
data = mw.execShell(py_cmd)
if mw.isDebugMode():
print('run', py_cmd)
print(data)
# print os.path.exists(py_cmd)
return (data[0].strip(), data[1].strip())
# 映射包调用
def callback(self, name, func, args='', script='index'):
package = self.__plugin_dir + '/' + name
if not os.path.exists(package):
return (False, "插件不存在!")
sys.path.append(package)
eval_str = "__import__('" + script + "')." + func + '(' + args + ')'
newRet = eval(eval_str)
if mw.isDebugMode():
print('callback', eval_str)
return (True, newRet)

File diff suppressed because it is too large Load Diff

View File

@@ -1,744 +0,0 @@
# coding: utf-8
# ---------------------------------------------------------------------------------
# MW-Linux面板
# ---------------------------------------------------------------------------------
# copyright (c) 2018-∞(https://github.com/midoks/mdserver-web) All rights reserved.
# ---------------------------------------------------------------------------------
# Author: midoks <midoks@163.com>
# ---------------------------------------------------------------------------------
# ---------------------------------------------------------------------------------
# 系统信息操作
# ---------------------------------------------------------------------------------
import psutil
import time
import os
import re
import math
import sys
import json
from flask import Flask, session
from flask import request
import db
import mw
import config_api
import requests
from threading import Thread
from time import sleep
def mw_async(f):
def wrapper(*args, **kwargs):
thr = Thread(target=f, args=args, kwargs=kwargs)
thr.start()
return wrapper
class system_api:
setupPath = None
pids = None
def __init__(self):
self.setupPath = mw.getServerDir()
##### ----- start ----- ###
def networkApi(self):
data = self.getNetWork()
return mw.getJson(data)
def updateServerApi(self):
stype = request.args.get('type', 'check')
version = request.args.get('version', '')
return self.updateServer(stype, version)
def systemTotalApi(self):
data = self.getSystemTotal()
return mw.getJson(data)
def diskInfoApi(self):
diskInfo = self.getDiskInfo()
return mw.getJson(diskInfo)
def setControlApi(self):
stype = request.form.get('type', '')
day = request.form.get('day', '')
data = self.setControl(stype, day)
return data
def getLoadAverageApi(self):
start = request.args.get('start', '')
end = request.args.get('end', '')
data = self.getLoadAverageData(start, end)
return mw.getJson(data)
def getCpuIoApi(self):
start = request.args.get('start', '')
end = request.args.get('end', '')
data = self.getCpuIoData(start, end)
return mw.getJson(data)
def getDiskIoApi(self):
start = request.args.get('start', '')
end = request.args.get('end', '')
data = self.getDiskIoData(start, end)
return mw.getJson(data)
def getNetworkIoApi(self):
start = request.args.get('start', '')
end = request.args.get('end', '')
data = self.getNetWorkIoData(start, end)
return mw.getJson(data)
def rememoryApi(self):
os.system('sync')
scriptFile = mw.getRunDir() + '/script/rememory.sh'
mw.execShell("/bin/bash " + scriptFile)
data = self.getMemInfo()
return mw.getJson(data)
# 重启面板
def restartApi(self):
self.restartMw()
return mw.returnJson(True, '面板已重启!')
def restartServerApi(self):
if mw.isAppleSystem():
return mw.returnJson(False, "开发环境不可重起")
self.restartServer()
return mw.returnJson(True, '正在重启服务器!')
##### ----- end ----- ###
def restartTask(self):
initd = mw.getRunDir() + '/scripts/init.d/mw'
if os.path.exists(initd):
os.system(initd + ' ' + 'restart_task')
return True
def restartMw(self):
mw.writeFile('data/restart.pl', 'True')
return True
@mw_async
def restartServer(self):
if not mw.isRestart():
return mw.returnJson(False, '请等待所有安装任务完成再执行!')
mw.execShell("sync && init 6 &")
return mw.returnJson(True, '命令发送成功!')
# 名取PID
def getPid(self, pname):
try:
if not self.pids:
self.pids = psutil.pids()
for pid in self.pids:
if psutil.Process(pid).name() == pname:
return True
return False
except:
return False
# 检查端口是否占用
def isOpen(self, port):
import socket
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
try:
s.connect(('127.0.0.1', int(port)))
s.shutdown(2)
return True
except:
return False
# 检测指定进程是否存活
def checkProcess(self, pid):
try:
if not self.pids:
self.pids = psutil.pids()
if int(pid) in self.pids:
return True
return False
except:
return False
def getPanelInfo(self, get=None):
# 取面板配置
address = mw.GetLocalIp()
try:
try:
port = web.ctx.host.split(':')[1]
except:
port = mw.readFile('data/port.pl')
except:
port = '7200'
domain = ''
if os.path.exists('data/domain.conf'):
domain = mw.readFile('data/domain.conf')
autoUpdate = ''
if os.path.exists('data/autoUpdate.pl'):
autoUpdate = 'checked'
limitip = ''
if os.path.exists('data/limitip.conf'):
limitip = mw.readFile('data/limitip.conf')
templates = []
for template in os.listdir('templates/'):
if os.path.isdir('templates/' + template):
templates.append(template)
template = mw.readFile('data/templates.pl')
check502 = ''
if os.path.exists('data/502Task.pl'):
check502 = 'checked'
return {'port': port, 'address': address, 'domain': domain, 'auto': autoUpdate, '502': check502, 'limitip': limitip, 'templates': templates, 'template': template}
def getSystemTotal(self, interval=1):
# 取系统统计信息
data = self.getMemInfo()
cpu = self.getCpuInfo(interval)
data['cpuNum'] = cpu[1]
data['cpuRealUsed'] = cpu[0]
data['time'] = self.getBootTime()
data['system'] = self.getSystemVersion()
data['isuser'] = mw.M('users').where(
'username=?', ('admin',)).count()
data['version'] = '0.0.1'
return data
def getLoadAverage(self):
c = os.getloadavg()
data = {}
data['one'] = round(float(c[0]), 2)
data['five'] = round(float(c[1]), 2)
data['fifteen'] = round(float(c[2]), 2)
data['max'] = psutil.cpu_count() * 2
data['limit'] = data['max']
data['safe'] = data['max'] * 0.75
return data
def getAllInfo(self, get):
data = {}
data['load_average'] = self.GetLoadAverage(get)
data['title'] = self.GetTitle()
data['network'] = self.GetNetWorkApi(get)
data['panel_status'] = not os.path.exists(
'/www/server/mdserver-web/data/close.pl')
import firewalls
ssh_info = firewalls.firewalls().GetSshInfo(None)
data['enable_ssh_status'] = ssh_info['status']
data['disable_ping_status'] = not ssh_info['ping']
data['time'] = self.GetBootTime()
# data['system'] = self.GetSystemVersion();
# data['mem'] = self.GetMemInfo();
data['version'] = web.ctx.session.version
return data
def getTitle(self):
titlePl = 'data/title.pl'
title = 'Linux面板'
if os.path.exists(titlePl):
title = mw.readFile(titlePl).strip()
return title
def getSystemVersion(self):
# 取操作系统版本
if mw.getOs() == 'darwin':
data = mw.execShell('sw_vers')[0]
data_list = data.strip().split("\n")
mac_version = ''
for x in data_list:
mac_version += x.split("\t")[1] + ' '
return mac_version
redhat_series = '/etc/redhat-release'
if os.path.exists(redhat_series):
version = mw.readFile('/etc/redhat-release')
version = version.replace('release ', '').strip()
return version
os_series = '/etc/os-release'
if os.path.exists(os_series):
version = mw.execShell(
"cat /etc/*-release | grep PRETTY_NAME | awk -F = '{print $2}' | awk -F '\"' '{print $2}'")
return version[0].strip()
return '未识别系统信息'
def getBootTime(self):
# 取系统启动时间
uptime = mw.readFile('/proc/uptime')
if uptime == False:
start_time = psutil.boot_time()
run_time = time.time() - start_time
else:
run_time = uptime.split()[0]
tStr = float(run_time)
min = tStr / 60
hours = min / 60
days = math.floor(hours / 24)
hours = math.floor(hours - (days * 24))
min = math.floor(min - (days * 60 * 24) - (hours * 60))
return mw.getInfo('已不间断运行: {1}天{2}小时{3}分钟', (str(int(days)), str(int(hours)), str(int(min))))
def getCpuInfo(self, interval=1):
# 取CPU信息
cpuCount = psutil.cpu_count()
cpuLogicalNum = psutil.cpu_count(logical=False)
used = psutil.cpu_percent(interval=interval)
if os.path.exists('/proc/cpuinfo'):
c_tmp = mw.readFile('/proc/cpuinfo')
d_tmp = re.findall("physical id.+", c_tmp)
cpuLogicalNum = len(set(d_tmp))
used_all = psutil.cpu_percent(percpu=True)
cpu_name = mw.getCpuType() + " * {}".format(cpuLogicalNum)
return used, cpuCount, used_all, cpu_name, cpuCount, cpuLogicalNum
def getMemInfo(self):
# 取内存信息
mem = psutil.virtual_memory()
if mw.getOs() == 'darwin':
memInfo = {
'memTotal': mem.total / 1024 / 1024
}
memInfo['memRealUsed'] = memInfo['memTotal'] * (mem.percent / 100)
else:
memInfo = {
'memTotal': mem.total / 1024 / 1024,
'memFree': mem.free / 1024 / 1024,
'memBuffers': mem.buffers / 1024 / 1024,
'memCached': mem.cached / 1024 / 1024
}
memInfo['memRealUsed'] = memInfo['memTotal'] - \
memInfo['memFree'] - memInfo['memBuffers'] - \
memInfo['memCached']
return memInfo
def getMemUsed(self):
# 取内存使用率
try:
import psutil
mem = psutil.virtual_memory()
if mw.getOs() == 'darwin':
return mem.percent
memInfo = {'memTotal': mem.total / 1024 / 1024, 'memFree': mem.free / 1024 / 1024,
'memBuffers': mem.buffers / 1024 / 1024, 'memCached': mem.cached / 1024 / 1024}
tmp = memInfo['memTotal'] - memInfo['memFree'] - \
memInfo['memBuffers'] - memInfo['memCached']
tmp1 = memInfo['memTotal'] / 100
return (tmp / tmp1)
except Exception as ex:
return 1
def getDiskInfo(self):
info = self.getDiskInfo2()
if len(info) != 0:
return info
# 取磁盘分区信息
diskIo = psutil.disk_partitions()
diskInfo = []
for disk in diskIo:
if disk[1] == '/mnt/cdrom':
continue
if disk[1] == '/boot':
continue
tmp = {}
tmp['path'] = disk[1]
size_tmp = psutil.disk_usage(disk[1])
tmp['size'] = [mw.toSize(size_tmp[0]), mw.toSize(
size_tmp[1]), mw.toSize(size_tmp[2]), str(size_tmp[3]) + '%']
diskInfo.append(tmp)
return diskInfo
def getDiskInfo2(self):
# 取磁盘分区信息
temp = mw.execShell(
"df -h -P|grep '/'|grep -v tmpfs | grep -v devfs")[0]
tempInodes = mw.execShell(
"df -i -P|grep '/'|grep -v tmpfs | grep -v devfs")[0]
temp1 = temp.split('\n')
tempInodes1 = tempInodes.split('\n')
diskInfo = []
n = 0
cuts = ['/mnt/cdrom', '/boot', '/boot/efi', '/dev',
'/dev/shm', '/zroot', '/run/lock', '/run', '/run/shm', '/run/user']
for tmp in temp1:
n += 1
inodes = tempInodes1[n - 1].split()
disk = tmp.split()
if len(disk) < 5:
continue
if disk[1].find('M') != -1:
continue
if disk[1].find('K') != -1:
continue
if len(disk[5].split('/')) > 4:
continue
if disk[5] in cuts:
continue
arr = {}
arr['path'] = disk[5]
tmp1 = [disk[1], disk[2], disk[3], disk[4]]
arr['size'] = tmp1
arr['inodes'] = [inodes[1], inodes[2], inodes[3], inodes[4]]
diskInfo.append(arr)
return diskInfo
# 清理系统垃圾
def clearSystem(self, get):
count = total = 0
tmp_total, tmp_count = self.ClearMail()
count += tmp_count
total += tmp_total
tmp_total, tmp_count = self.ClearOther()
count += tmp_count
total += tmp_total
return count, total
# 清理邮件日志
def clearMail(self):
rpath = '/var/spool'
total = count = 0
import shutil
con = ['cron', 'anacron', 'mail']
for d in os.listdir(rpath):
if d in con:
continue
dpath = rpath + '/' + d
time.sleep(0.2)
num = size = 0
for n in os.listdir(dpath):
filename = dpath + '/' + n
fsize = os.path.getsize(filename)
size += fsize
if os.path.isdir(filename):
shutil.rmtree(filename)
else:
os.remove(filename)
print('mail clear ok')
num += 1
total += size
count += num
return total, count
# 清理其它
def clearOther(self):
clearPath = [
{'path': '/www/server/mdserver-web', 'find': 'testDisk_'},
{'path': '/www/wwwlogs', 'find': 'log'},
{'path': '/tmp', 'find': 'panelBoot.pl'},
{'path': '/www/server/mdserver-web/install', 'find': '.rpm'}
]
total = count = 0
for c in clearPath:
for d in os.listdir(c['path']):
if d.find(c['find']) == -1:
continue
filename = c['path'] + '/' + d
fsize = os.path.getsize(filename)
total += fsize
if os.path.isdir(filename):
shutil.rmtree(filename)
else:
os.remove(filename)
count += 1
mw.restartWeb()
os.system('echo > /tmp/panelBoot.pl')
return total, count
def psutilNetIoCounters(self):
'''
统计网卡流量
'''
stat_pl = 'data/only_netio_counters.pl'
if os.path.exists(stat_pl):
local_lo = (0, 0, 0, 0)
ioName = psutil.net_io_counters(pernic=True).keys()
for x in ioName:
if x.find("lo") > -1:
local_lo = psutil.net_io_counters(
pernic=True).get(x)[:4]
all_io = psutil.net_io_counters()[:4]
result_io = tuple([all_io[i] - local_lo[i]
for i in range(0, len(all_io))])
# print(local_lo)
# print(all_io)
# print(result_io)
return result_io
return psutil.net_io_counters()[:4]
def getNetWork(self):
# 取网络流量信息
try:
# 取网络流量信息
networkIo = self.psutilNetIoCounters()
if not "otime" in session:
session['up'] = networkIo[0]
session['down'] = networkIo[1]
session['otime'] = time.time()
ntime = time.time()
networkInfo = {}
networkInfo['upTotal'] = networkIo[0]
networkInfo['downTotal'] = networkIo[1]
networkInfo['up'] = round(float(
networkIo[0] - session['up']) / 1024 / (ntime - session['otime']), 2)
networkInfo['down'] = round(
float(networkIo[1] - session['down']) / 1024 / (ntime - session['otime']), 2)
networkInfo['downPackets'] = networkIo[3]
networkInfo['upPackets'] = networkIo[2]
# print networkIo[1], session['down'], ntime, session['otime']
session['up'] = networkIo[0]
session['down'] = networkIo[1]
session['otime'] = time.time()
networkInfo['cpu'] = self.getCpuInfo()
networkInfo['load'] = self.getLoadAverage()
networkInfo['mem'] = self.getMemInfo()
return networkInfo
except Exception as e:
print("getNetWork error:", e)
return None
def getNetWorkIoData(self, start, end):
# 取指定时间段的网络Io
data = mw.M('network').dbfile('system').where("addtime>=? AND addtime<=?", (start, end)).field(
'id,up,down,total_up,total_down,down_packets,up_packets,addtime').order('id asc').select()
return self.toAddtime(data)
def getDiskIoData(self, start, end):
# 取指定时间段的磁盘Io
data = mw.M('diskio').dbfile('system').where("addtime>=? AND addtime<=?", (start, end)).field(
'id,read_count,write_count,read_bytes,write_bytes,read_time,write_time,addtime').order('id asc').select()
return self.toAddtime(data)
def getCpuIoData(self, start, end):
# 取指定时间段的CpuIo
data = mw.M('cpuio').dbfile('system').where("addtime>=? AND addtime<=?",
(start, end)).field('id,pro,mem,addtime').order('id asc').select()
return self.toAddtime(data, True)
def getLoadAverageData(self, start, end):
data = mw.M('load_average').dbfile('system').where("addtime>=? AND addtime<=?", (
start, end)).field('id,pro,one,five,fifteen,addtime').order('id asc').select()
return self.toAddtime(data)
# 格式化addtime列
def toAddtime(self, data, tomem=False):
import time
if tomem:
import psutil
mPre = (psutil.virtual_memory().total / 1024 / 1024) / 100
length = len(data)
he = 1
if length > 100:
he = 1
if length > 1000:
he = 3
if length > 10000:
he = 15
if he == 1:
for i in range(length):
data[i]['addtime'] = time.strftime(
'%m/%d %H:%M', time.localtime(float(data[i]['addtime'])))
if tomem and data[i]['mem'] > 100:
data[i]['mem'] = data[i]['mem'] / mPre
return data
else:
count = 0
tmp = []
for value in data:
if count < he:
count += 1
continue
value['addtime'] = time.strftime(
'%m/%d %H:%M', time.localtime(float(value['addtime'])))
if tomem and value['mem'] > 100:
value['mem'] = value['mem'] / mPre
tmp.append(value)
count = 0
return tmp
def setControl(self, stype, day):
filename = 'data/control.conf'
stat_pl = 'data/only_netio_counters.pl'
if stype == '0':
mw.execShell("rm -rf " + filename)
elif stype == '1':
_day = int(day)
if _day < 1:
return mw.returnJson(False, "设置失败!")
mw.writeFile(filename, day)
elif stype == '2':
mw.execShell("rm -rf " + stat_pl)
elif stype == '3':
mw.execShell("echo 'True' > " + stat_pl)
elif stype == 'del':
if not mw.isRestart():
return mw.returnJson(False, '请等待所有安装任务完成再执行')
os.remove("data/system.db")
sql = db.Sql().dbfile('system')
csql = mw.readFile('data/sql/system.sql')
csql_list = csql.split(';')
for index in range(len(csql_list)):
sql.execute(csql_list[index], ())
return mw.returnJson(True, "监控服务已关闭")
else:
data = {}
if os.path.exists(filename):
try:
data['day'] = int(mw.readFile(filename))
except:
data['day'] = 30
data['status'] = True
else:
data['day'] = 30
data['status'] = False
if os.path.exists(stat_pl):
data['stat_all_status'] = True
else:
data['stat_all_status'] = False
return mw.getJson(data)
return mw.returnJson(True, "设置成功!")
def versionDiff(self, old, new):
'''
test 测试
new 有新版本
none 没有新版本
'''
new_list = new.split('.')
if len(new_list) > 3:
return 'test'
old_list = old.split('.')
ret = 'none'
isHasNew = True
if int(new_list[0]) == int(old_list[0]) and int(new_list[1]) == int(old_list[1]) and int(new_list[2]) == int(old_list[2]):
isHasNew = False
if isHasNew:
return 'new'
return ret
def getServerInfo(self):
import urllib.request
import ssl
upAddr = 'https://api.github.com/repos/midoks/mdserver-web/releases/latest'
try:
context = ssl._create_unverified_context()
req = urllib.request.urlopen(upAddr, context=context, timeout=3)
result = req.read().decode('utf-8')
version = json.loads(result)
return version
except Exception as e:
print('getServerInfo', e)
return {}
def updateServer(self, stype, version=''):
# 更新服务
try:
if not mw.isRestart():
return mw.returnJson(False, '请等待所有安装任务完成再执行!')
version_new_info = self.getServerInfo()
version_now = config_api.config_api().getVersion()
new_ver = version_new_info['name']
if stype == 'check':
if not 'name' in version_new_info:
return mw.returnJson(False, '服务器数据或网络有问题!')
diff = self.versionDiff(version_now, new_ver)
if diff == 'new':
return mw.returnJson(True, '有新版本!', new_ver)
elif diff == 'test':
return mw.returnJson(True, '有测试版本!', new_ver)
else:
return mw.returnJson(False, '已经是最新,无需更新!')
if stype == 'info':
if not 'name' in version_new_info:
return mw.returnJson(False, '服务器数据有问题!')
diff = self.versionDiff(version_now, new_ver)
data = {}
data['version'] = new_ver
data['content'] = version_new_info[
'body'].replace("\n", "<br/>")
return mw.returnJson(True, '更新信息!', data)
if stype == 'update':
if version == '':
return mw.returnJson(False, '缺少版本信息!')
if new_ver != version:
return mw.returnJson(False, '更新失败,请重试!')
toPath = mw.getRootDir() + '/temp'
if not os.path.exists(toPath):
mw.execShell('mkdir -p ' + toPath)
newUrl = "https://github.com/midoks/mdserver-web/archive/refs/tags/" + version + ".zip"
dist_mw = toPath + '/mw.zip'
if not os.path.exists(dist_mw):
mw.execShell(
'wget --no-check-certificate -O ' + dist_mw + ' ' + newUrl)
dist_to = toPath + "/mdserver-web-" + version
if not os.path.exists(dist_to):
os.system('unzip -o ' + toPath +
'/mw.zip' + ' -d ' + toPath)
cmd_cp = 'cp -rf ' + toPath + '/mdserver-web-' + \
version + '/* ' + mw.getServerDir() + '/mdserver-web'
mw.execShell(cmd_cp)
mw.execShell('rm -rf ' + toPath + '/mdserver-web-' + version)
mw.execShell('rm -rf ' + toPath + '/mw.zip')
self.restartMw()
return mw.returnJson(True, '安装更新成功!')
return mw.returnJson(False, '已经是最新,无需更新!')
except Exception as ex:
# print('updateServer', ex)
return mw.returnJson(False, "连接服务器失败!" + str(ex))
# 修复面板
def repPanel(self, get):
vp = ''
if mw.readFile('/www/server/mdserver-web/class/common.py').find('checkSafe') != -1:
vp = '_pro'
mw.ExecShell("wget -O update.sh " + mw.get_url() +
"/install/update" + vp + ".sh && bash update.sh")
if hasattr(web.ctx.session, 'getCloudPlugin'):
del(web.ctx.session['getCloudPlugin'])
return True

View File

@@ -1,103 +0,0 @@
# coding: utf-8
# ---------------------------------------------------------------------------------
# MW-Linux面板
# ---------------------------------------------------------------------------------
# copyright (c) 2018-∞(https://github.com/midoks/mdserver-web) All rights reserved.
# ---------------------------------------------------------------------------------
# Author: midoks <midoks@163.com>
# ---------------------------------------------------------------------------------
# ---------------------------------------------------------------------------------
# 任务页操作
# ---------------------------------------------------------------------------------
import psutil
import time
import os
import sys
import mw
import re
import json
import pwd
from flask import request
class task_api:
def __init__(self):
pass
def countApi(self):
c = mw.M('tasks').where("status!=?", ('1',)).count()
return str(c)
def listApi(self):
p = request.form.get('p', '1')
limit = request.form.get('limit', '10').strip()
search = request.form.get('search', '').strip()
start = (int(p) - 1) * int(limit)
limit_str = str(start) + ',' + str(limit)
_list = mw.M('tasks').where('', ()).field(
'id,name,type,status,addtime,start,end').limit(limit_str).order('id desc').select()
_ret = {}
_ret['data'] = _list
count = mw.M('tasks').where('', ()).count()
_page = {}
_page['count'] = count
_page['tojs'] = 'remind'
_page['p'] = p
# return data
_ret['count'] = count
_ret['page'] = mw.getPage(_page)
return mw.getJson(_ret)
def getExecLogApi(self):
file = os.getcwd() + "/tmp/panelExec.log"
v = mw.getLastLine(file, 100)
return v
def getTaskSpeedApi(self):
tempFile = mw.getRunDir() + '/tmp/panelExec.log'
freshFile = mw.getRunDir() + '/tmp/panelFresh'
find = mw.M('tasks').where('status=? OR status=?',
('-1', '0')).field('id,type,name,execstr').find()
if not len(find):
return mw.returnJson(False, '当前没有任务队列在执行-2!')
mw.triggerTask()
data = {}
data['name'] = find['name']
data['execstr'] = find['execstr']
if find['type'] == 'download':
readLine = ""
for i in range(3):
try:
readLine = mw.readFile(tempFile)
if len(readLine) > 10:
data['msg'] = json.loads(readLine)
data['isDownload'] = True
break
except Exception as e:
if i == 2:
mw.M('tasks').where("id=?", (find['id'],)).save(
'status', ('0',))
return mw.returnJson(False, '当前没有任务队列在执行-4:' + str(e))
time.sleep(0.5)
else:
data['msg'] = mw.getLastLine(tempFile, 10)
data['isDownload'] = False
data['task'] = mw.M('tasks').where("status!=?", ('1',)).field(
'id,status,name,type').order("id asc").select()
return mw.getJson(data)

View File

@@ -1,143 +0,0 @@
# coding: utf-8
# ---------------------------------------------------------------------------------
# MW-Linux面板
# ---------------------------------------------------------------------------------
# copyright (c) 2018-∞(https://github.com/midoks/mdserver-web) All rights reserved.
# ---------------------------------------------------------------------------------
# Author: midoks <midoks@163.com>
# ---------------------------------------------------------------------------------
# ---------------------------------------------------------------------------------
# 验证码操作
# ---------------------------------------------------------------------------------
import random
import math
from PIL import Image, ImageDraw, ImageFont, ImageFilter
class vieCode:
__fontSize = 20 # 字体大小
__width = 120 # 画布宽度
__heigth = 45 # 画布高度
__length = 4 # 验证码长度
__draw = None # 画布
__img = None # 图片资源
__code = None # 验证码字符
__str = None # 自定义验证码字符集
__inCurve = True # 是否画干扰线
__inNoise = True # 是否画干扰点
__type = 2 # 验证码类型 1、纯字母 2、数字字母混合
__fontPatn = 'class/fonts/2.ttf' # 字体
def GetCodeImage(self, size=80, length=4):
'''获取验证码图片
@param int size 验证码大小
@param int length 验证码长度
'''
# 准备基础数据
self.__length = length
self.__fontSize = size
self.__width = self.__fontSize * self.__length
self.__heigth = int(self.__fontSize * 1.5)
# 生成验证码图片
self.__createCode()
self.__createImage()
self.__createNoise()
self.__printString()
self.__cerateFilter()
return self.__img, self.__code
def __cerateFilter(self):
'''模糊处理'''
self.__img = self.__img.filter(ImageFilter.BLUR)
filter = ImageFilter.ModeFilter(8)
self.__img = self.__img.filter(filter)
def __createCode(self):
'''创建验证码字符'''
# 是否自定义字符集合
if not self.__str:
# 源文本
number = "3456789"
srcLetter = "qwertyuipasdfghjkzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM"
srcUpper = srcLetter.upper()
if self.__type == 1:
self.__str = number
else:
self.__str = srcLetter + srcUpper + number
# 构造验证码
self.__code = random.sample(self.__str, self.__length)
def __createImage(self):
'''创建画布'''
bgColor = (random.randint(200, 255), random.randint(
200, 255), random.randint(200, 255))
self.__img = Image.new('RGB', (self.__width, self.__heigth), bgColor)
self.__draw = ImageDraw.Draw(self.__img)
def __createNoise(self):
'''画干扰点'''
if not self.__inNoise:
return
font = ImageFont.truetype(self.__fontPatn, int(self.__fontSize / 1.5))
for i in range(5):
# 杂点颜色
noiseColor = (random.randint(150, 200), random.randint(
150, 200), random.randint(150, 200))
putStr = random.sample(self.__str, 2)
for j in range(2):
# 绘杂点
size = (random.randint(-10, self.__width),
random.randint(-10, self.__heigth))
self.__draw.text(size, putStr[j], font=font, fill=noiseColor)
pass
def __createCurve(self):
'''画干扰线'''
if not self.__inCurve:
return
x = y = 0
# 计算曲线系数
a = random.uniform(1, self.__heigth / 2)
b = random.uniform(-self.__width / 4, self.__heigth / 4)
f = random.uniform(-self.__heigth / 4, self.__heigth / 4)
t = random.uniform(self.__heigth, self.__width * 2)
xend = random.randint(self.__width / 2, self.__width * 2)
w = (2 * math.pi) / t
# 画曲线
color = (random.randint(30, 150), random.randint(
30, 150), random.randint(30, 150))
for x in range(xend):
if w != 0:
for k in range(int(self.__heigth / 10)):
y = a * math.sin(w * x + f) + b + self.__heigth / 2
i = int(self.__fontSize / 5)
while i > 0:
px = x + i
py = y + i + k
self.__draw.point((px, py), color)
i -= i
def __printString(self):
'''打印验证码字符串'''
font = ImageFont.truetype(self.__fontPatn, self.__fontSize)
x = 0
# 打印字符到画板
for i in range(self.__length):
# 设置字体随机颜色
color = (random.randint(30, 150), random.randint(
30, 150), random.randint(30, 150))
# 计算座标
x = random.uniform(self.__fontSize * i * 0.95,
self.__fontSize * i * 1.1)
y = self.__fontSize * random.uniform(0.3, 0.5)
# 打印字符
self.__draw.text((x, y), self.__code[i], font=font, fill=color)

Binary file not shown.

View File

View File

@@ -1,5 +0,0 @@
- fcgi-client.py
```
https://pypi.org/project/flup6/1.1.1/#files
```

View File

@@ -1,408 +0,0 @@
# Copyright (c) 2006 Allan Saddi <allan@saddi.com>
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
# 1. Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
# SUCH DAMAGE.
#
# $Id$
#
# Copyright (c) 2011 Vladimir Rusinov <vladimir@greenmice.info>
__author__ = 'Allan Saddi <allan@saddi.com>'
__version__ = '$Revision$'
import sys
import select
import struct
import socket
import errno
import types
__all__ = ['FCGIApp']
# Constants from the spec.
FCGI_LISTENSOCK_FILENO = 0
FCGI_HEADER_LEN = 8
FCGI_VERSION_1 = 1
FCGI_BEGIN_REQUEST = 1
FCGI_ABORT_REQUEST = 2
FCGI_END_REQUEST = 3
FCGI_PARAMS = 4
FCGI_STDIN = 5
FCGI_STDOUT = 6
FCGI_STDERR = 7
FCGI_DATA = 8
FCGI_GET_VALUES = 9
FCGI_GET_VALUES_RESULT = 10
FCGI_UNKNOWN_TYPE = 11
FCGI_MAXTYPE = FCGI_UNKNOWN_TYPE
FCGI_NULL_REQUEST_ID = 0
FCGI_KEEP_CONN = 1
FCGI_RESPONDER = 1
FCGI_AUTHORIZER = 2
FCGI_FILTER = 3
FCGI_REQUEST_COMPLETE = 0
FCGI_CANT_MPX_CONN = 1
FCGI_OVERLOADED = 2
FCGI_UNKNOWN_ROLE = 3
FCGI_MAX_CONNS = 'FCGI_MAX_CONNS'
FCGI_MAX_REQS = 'FCGI_MAX_REQS'
FCGI_MPXS_CONNS = 'FCGI_MPXS_CONNS'
FCGI_Header = '!BBHHBx'
FCGI_BeginRequestBody = '!HB5x'
FCGI_EndRequestBody = '!LB3x'
FCGI_UnknownTypeBody = '!B7x'
FCGI_BeginRequestBody_LEN = struct.calcsize(FCGI_BeginRequestBody)
FCGI_EndRequestBody_LEN = struct.calcsize(FCGI_EndRequestBody)
FCGI_UnknownTypeBody_LEN = struct.calcsize(FCGI_UnknownTypeBody)
if __debug__:
import time
# Set non-zero to write debug output to a file.
DEBUG = 0
DEBUGLOG = '/www/server/mdserver-web/logs/fastcgi.log'
def _debug(level, msg):
if DEBUG < level:
return
try:
f = open(DEBUGLOG, 'a')
f.write('%sfcgi: %s\n' % (time.ctime()[4:-4], msg))
f.close()
except:
pass
def decode_pair(s, pos=0):
"""
Decodes a name/value pair.
The number of bytes decoded as well as the name/value pair
are returned.
"""
nameLength = ord(s[pos])
if nameLength & 128:
nameLength = struct.unpack('!L', s[pos:pos + 4])[0] & 0x7fffffff
pos += 4
else:
pos += 1
valueLength = ord(s[pos])
if valueLength & 128:
valueLength = struct.unpack('!L', s[pos:pos + 4])[0] & 0x7fffffff
pos += 4
else:
pos += 1
name = s[pos:pos + nameLength]
pos += nameLength
value = s[pos:pos + valueLength]
pos += valueLength
return (pos, (name, value))
def encode_pair(name, value):
"""
Encodes a name/value pair.
The encoded string is returned.
"""
nameLength = len(name)
if nameLength < 128:
s = chr(nameLength).encode()
else:
s = struct.pack('!L', nameLength | 0x80000000)
valueLength = len(value)
if valueLength < 128:
s += chr(valueLength).encode()
else:
s += struct.pack('!L', valueLength | 0x80000000)
return s + name + value
class Record(object):
"""
A FastCGI Record.
Used for encoding/decoding records.
"""
def __init__(self, type=FCGI_UNKNOWN_TYPE, requestId=FCGI_NULL_REQUEST_ID):
self.version = FCGI_VERSION_1
self.type = type
self.requestId = requestId
self.contentLength = 0
self.paddingLength = 0
self.contentData = ''
def _recvall(sock, length):
"""
Attempts to receive length bytes from a socket, blocking if necessary.
(Socket may be blocking or non-blocking.)
"""
dataList = []
recvLen = 0
while length:
try:
data = sock.recv(length)
except socket.error as e:
if e[0] == errno.EAGAIN:
select.select([sock], [], [])
continue
else:
raise
if not data: # EOF
break
dataList.append(data)
dataLen = len(data)
recvLen += dataLen
length -= dataLen
return b''.join(dataList), recvLen
_recvall = staticmethod(_recvall)
def read(self, sock):
"""Read and decode a Record from a socket."""
try:
header, length = self._recvall(sock, FCGI_HEADER_LEN)
except:
raise EOFError
if length < FCGI_HEADER_LEN:
raise EOFError
self.version, self.type, self.requestId, self.contentLength, \
self.paddingLength = struct.unpack(FCGI_Header, header)
if __debug__:
_debug(9, 'read: fd = %d, type = %d, requestId = %d, '
'contentLength = %d' %
(sock.fileno(), self.type, self.requestId,
self.contentLength))
if self.contentLength:
try:
self.contentData, length = self._recvall(sock,
self.contentLength)
except:
raise EOFError
if length < self.contentLength:
raise EOFError
if self.paddingLength:
try:
self._recvall(sock, self.paddingLength)
except:
raise EOFError
def _sendall(sock, data):
"""
Writes data to a socket and does not return until all the data is sent.
"""
length = len(data)
while length:
try:
sent = sock.send(data)
except socket.error as e:
if e[0] == errno.EAGAIN:
select.select([], [sock], [])
continue
else:
raise
data = data[sent:]
length -= sent
_sendall = staticmethod(_sendall)
def write(self, sock):
"""Encode and write a Record to a socket."""
self.paddingLength = -self.contentLength & 7
if __debug__:
_debug(9, 'write: fd = %d, type = %d, requestId = %d, '
'contentLength = %d' %
(sock.fileno(), self.type, self.requestId,
self.contentLength))
header = struct.pack(FCGI_Header, self.version, self.type,
self.requestId, self.contentLength,
self.paddingLength)
self._sendall(sock, header)
if self.contentLength:
self._sendall(sock, self.contentData)
if self.paddingLength:
self._sendall(sock, b'\x00' * self.paddingLength)
class FCGIApp(object):
def __init__(self, connect=None, host=None, port=None, filterEnviron=True):
if host is not None:
assert port is not None
connect = (host, port)
self._connect = connect
self._filterEnviron = filterEnviron
def __call__(self, environ, io, start_response=None):
# For sanity's sake, we don't care about FCGI_MPXS_CONN
# (connection multiplexing). For every request, we obtain a new
# transport socket, perform the request, then discard the socket.
# This is, I believe, how mod_fastcgi does things...
sock = self._getConnection()
# Since this is going to be the only request on this connection,
# set the request ID to 1.
requestId = 1
# Begin the request
rec = Record(FCGI_BEGIN_REQUEST, requestId)
rec.contentData = struct.pack(FCGI_BeginRequestBody, FCGI_RESPONDER, 0)
rec.contentLength = FCGI_BeginRequestBody_LEN
rec.write(sock)
# Filter WSGI environ and send it as FCGI_PARAMS
if self._filterEnviron:
params = self._defaultFilterEnviron(environ)
else:
params = self._lightFilterEnviron(environ)
# TODO: Anything not from environ that needs to be sent also?
# return '200 OK',[],str(params),''
self._fcgiParams(sock, requestId, params)
self._fcgiParams(sock, requestId, {})
# Transfer wsgi.input to FCGI_STDIN
content_length = int(environ.get('CONTENT_LENGTH') or 0)
s = ''
#io = StringIO(stdin)
while True:
if not io:
break
chunk_size = min(content_length, 4096)
s = io.read(chunk_size)
content_length -= len(s)
rec = Record(FCGI_STDIN, requestId)
rec.contentData = s
rec.contentLength = len(s)
rec.write(sock)
if not s:
break
# Empty FCGI_DATA stream
rec = Record(FCGI_DATA, requestId)
rec.write(sock)
return sock
def _getConnection(self):
if self._connect is not None:
# The simple case. Create a socket and connect to the
# application.
if isinstance(self._connect, str):
sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
sock.connect(self._connect)
elif hasattr(socket, 'create_connection'):
sock = socket.create_connection(self._connect)
else:
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect(self._connect)
return sock
# To be done when I have more time...
# , 'Launching and managing FastCGI programs not yet implemented'
raise NotImplementedError
def _fcgiGetValues(self, sock, vars):
# Construct FCGI_GET_VALUES record
outrec = Record(FCGI_GET_VALUES)
data = []
for name in vars:
data.append(encode_pair(name, ''))
data = ''.join(data)
outrec.contentData = data
outrec.contentLength = len(data)
outrec.write(sock)
# Await response
inrec = Record()
inrec.read(sock)
result = {}
if inrec.type == FCGI_GET_VALUES_RESULT:
pos = 0
while pos < inrec.contentLength:
pos, (name, value) = decode_pair(inrec.contentData, pos)
result[name] = value
return result
def _fcgiParams(self, sock, requestId, params):
rec = Record(FCGI_PARAMS, requestId)
data = []
for name, value in params.items():
data.append(encode_pair(name.encode(
'latin-1'), value.encode('latin-1')))
data = b''.join(data)
rec.contentData = data
rec.contentLength = len(data)
rec.write(sock)
_environPrefixes = ['SERVER_', 'HTTP_', 'REQUEST_', 'REMOTE_', 'PATH_',
'CONTENT_', 'DOCUMENT_', 'SCRIPT_']
_environCopies = ['SCRIPT_NAME', 'QUERY_STRING', 'AUTH_TYPE']
_environRenames = []
def _defaultFilterEnviron(self, environ):
result = {}
for n in environ.keys():
iv = False
for p in self._environPrefixes:
if n.startswith(p):
result[n] = environ[n]
iv = True
if n in self._environCopies:
result[n] = environ[n]
iv = True
if n in self._environRenames:
result[self._environRenames[n]] = environ[n]
iv = True
if not iv:
result[n] = environ[n]
return result
def _lightFilterEnviron(self, environ):
result = {}
for n in environ.keys():
if n.upper() == n:
result[n] = environ[n]
return result

View File

@@ -1,208 +0,0 @@
# coding:utf-8
import json
import os
import time
import re
import sys
import time
import struct
import fcgi_client
FCGI_Header = '!BBHHBx'
if sys.version_info[0] == 2:
try:
from cStringIO import StringIO
except:
from StringIO import StringIO
else:
from io import BytesIO as StringIO
def get_header_data(sock):
'''
@name 获取头部32KB数据
@param sock socketobject(fastcgi套接字对象)
@return bytes
'''
headers_data = b''
total_len = 0
header_len = 1024 * 128
while True:
fastcgi_header = sock.recv(8)
if not fastcgi_header:
break
if len(fastcgi_header) != 8:
headers_data += fastcgi_header
break
fast_pack = struct.unpack(FCGI_Header, fastcgi_header)
if fast_pack[1] == 3:
break
tlen = fast_pack[3]
while tlen > 0:
sd = sock.recv(tlen)
if not sd:
break
headers_data += sd
tlen -= len(sd)
total_len += fast_pack[3]
if fast_pack[4]:
sock.recv(fast_pack[4])
if total_len > header_len:
break
return headers_data
def format_header_data(headers_data):
'''
@name 格式化响应头
@param headers_data bytes(fastcgi头部32KB数据)
@return status int(响应状态), headers dict(响应头), bdata bytes(格式化响应头后的多余数据)
'''
status = '200 OK'
headers = {}
pos = 0
while True:
eolpos = headers_data.find(b'\n', pos)
if eolpos < 0:
break
line = headers_data[pos:eolpos - 1]
pos = eolpos + 1
line = line.strip()
if len(line) < 2:
break
if line.find(b':') == -1:
continue
header, value = line.split(b':', 1)
header = header.strip()
value = value.strip()
if isinstance(header, bytes):
header = header.decode()
value = value.decode()
if header == 'Status':
status = value
if status.find(' ') < 0:
status += ' BTPanel'
else:
headers[header] = value
bdata = headers_data[pos:]
status = int(status.split(' ')[0])
return status, headers, bdata
def resp_sock(sock, bdata):
'''
@name 以流的方式发送剩余数据
@param sock socketobject(fastcgi套接字对象)
@param bdata bytes(格式化响应头后的多余数据)
@return yield bytes
'''
# 发送除响应头以外的多余头部数据
yield bdata
while True:
fastcgi_header = sock.recv(8)
if not fastcgi_header:
break
if len(fastcgi_header) != 8:
yield fastcgi_header
break
fast_pack = struct.unpack(FCGI_Header, fastcgi_header)
if fast_pack[1] == 3:
break
tlen = fast_pack[3]
while tlen > 0:
sd = sock.recv(tlen)
if not sd:
break
tlen -= len(sd)
if sd:
yield sd
if fast_pack[4]:
sock.recv(fast_pack[4])
sock.close()
class fpm(object):
def __init__(self, sock=None, document_root='', last_path=''):
'''
@name 实例化FPM对象
@param sock string(unixsocket路径)
@param document_root string(PHP文档根目录)
@return FPM
'''
if sock:
self.fcgi_sock = sock
if document_root[-1:] != '/':
document_root += '/'
self.document_root = document_root
self.last_path = last_path
def load_url_public(self, url, content=b'', method='GET', content_type='application/x-www-form-urlencoded'):
'''
@name 转发URL到PHP-FPM 公共
@param url string(URI地址)
@param content stream(POST数据io对象)
@return fastcgi-socket
'''
fcgi = fcgi_client.FCGIApp(connect=self.fcgi_sock)
try:
script_name, query_string = url.split('?')
except ValueError:
script_name = url
query_string = ''
content_length = len(content)
if content:
content = StringIO(content)
env = {
'SCRIPT_FILENAME': '%s%s' % (self.document_root, script_name),
'QUERY_STRING': query_string,
'REQUEST_METHOD': method,
'SCRIPT_NAME': self.last_path + script_name,
'REQUEST_URI': url,
'GATEWAY_INTERFACE': 'CGI/1.1',
'SERVER_SOFTWARE': 'MW-Panel',
'REDIRECT_STATUS': '200',
'CONTENT_TYPE': content_type,
'CONTENT_LENGTH': str(content_length),
'DOCUMENT_URI': script_name,
'DOCUMENT_ROOT': self.document_root,
'SERVER_PROTOCOL': 'HTTP/1.1',
'REMOTE_ADDR': '127.0.0.1',
'REMOTE_PORT': '7200',
'SERVER_ADDR': '127.0.0.1',
'SERVER_PORT': '80',
'SERVER_NAME': 'MW-Panel'
}
fpm_sock = fcgi(env, content)
_data = b''
while True:
fastcgi_header = fpm_sock.recv(8)
if not fastcgi_header:
break
if len(fastcgi_header) != 8:
_data += fastcgi_header
break
fast_pack = struct.unpack(FCGI_Header, fastcgi_header)
if fast_pack[1] == 3:
break
tlen = fast_pack[3]
while tlen > 0:
sd = fpm_sock.recv(tlen)
if not sd:
break
tlen -= len(sd)
_data += sd
if fast_pack[4]:
fpm_sock.recv(fast_pack[4])
status, headers, data = format_header_data(_data)
return data

View File

@@ -1,113 +0,0 @@
# coding: utf-8
import re
import os
import sys
import pymysql.cursors
class ORM:
__DB_PASS = None
__DB_USER = 'root'
__DB_PORT = 3306
__DB_NAME = ''
__DB_HOST = 'localhost'
__DB_CONN = None
__DB_CUR = None
__DB_ERR = None
__DB_CNF = '/etc/my.cnf'
__DB_SOCKET = '/www/server/mysql/mysql.sock'
__DB_CHARSET = "utf8"
def __Conn(self):
'''连接数据库'''
try:
if os.path.exists(self.__DB_SOCKET):
try:
self.__DB_CONN = pymysql.connect(host=self.__DB_HOST, user=self.__DB_USER, passwd=self.__DB_PASS,
database=self.__DB_NAME,
port=int(self.__DB_PORT), charset=self.__DB_CHARSET, connect_timeout=1,
unix_socket=self.__DB_SOCKET, cursorclass=pymysql.cursors.DictCursor)
except Exception as e:
self.__DB_HOST = '127.0.0.1'
self.__DB_CONN = pymysql.connect(host=self.__DB_HOST, user=self.__DB_USER, passwd=self.__DB_PASS,
database=self.__DB_NAME,
port=int(self.__DB_PORT), charset=self.__DB_CHARSET, connect_timeout=1,
unix_socket=self.__DB_SOCKET, cursorclass=pymysql.cursors.DictCursor)
else:
try:
self.__DB_CONN = pymysql.connect(host=self.__DB_HOST, user=self.__DB_USER, passwd=self.__DB_PASS,
database=self.__DB_NAME,
port=int(self.__DB_PORT), charset=self.__DB_CHARSET, connect_timeout=1,
cursorclass=pymysql.cursors.DictCursor)
except Exception as e:
self.__DB_HOST = '127.0.0.1'
self.__DB_CONN = pymysql.connect(host=self.__DB_HOST, user=self.__DB_USER, passwd=self.__DB_PASS,
database=self.__DB_NAME,
port=int(self.__DB_PORT), charset=self.__DB_CHARSET, connect_timeout=1,
cursorclass=pymysql.cursors.DictCursor)
self.__DB_CUR = self.__DB_CONN.cursor()
return True
except Exception as e:
self.__DB_ERR = e
return False
def setDbConf(self, conf):
self.__DB_CNF = conf
def setSocket(self, sock):
self.__DB_SOCKET = sock
def setCharset(self, charset):
self.__DB_CHARSET = charset
def setPort(self, port):
self.__DB_PORT = port
def setUser(self, user):
self.__DB_USER = user
def setPwd(self, pwd):
self.__DB_PASS = pwd
def getPwd(self):
return self.__DB_PASS
def setDbName(self, name):
self.__DB_NAME = name
def execute(self, sql):
# 执行SQL语句返回受影响行
if not self.__Conn():
return self.__DB_ERR
try:
result = self.__DB_CUR.execute(sql)
self.__DB_CONN.commit()
self.__Close()
return result
except Exception as ex:
return ex
def query(self, sql):
# 执行SQL语句返回数据集
if not self.__Conn():
return self.__DB_ERR
try:
self.__DB_CUR.execute(sql)
result = self.__DB_CUR.fetchall()
# print(result)
# 将元组转换成列表
# data = map(list, result)
self.__Close()
return result
except Exception as ex:
return ex
def __Close(self):
# 关闭连接
self.__DB_CUR.close()
self.__DB_CONN.close()

View File

@@ -1,115 +0,0 @@
# coding: utf-8
import re
import os
import sys
class ORM:
__DB_PASS = None
__DB_USER = 'root'
__DB_PORT = 3306
__DB_HOST = 'localhost'
__DB_CONN = None
__DB_CUR = None
__DB_ERR = None
__DB_CNF = '/etc/my.cnf'
__DB_SOCKET = '/www/server/mysql/mysql.sock'
__DB_CHARSET = 'utf8'
def __Conn(self):
'''连接MYSQL数据库'''
try:
try:
import MySQLdb
except Exception as ex:
self.__DB_ERR = ex
return False
# print(self.__DB_HOST)
# print(self.__DB_USER)
# print(self.__DB_PASS)
# print(self.__DB_PORT)
# print(self.__DB_CHARSET)
# print(self.__DB_SOCKET)
if os.path.exists(self.__DB_SOCKET):
try:
self.__DB_CONN = MySQLdb.connect(host=self.__DB_HOST, user=self.__DB_USER, passwd=self.__DB_PASS,
port=int(self.__DB_PORT), charset=self.__DB_CHARSET, connect_timeout=1, unix_socket=self.__DB_SOCKET)
except Exception as e:
print(e)
self.__DB_HOST = '127.0.0.1'
self.__DB_CONN = MySQLdb.connect(host=self.__DB_HOST, user=self.__DB_USER, passwd=self.__DB_PASS,
port=int(self.__DB_PORT), charset=self.__DB_CHARSET, connect_timeout=1, unix_socket=self.__DB_SOCKET)
else:
try:
self.__DB_CONN = MySQLdb.connect(host=self.__DB_HOST, user=self.__DB_USER, passwd=self.__DB_PASS,
port=int(self.__DB_PORT), charset=self.__DB_CHARSET, connect_timeout=1)
except Exception as e:
self.__DB_HOST = '127.0.0.1'
self.__DB_CONN = MySQLdb.connect(host=self.__DB_HOST, user=self.__DB_USER, passwd=self.__DB_PASS,
port=int(self.__DB_PORT), charset=self.__DB_CHARSET, connect_timeout=1)
self.__DB_CUR = self.__DB_CONN.cursor()
return True
except MySQLdb.Error as e:
self.__DB_ERR = e
return False
def setDbConf(self, conf):
self.__DB_CNF = conf
def setSocket(self, sock):
self.__DB_SOCKET = sock
def setCharset(self, charset):
self.__DB_CHARSET = charset
def setPort(self, port):
self.__DB_PORT = port
def setPwd(self, pwd):
self.__DB_PASS = pwd
def getPwd(self):
return self.__DB_PASS
def setDbName(self, name):
self.__DB_NAME = name
def setUser(self, user):
self.__DB_USER = user
def execute(self, sql):
# 执行SQL语句返回受影响行
if not self.__Conn():
return self.__DB_ERR
try:
result = self.__DB_CUR.execute(sql)
self.__DB_CONN.commit()
self.__Close()
return result
except Exception as ex:
return ex
def query(self, sql):
# 执行SQL语句返回数据集
if not self.__Conn():
return self.__DB_ERR
try:
self.__DB_CUR.execute(sql)
result = self.__DB_CUR.fetchall()
# 将元组转换成列表
data = map(list, result)
self.__Close()
return data
except Exception as ex:
return ex
# 关闭连接
def __Close(self):
self.__DB_CUR.close()
self.__DB_CONN.close()

View File

@@ -1,7 +0,0 @@
#!/bin/bash
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin:/usr/local/lib/python2.7/bin
find . -name .DS_Store | xargs rm -rf
find . -type d -name "*.pyc" | xargs rm -rf

86
cli.sh
View File

@@ -1,86 +0,0 @@
#!/bin/bash
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
DIR=$(cd "$(dirname "$0")"; pwd)
MDIR=$(dirname "$DIR")
PATH=$PATH:$DIR/bin
if [ -f bin/activate ];then
source bin/activate
fi
export LC_ALL="en_US.UTF-8"
mw_start_task()
{
isStart=$(ps aux |grep 'task.py'|grep -v grep|awk '{print $2}')
if [ "$isStart" == '' ];then
echo -e "Starting mw-tasks... \c"
cd $DIR && python3 task.py >> ${DIR}/logs/task.log 2>&1 &
sleep 0.3
isStart=$(ps aux |grep 'task.py'|grep -v grep|awk '{print $2}')
if [ "$isStart" == '' ];then
echo -e "\033[31mfailed\033[0m"
echo '------------------------------------------------------'
tail -n 20 $DIR/logs/task.log
echo '------------------------------------------------------'
echo -e "\033[31mError: mw-tasks service startup failed.\033[0m"
return;
fi
echo -e "\033[32mdone\033[0m"
else
echo "Starting mw-tasks... mw-tasks (pid $(echo $isStart)) already running"
fi
}
mw_start(){
gunicorn -c setting.py app:app
#安全启动
mw_start_task
}
mw_start_debug(){
python3 task.py >> $DIR/logs/task.log 2>&1 &
gunicorn -b :7200 -k gevent -w 1 app:app
}
mw_start_debug2(){
python3 task.py >> $DIR/logs/task.log 2>&1 &
gunicorn -b :7200 -k geventwebsocket.gunicorn.workers.GeventWebSocketWorker -w 1 app:app
}
mw_stop()
{
PLIST=`ps -ef|grep app:app |grep -v grep|awk '{print $2}'`
for i in $PLIST
do
kill -9 $i
done
pids=`ps -ef|grep task.py | grep -v grep |awk '{print $2}'`
arr=($pids)
for p in ${arr[@]}
do
kill -9 $p
done
}
case "$1" in
'start') mw_start;;
'stop') mw_stop;;
'restart')
mw_stop
mw_start
;;
'debug')
mw_stop
mw_start_debug
;;
'debug2')
mw_stop
mw_start_debug2
;;
esac

68
cmd.md
View File

@@ -1,68 +0,0 @@
### 常用命令说明
- 面板相关命令
```
/etc/init.d/mw default
service mw [start|stop|reload|restart|status]
```
- OpenResty
```
systemctl [start|stop|reload|restart|status] openresty
```
- MySQL
```
systemctl [start|stop|reload|restart|status] mysql
```
- MariaDB
```
systemctl [start|stop|reload|restart|status] mariadb
```
- PHP
```
systemctl [start|stop|reload|restart|status] php[54-81]
systemctl start php71
```
- Redis
```
systemctl [start|stop|reload|restart|status] redis
```
- Memcached
```
systemctl [start|stop|reload|restart|status] memcached
```
- sphinx
```
systemctl [start|stop|reload|restart|status] sphinx
```

View File

@@ -1,68 +0,0 @@
### 兼容性测试报告
快速测试命令
```
cd /www/server/mdserver-web/scripts/quick && bash debug.sh
cd /www/server/mdserver-web/plugins/php/versions && bash all_test.sh
```
| 系统名称 | 面板 | OpenResty |
| ----------------- |---------------|---------------|
| CentOS 7.9 |✅ |✅ |
| CentOS 8.4 |✅ |✅ |
| CentOS 8 Stream |✅ |✅ |
| CentOS 9 Stream |✅ |✅ |
| Debian 10.3 |✅ |✅ |
| Debian 11.3 |✅ |✅ |
| Ubuntu 18.04 |✅ |✅ |
| Ubuntu 20.04 |✅ |✅ |
| Ubuntu 22.04 |✅ |✅ |
| Fedora 31 |✅ |✅ |
| Fedora 32 |✅ |✅ |
| AlmaLinux 9 |✅ |✅ |
| RockyLinux 8.6 |✅ |✅ |
| Arch Linux  |✅ |✅ |
| openSUSE 15.4 |✅ |✅ |
| 系统名称 |PHP53 |PHP54 |PHP55 |PHP56 |PHP70 |PHP71 |PHP72 |PHP73 |PHP74 |PHP80 |PHP81 |
| ----------------- |-------|-------|-------|-------|-------|-------|-------|-------|-------|-------|-------|
| CentOS 7.9 |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |
| CentOS 8.4 |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |
| CentOS 8 Stream |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |
| CentOS 9 Stream |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |
| Debian 10.3 |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |
| Debian 11.3 |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |
| Ubuntu 18.04 |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |
| Ubuntu 20.04 |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |
| Ubuntu 22.04 |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |
| Fedora 31 |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |
| Fedora 32 |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |
| AlmaLinux 9 |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |
| RockyLinux 8.6 |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |
| Arch Linux |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |
| openSUSE 15.4 |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |
| 系统名称 | MySQL55|MySQL56|MySQL57|MySQL80|
| ----------------- |-------|--------|-------|-------|
| CentOS 7.9 |✅ |✅ |✅ |✅ |
| CentOS 8.4 |✅ |✅ |✅ |✅ |
| CentOS 8 Stream |✅ |✅ |✅ |✅ |
| CentOS 9 Stream |✅ |✅ |✅ |✅ |
| Debian 10.3 |✅ |✅ |✅ |✅ |
| Debian 11.3 |✅ |✅ |✅ |✅ |
| Ubuntu 18.04 |✅ |✅ |✅ |✅ |
| Ubuntu 20.04 |✅ |✅ |✅ |✅ |
| Ubuntu 22.04 |✅ |✅ |✅ |✅ |
| Fedora 31 |✅ |✅ |✅ |✅ |
| Fedora 32 |✅ |✅ |✅ |✅ |
| AlmaLinux 9 |✅ |✅ |✅ |✅ |
| RockyLinux 8.6 |✅ |✅ |✅ |✅ |
| Arch Linux |✅ |✅ |✅ |✅ |
| openSUSE 15.4 |✅ |✅ |✅ |✅ |

View File

@@ -1,37 +0,0 @@
[
{
"title":"全部",
"type":0,
"ps":""
},
{
"title":"运行环境",
"type":1,
"ps":""
},
{
"title":"数据软件",
"type":2,
"ps":""
},
{
"title":"代码管理",
"type":3,
"ps":""
},
{
"title":"系统工具",
"type":4,
"ps":""
},
{
"title":"其他插件",
"type":5,
"ps":""
},
{
"title":"辅助插件",
"type":6,
"ps":""
}
]

View File

@@ -1 +0,0 @@
True

View File

@@ -1,134 +0,0 @@
CREATE TABLE IF NOT EXISTS `backup` (
`id` INTEGER PRIMARY KEY AUTOINCREMENT,
`type` INTEGER,
`name` TEXT,
`pid` INTEGER,
`filename` TEXT,
`size` INTEGER,
`addtime` TEXT
);
CREATE TABLE IF NOT EXISTS `binding` (
`id` INTEGER PRIMARY KEY AUTOINCREMENT,
`pid` INTEGER,
`domain` TEXT,
`path` TEXT,
`port` INTEGER,
`addtime` TEXT
);
CREATE TABLE IF NOT EXISTS `crontab` (
`id` INTEGER PRIMARY KEY AUTOINCREMENT,
`name` TEXT,
`type` TEXT,
`where1` TEXT,
`where_hour` INTEGER,
`where_minute` INTEGER,
`echo` TEXT,
`addtime` TEXT,
`status` INTEGER DEFAULT '1',
`save` INTEGER DEFAULT '3',
`backup_to` TEXT DEFAULT 'off',
`sname` TEXT,
`sbody` TEXT,
'stype' TEXT,
`urladdress` TEXT
);
CREATE TABLE IF NOT EXISTS `firewall` (
`id` INTEGER PRIMARY KEY AUTOINCREMENT,
`port` TEXT,
`ps` TEXT,
`addtime` TEXT
);
INSERT INTO `firewall` (`id`, `port`, `ps`, `addtime`) VALUES
(1, '80', '网站默认端口', '0000-00-00 00:00:00'),
(2, '22', 'SSH远程管理服务', '0000-00-00 00:00:00'),
(3, '443', 'HTTPS', '0000-00-00 00:00:00'),
(4, '888', 'phpMyAdmin默认端口', '0000-00-00 00:00:00');
CREATE TABLE IF NOT EXISTS `logs` (
`id` INTEGER PRIMARY KEY AUTOINCREMENT,
`type` TEXT,
`log` TEXT,
`uid` TEXT,
`addtime` TEXT
);
ALTER TABLE `logs` ADD COLUMN `uid` INTEGER DEFAULT '1';
CREATE TABLE IF NOT EXISTS `sites` (
`id` INTEGER PRIMARY KEY AUTOINCREMENT,
`name` TEXT,
`path` TEXT,
`status` TEXT,
`index` TEXT,
`type_id` INTEGER,
`ps` TEXT,
`edate` TEXT,
`addtime` TEXT
);
CREATE TABLE IF NOT EXISTS `site_types` (
`id` INTEGER PRIMARY KEY AUTOINCREMENT,
`name` TEXT
);
CREATE TABLE IF NOT EXISTS `domain` (
`id` INTEGER PRIMARY KEY AUTOINCREMENT,
`pid` INTEGER,
`name` TEXT,
`port` INTEGER,
`addtime` TEXT
);
CREATE TABLE IF NOT EXISTS `users` (
`id` INTEGER PRIMARY KEY AUTOINCREMENT,
`username` TEXT,
`password` TEXT,
`login_ip` TEXT,
`login_time` TEXT,
`phone` TEXT,
`email` TEXT
);
INSERT INTO `users` (`id`, `username`, `password`, `login_ip`, `login_time`, `phone`, `email`) VALUES
(1, 'admin', '21232f297a57a5a743894a0e4a801fc3', '192.168.0.10', '2022-02-02 00:00:00', 0, 'midoks@163.com');
CREATE TABLE IF NOT EXISTS `tasks` (
`id` INTEGER PRIMARY KEY AUTOINCREMENT,
`name` TEXT,
`type` TEXT,
`status` TEXT,
`addtime` TEXT,
`start` INTEGER,
`end` INTEGER,
`execstr` TEXT
);
CREATE TABLE IF NOT EXISTS `temp_login` (
`id` INTEGER PRIMARY KEY AUTOINCREMENT,
`token` REAL,
`salt` REAL,
`state` INTEGER,
`login_time` INTEGER,
`login_addr` REAL,
`logout_time` INTEGER,
`expire` INTEGER,
`addtime` INTEGER
);
CREATE TABLE IF NOT EXISTS `panel` (
`id` INTEGER PRIMARY KEY AUTOINCREMENT,
`title` TEXT,
`url` TEXT,
`username` TEXT,
`password` TEXT,
`click` INTEGER,
`addtime` INTEGER
);

View File

@@ -1,37 +0,0 @@
CREATE TABLE IF NOT EXISTS `network` (
`id` INTEGER PRIMARY KEY AUTOINCREMENT,
`up` INTEGER,
`down` INTEGER,
`total_up` INTEGER,
`total_down` INTEGER,
`down_packets` INTEGER,
`up_packets` INTEGER,
`addtime` INTEGER
);
CREATE TABLE IF NOT EXISTS `cpuio` (
`id` INTEGER PRIMARY KEY AUTOINCREMENT,
`pro` INTEGER,
`mem` INTEGER,
`addtime` INTEGER
);
CREATE TABLE IF NOT EXISTS `diskio` (
`id` INTEGER PRIMARY KEY AUTOINCREMENT,
`read_count` INTEGER,
`write_count` INTEGER,
`read_bytes` INTEGER,
`write_bytes` INTEGER,
`read_time` INTEGER,
`write_time` INTEGER,
`addtime` INTEGER
);
CREATE TABLE IF NOT EXISTS `load_average` (
`id` INTEGER PRIMARY KEY AUTOINCREMENT,
`pro` REAL,
`one` REAL,
`five` REAL,
`fifteen` REAL,
`addtime` INTEGER
);

View File

@@ -1,58 +0,0 @@
server
{
listen {$PORT};
listen [::]:{$PORT};
server_name {$SERVER_NAME};
index index.php index.html index.htm default.php default.htm default.html;
root {$ROOT_DIR};
#SSL-START
#error_page 404/404.html;
#SSL-END
#301-START
#PROXY-START
#ERROR-PAGE-START
#error_page 404 /404.html;
#error_page 502 /502.html;
#ERROR-PAGE-END
#PHP-INFO-START
include {$PHP_DIR}/conf/enable-php-{$PHPVER}.conf;
#PHP-INFO-END
#REWRITE-START
include {$OR_REWRITE}/{$SERVER_NAME}.conf;
#REWRITE-END
#禁止访问的文件或目录
location ~ ^/(\.user.ini|\.htaccess|\.git|\.svn|\.project|LICENSE|README.md)
{
return 404;
}
#一键申请SSL证书验证目录相关设置
location ~ \.well-known{
allow all;
}
location ~ .*\\.(gif|jpg|jpeg|png|bmp|swf)$
{
expires 30d;
error_log off;
access_log /dev/null;
}
location ~ .*\\.(js|css)?$
{
expires 12h;
error_log off;
access_log /dev/null;
}
access_log {$LOGPATH}/{$SERVER_NAME}.log main;
error_log {$LOGPATH}/{$SERVER_NAME}.error.log;
}

View File

@@ -1,55 +0,0 @@
#BINDING-{$DIRBIND}-START
server
{
listen {$PORT};
server_name {$DIRBIND};
index index.php index.html index.htm default.php default.htm default.html;
root {$ROOT_DIR};
#SSL-START
#error_page 404/404.html;
#SSL-END
#ERROR-PAGE-START
#error_page 404 /404.html;
#error_page 502 /502.html;
#ERROR-PAGE-END
#PHP-INFO-START
include enable-php-{$PHPVER}.conf;
#PHP-INFO-END
#REWRITE-START
include {$OR_REWRITE}/{$SERVER_MAIN}.conf;
#REWRITE-END
#禁止访问的文件或目录
location ~ ^/(\.user.ini|\.htaccess|\.git|\.svn|\.project|LICENSE|README.md)
{
return 404;
}
#一键申请SSL证书验证目录相关设置
location ~ \.well-known{
allow all;
}
location ~ .*\\.(gif|jpg|jpeg|png|bmp|swf)$
{
expires 30d;
error_log off;
access_log /dev/null;
}
location ~ .*\\.(js|css)?$
{
expires 12h;
error_log off;
access_log /dev/null;
}
access_log {$LOGPATH}/{$SERVER_MAIN}_{$DIRBIND}.log;
error_log {$LOGPATH}/{$SERVER_MAIN}_{$DIRBIND}.error.log;
}
#BINDING-{$DIRBIND}-END

View File

@@ -1,62 +0,0 @@
FROM debian:10.12-slim
STOPSIGNAL SIGRTMIN+3
RUN rm -f /lib/systemd/system/multi-user.target.wants/* \
/etc/systemd/system/*.wants/* \
/lib/systemd/system/local-fs.target.wants/* \
/lib/systemd/system/sockets.target.wants/*udev* \
/lib/systemd/system/sockets.target.wants/*initctl* \
/lib/systemd/system/sysinit.target.wants/systemd-tmpfiles-setup* \
/lib/systemd/system/systemd-update-utmp*
RUN apt update -y && \
apt install -y devscripts && \
apt install -y wget zip unzip && \
apt-get install -y locales && \
localedef -i en_US -c -f UTF-8 -A /usr/share/locale/locale.alias en_US.UTF-8 && \
locale-gen en_US.UTF-8 && \
export LC_ALL=en_US.UTF-8 && \
export LANG=en_US.UTF-8 && \
export LANGUAGE=en_US.UTF-8 && \
localedef -i en_US -f UTF-8 en_US.UTF-8 && \
mkdir -p /www/server && \
mkdir -p /www/wwwroot && \
mkdir -p /www/wwwlogs && \
mkdir -p /www/backup/database && \
mkdir -p /www/backup/site && \
wget -O /tmp/dev.zip https://github.com/midoks/mdserver-web/archive/refs/heads/dev.zip && \
cd /tmp && unzip /tmp/dev.zip && \
mv -f /tmp/mdserver-web-dev /www/server/mdserver-web && \
rm -rf /tmp/dev.zip && \
rm -rf /tmp/mdserver-web-dev && \
cd /www/server/mdserver-web && \
bash scripts/install/debian.sh && \
cd /www/server/mdserver-web/ && \
/www/server/mdserver-web/bin/python tools.py username username && \
cd /www/server/mdserver-web/ && \
/www/server/mdserver-web/bin/python tools.py panel password
RUN cd /www/server/mdserver-web/plugins/php && \
bash install.sh install 74 && \
cd /www/server/mdserver-web/plugins/openresty && \
bash install.sh install 1.21.4.1 && \
cd /www/server/mdserver-web/plugins/mysql && \
bash install.sh install 5.6 && \
cd /www/server/mdserver-web/plugins/phpmyadmin && \
bash install.sh install 4.4.15 && \
systemctl enable openresty && \
systemctl enable php74 && \
systemctl enable mysql
RUN rm -rf /www/server/mysql/data
ADD ./start.sh /start.sh
ADD start.service /usr/lib/systemd/system/start.service
#RUN systemctl enable start
CMD [ "/lib/systemd/systemd", "log-level=info", "unit=sysinit.target" ]
EXPOSE 7200 80 443 888
VOLUME [ "/www" ]

View File

@@ -1,9 +0,0 @@
[Unit]
Description=Customize the script
After=network.target
[Service]
ExecStart=/start.sh
[Install]
WantedBy=multi-user.target

View File

@@ -1 +0,0 @@
#!/bin/bash

BIN
images/slideshow/mw-php.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 55 KiB

96
index.html Normal file
View File

@@ -0,0 +1,96 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<link rel="icon" type="image/x-icon" href="./static/images/logo.png">
<link type="text/css" href="./static/css/pages.css" media="all" rel="stylesheet">
<title>mdserver-web | Linux开源面板</title>
<meta name="generator" content="Jekyll v3.9.3">
<meta property="og:title" content="Linux开源面板">
<meta property="og:locale" content="en_US">
<meta name="description" content="Linux开源面板">
<meta property="og:description" content="Linux开源面板">
<link rel="canonical" href="https://www.midoks.me/">
<meta property="og:url" content="https://www.midoks.me/">
<meta property="og:site_name" content="Linux开源面板">
<meta property="og:type" content="website">
<meta name="twitter:card" content="summary">
<meta property="twitter:title" content="Linux开源面板">
<meta name="twitter:site" content="@github">
<script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-7736695762519033"
crossorigin="anonymous"></script>
</head>
<body class="home" style="">
<section id="hero-spot" class="hero-spot">
<a href="https://pages.github.com/">
<img src="./static/images/logo.png" alt="Linux开源面板" class="logo">
</a>
<h1>管理你的服务器</h1>
<a href="https://github.com/midoks/mdserver-web" class="help-link">Github</a>
<div id="slideshow">
<img src="./static/images/mw.jpg" alt="mdserver-web" class="slide active" width="893">
</div>
</section>
<section id="tutorial" class="tutorial">
<h1>准备好开始了吗?</h1>
<!-- ### Start of tutorials -->
<ul id="user-site" class="tutorial-list wrapper active">
<li id="terminal-step-1" class="option-terminal">
<h4>安装面板</h4>
<div class="terminal">
<div class="header"></div>
<div class="shell">
<p><span class="path">~</span><span class="prompt">$</span>curl -fsSL https://cdn.jsdelivr.net/gh/midoks/mdserver-web@latest/scripts/install.sh | bash</p>
<p><span class="path">~</span><span class="prompt">$</span>mw default</p>
<p>==================================================================</p>
<p>MW-Panel default info!</p>
<p>==================================================================</p>
<p>MW-Panel-Url: http://0.0.0.0:13148/xxx</p>
<p>username: yrajbw9q</p>
<p>password: oqxotjgb</p>
<p>Warning:</p>
<p>If you cannot access the panel. </p>
<p>release the following port (44010|888|80|443|22) in the security group.</p>
<p>==================================================================</p>
</div>
</div>
</li>
</ul>
<!-- End of user site tutorial -->
</section>
<!-- End of tutorial section -->
<script src="./static/js/jquery.js"></script>
<script>window.slides = {"mdserver-web":"mw","mw-php":"php",}</script>
<script src="./static/js/application.js"></script>
<footer class="page-footer">
<ul class="site-footer-links right">
<li><a href="https://bbs.midoks.icu">论坛</a></li>
</ul>
<ul class="site-footer-links">
<li>mdserver-web ©2018-∞ Linux面板</li>
<li><a href="https://github.com/midoks/mdserver-web">Github</a></li>
</ul>
</footer>
</body>
</html>

View File

@@ -1,418 +0,0 @@
# coding:utf-8
'''
doc: https://docs.python.org/zh-cn/3/library/ftplib.html
'''
import sys
import io
import os
import time
import re
import json
import paramiko
import ftplib
sys.path.append(os.getcwd() + "/class/core")
import mw
DEBUG = True
BLOCK_SIZE = 1024 * 1024 * 2
# BLOCK_SIZE = 50
PROGRESS_FILE_NAME = "PROGRESS_FILE_NAME"
"""
=============自定义异常===================
"""
class OsError(Exception):
"""OS端异常"""
class ObjectNotFound(OsError):
"""对象不存在时抛出的异常"""
def __init__(self, *args, **kwargs):
message = "文件对象不存在。"
super(ObjectNotFound, self).__init__(message, *args, **kwargs)
class APIError(Exception):
"""API参数错误异常"""
def __init__(self, *args, **kwargs):
_api_error_msg = 'API资料校验失败,请核实!'
super(APIError, self).__init__(_api_error_msg, *args, **kwargs)
class FtpPSClient:
_title = "FTP"
_name = "ftp"
__host = None
__port = None
__user = None
__password = None
default_port = 21
default_backup_path = "/backup/"
config_file = "cfg.json"
def __init__(self, load_config=True, timeout=10):
self.timeout = timeout
if load_config:
data = self.get_config()
self.injection_config(data)
def get_config(self):
default_config = {
"ftp_host": '',
"ftp_user": '',
"ftp_pass": '',
"backup_path": self.default_backup_path
}
cfg = mw.getServerDir() + "/backup_ftp/" + self.config_file
if os.path.exists(cfg):
data = mw.readFile(cfg)
return json.loads(data)
else:
return default_config
def injection_config(self, data):
host = data["ftp_host"].strip()
if host.find(':') == -1:
self.__port = self.default_port
self.__host = data['ftp_host'].strip()
self.__user = data['ftp_user'].strip()
self.__password = data['ftp_pass'].strip()
bp = data['backup_path'].strip()
if bp:
self.backup_path = self.getPath(bp)
else:
self.backup_path = self.getPath(self.default_backup_path)
def authorize(self):
try:
if self.timeout is not None:
ftp = ftplib.FTP(timeout=self.timeout)
else:
ftp = ftplib.FTP()
debuglevel = 0
# if DEBUG:
# debuglevel = 3
ftp.set_debuglevel(debuglevel)
# ftp.set_pasv(True)
ftp.connect(self.__host, int(self.__port))
ftp.login(self.__user, self.__password)
return ftp
except Exception as e:
raise OsError("无法连接FTP客户端,请检查配置参数是否正确!")
# 取目录路径
def getPath(self, path):
if path[-1:] != '/':
path += '/'
if path[:1] != '/':
path = '/' + path
return path.replace('//', '/')
def generateDownloadUrl(self, object_name):
return 'ftp://' + \
self.__user + ':' + \
self.__password + '@' + \
self.__host + ':' + \
"/" + object_name
def buildDirName(self, data_type, file_name):
import re
prefix_dict = {
"site": "web",
"database": "db",
"path": "path",
}
file_regx = prefix_dict.get(data_type) + "_(.+)_20\d+_\d+\."
sub_search = re.search(file_regx, file_name)
sub_path_name = ""
if sub_search:
sub_path_name = sub_search.groups()[0]
sub_path_name += '/'
# 构建OS存储路径
object_name = self.backup_path + \
data_type + '/' + \
sub_path_name + \
file_name
return object_name
def uploadFile(self, filename, data_type=None, *args, **kwargs):
client = self.authorize()
local_file_name = filename
filename = os.path.abspath(filename)
dirname = os.path.dirname(filename)
temp_name = os.path.split(filename)[1]
object_name = self.buildDirName(data_type, temp_name)
upload_tmp_dir = os.path.join(dirname, ".upload_tmp")
if not os.path.exists(upload_tmp_dir):
os.mkdir(upload_tmp_dir)
print("|-正在上传文件到 {}".format(object_name))
total_bytes = os.path.getsize(filename)
object_md5_name = mw.md5(object_name)
pg_file = os.path.join(upload_tmp_dir, object_md5_name + ".pl")
block_size = BLOCK_SIZE
if kwargs.get("block_size"):
try:
block_size = float(kwargs.get("block_size"))
except:
pass
remote_file_size = None
if not os.path.exists(pg_file):
# import uuid
# uid = str(uuid.uuid1())
progress_info = {
"filename": local_file_name,
"total_bytes": total_bytes,
"uploaded_bytes": 0,
}
mw.writeFile(pg_file, json.dumps(progress_info))
else:
progress_info = json.loads(mw.readFile(pg_file))
if total_bytes == progress_info.get("total_bytes"):
# 取远程文件大小
_max_loop = 10
while _max_loop > 0:
try:
time.sleep(1)
remote_file_size = client.size(object_name)
if remote_file_size > total_bytes:
remote_file_size = None
break
except Exception as e:
if DEBUG:
print(type(e))
print(e)
_max_loop -= 1
else:
remote_file_size = None
uploaded_bytes = 0 if remote_file_size is None else remote_file_size
dir_name = os.path.split(object_name)[0]
if dir_name:
self.createDirP(dir_name)
upload_start = time.time()
try:
if total_bytes > 1024 * 1024 * 1024:
with open(local_file_name, 'rb') as file_handler:
if remote_file_size is not None:
file_handler.seek(remote_file_size)
client.voidcmd("TYPE I")
datasock = ''
esize = ''
datasock, esize = client.ntransfercmd(
"STOR " + object_name, remote_file_size)
while True:
buf = file_handler.read(block_size)
if not len(buf):
break
datasock.sendall(buf)
uploaded_bytes += len(buf)
if DEBUG:
print('\ruploading %.2f%%' %
(float(uploaded_bytes) / total_bytes * 100))
print("uploaded_bytes", uploaded_bytes)
if uploaded_bytes == total_bytes:
break
datasock.close()
if DEBUG:
print('close data handle')
try:
client.voidcmd('NOOP')
except Exception as e:
if DEBUG:
print("Send NOOP command error:")
print(e)
else:
if DEBUG:
print('keep alive cmd success')
client.voidresp()
if DEBUG:
print('No loop cmd')
else:
# 小于1G文件直接上传
file_handler = open(local_file_name, "rb")
client.storbinary('STOR %s' % object_name,
file_handler, blocksize=block_size)
file_handler.close()
except Exception as e:
print(str(e))
completed_file_size = None
_max_loop = 10
while _max_loop > 0:
try:
time.sleep(1)
completed_file_size = client.size(object_name)
break
except Exception as e:
_max_loop -= 1
if DEBUG:
print("size error:" + str(e))
# 上传完成
if completed_file_size == total_bytes:
if DEBUG:
upload_completed = time.time()
upload_diff = upload_completed - upload_start
print("文件上传成功, 耗时: {}s。".format(upload_diff))
if os.path.exists(pg_file):
os.remove(pg_file)
return True
else:
if os.path.exists(pg_file):
os.remove(pg_file)
print("文件上传后大小不一致!")
print("completed_file_size:" + str(completed_file_size))
print("total_bytes:", total_bytes)
print("object_md5_name:", object_md5_name)
print("pg_file:", pg_file)
print("filename:", filename)
print("dirname:", dirname)
print("object_name:", object_name)
return False
def createDirP(self, dir_name):
"""创建远程目录
:param dir_name: 目录名称
:return:
"""
try:
dirnames = dir_name.split('/')
ftp = self.authorize()
# ftp.cwd(get.path);
for dirname in dirnames:
if not dirname or not dirname.strip():
continue
try:
flist = ftp.nlst()
if not dirname in flist:
ftp.mkd(dirname)
except:
# print("mlsd mode.")
try:
flist = list(ftp.mlsd())[1:]
for f in flist:
if dirname == f[0]:
break
else:
ftp.mkd(dirname)
except:
return False
ftp.cwd(dirname)
return True
except:
return False
def createDir(self, path, name):
ftp = self.authorize()
path = self.getPath(path)
ftp.cwd(path)
try:
ftp.mkd(name)
ftp.close()
return True
except Exception as e:
print(str(e))
ftp.close()
return False
def deleteDir(self, path, dir_name):
try:
ftp = self.authorize()
ftp.rmd(dir_name)
return True
except ftplib.error_perm as e:
print("deleteDir:" + str(e) + ":" + dir_name)
except Exception as e:
print(e)
return False
def deleteFile(self, filename):
try:
ftp = self.authorize()
ftp.delete(filename)
return True
except Exception as e:
print(str(e))
return False
def getList(self, path="/"):
ftp = self.authorize()
path = self.getPath(path)
ftp.cwd(path)
mlsd = False
try:
files = list(ftp.mlsd())
mlsd = True
except:
try:
files = ftp.nlst(path)
mlsd = False
except:
raise RuntimeError("FTP服务器数据返回异常!")
ftp.close()
# print(files)
f_list = []
dirs = []
data = []
default_time = '1971/01/01 01:01:01'
for dt in files:
# print(dt)
if mlsd:
dt_name = dt[0]
dt_info = dt[1]
else:
if dt.find("/") >= 0:
dt = dt.split("/")[-1]
tmp = {}
tmp['name'] = dt_name
if dt_name == '.' or dt_name == '..':
continue
tmp['time'] = dt_info['modify']
try:
tmp['size'] = dt_info['size']
tmp['type'] = "File"
tmp['download'] = self.generateDownloadUrl(path + dt_name)
f_list.append(tmp)
except:
tmp['size'] = dt_info['sizd']
tmp['type'] = None
tmp['download'] = ''
dirs.append(tmp)
data = dirs + f_list
mlist = {}
mlist['path'] = path
mlist['list'] = data
return mlist

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

View File

@@ -1,101 +0,0 @@
<style>
.upyunCon {
height: 428px;
}
.up-place {
height: 62px;
border-bottom: 1px solid #ddd;
}
.up-place .btn {
border-radius: 0;
}
.up-place .place-input {
background-color: #f3f3f3;
border: 1px solid #ccc;
height: 30px;
line-height: 28px;
overflow: hidden;
margin: 1px 0 0 -1px;
width: 340px;
}
.place-input ul {
display: inline-block;
position: relative;
width: auto;
}
.place-input ul li {
background: url("/static/img/ico/ico-ltr.png") no-repeat right center;
float: left;
padding-left: 10px;
padding-right: 18px;
}
.place-input ul li a {
height: 28px;
cursor: pointer;
display: inline-block;
}
.upyunlist {
height: 516px;
overflow: auto;
}
.up-bottom {
background-color: #fafafa;
border-top: 1px solid #eee;
bottom: 0;
position: absolute;
width: 100%;
}
.up-use {
line-height: 50px
}
.list-list .cursor span {
line-height: 30px;
}
.btn-title {
margin-top: 1px
}
.tip {
font-size: 10px;
font-style: oblique;
color: green;
}
</style>
<div class="upyunCon">
<div class="up-place pd15">
<button id="backBtn" class="btn btn-default btn-sm glyphicon glyphicon-arrow-left pull-left" title="后退"></button>
<input id="myPath" style="display:none;" type="text" value="">
<div class="place-input pull-left">
<div style="width:1400px;height:28px"><ul></ul></div>
</div>
<button class="refreshBtn btn btn-default btn-sm glyphicon glyphicon-refresh pull-left mr20" title="刷新" style="margin-left:-1px;"></button>
<button class="btn btn-default btn-sm pull-right btn-title" onclick="upyunApi()">帐户设置</button>
<button class="btn btn-default btn-sm pull-right mr20 btn-title" onclick="createDir()">新建文件夹</button>
</div>
<div class="upyunlist pd15">
<div class="divtable" style="margin-bottom:15px">
<table class="table table-hover">
<thead><tr><th>名称</th><th>大小</th><th>更新时间</th><th class="text-right">操作</th></tr></thead>
<tbody class="list-list"></tbody>
</table>
</div>
</div>
</div>
<script type="text/javascript">
$.getScript( "/plugins/file?name=backup_ftp&f=js/backup_ftp.js", function(){
osList('/');
});
</script>

View File

@@ -1,257 +0,0 @@
# coding:utf-8
import sys
import io
import os
import time
import re
import json
# print(sys.platform)
if sys.platform != "darwin":
os.chdir("/www/server/mdserver-web")
sys.path.append(os.getcwd() + "/class/core")
import mw
_ver = sys.version_info
is_py2 = (_ver[0] == 2)
is_py3 = (_ver[0] == 3)
DEBUG = False
if is_py2:
reload(sys)
sys.setdefaultencoding('utf-8')
app_debug = False
if mw.isAppleSystem():
app_debug = True
def getPluginName():
return 'backup_ftp'
def getPluginDir():
return mw.getPluginDir() + '/' + getPluginName()
sys.path.append(getPluginDir() + "/class")
from ftp_client import FtpPSClient
def getServerDir():
return mw.getServerDir() + '/' + getPluginName()
def getArgs():
args = sys.argv[2:]
tmp = {}
args_len = len(args)
if args_len == 1:
t = args[0].strip('{').strip('}')
t = t.split(':')
tmp[t[0]] = t[1]
elif args_len > 1:
for i in range(len(args)):
t = args[i].split(':')
tmp[t[0]] = t[1]
return tmp
def checkArgs(data, ck=[]):
for i in range(len(ck)):
if not ck[i] in data:
return (False, mw.returnJson(False, '参数:(' + ck[i] + ')没有!'))
return (True, mw.returnJson(True, 'ok'))
def status():
return 'start'
def getConf():
cfg = getServerDir() + "/cfg.json"
if not os.path.exists(cfg):
return mw.returnJson(False, "未配置", [])
data = mw.readFile(cfg)
data = json.loads(data)
return mw.returnJson(True, "OK", data)
def setConf():
args = getArgs()
data = checkArgs(args, ['use_sftp', 'ftp_user',
'ftp_pass', 'ftp_host', 'backup_path'])
if not data[0]:
return data[1]
cfg = getServerDir() + "/cfg.json"
values = ['ftp_user',
'ftp_pass',
'ftp_host']
for v in values:
if args[v] == '':
return mw.returnJson(False, '必填资料不能为空,请核实!', [])
if args['backup_path'] == '':
args['backup_path'] = "/backup"
try:
ftp = FtpPSClient(load_config=False)
ftp.injection_config(args)
data = ftp.getList("/")
if data:
mw.writeFile(cfg, mw.getJson(args))
return mw.returnJson(True, '设置成功', [])
except Exception as e:
return mw.returnJson(False, "FTP校验失败,请核实!\n" + str(e), [])
def getList():
cfg = getServerDir() + "/cfg.json"
if not os.path.exists(cfg):
return mw.returnJson(False, "未配置FTP,请点击`账户设置`", [])
args = getArgs()
data = checkArgs(args, ['path'])
if not data[0]:
return data[1]
try:
ftp = FtpPSClient()
flist = ftp.getList(args['path'])
return mw.returnJson(True, "ok", flist)
except Exception as e:
return mw.returnJson(False, str(e), [])
def createDir():
cfg = getServerDir() + "/cfg.json"
if not os.path.exists(cfg):
return mw.returnJson(False, "未配置FTP,请点击`账户设置`", [])
args = getArgs()
data = checkArgs(args, ['path', 'name'])
if not data[0]:
return data[1]
ftp = FtpPSClient()
isok = ftp.createDir(args['path'], args['name'])
if isok:
return mw.returnJson(True, "创建成功")
return mw.returnJson(False, "创建失败")
def deleteDir():
args = getArgs()
data = checkArgs(args, ['dir_name', 'path'])
if not data[0]:
return data[1]
ftp = FtpPSClient()
isok = ftp.deleteDir(args['path'], args['dir_name'])
if isok:
return mw.returnJson(True, "删除成功")
return mw.returnJson(False, "删除失败")
def deleteFile():
args = getArgs()
data = checkArgs(args, ['path', 'filename'])
if not data[0]:
return data[1]
ftp = FtpPSClient()
isok = ftp.deleteFile(args['path'] + "/" + args['filename'])
if isok:
return mw.returnJson(True, "删除成功")
return mw.returnJson(False, "删除失败")
def backupAllFunc(stype):
os.chdir(mw.getRunDir())
name = sys.argv[2]
num = sys.argv[3]
args = stype + " " + name + " " + num
cmd = 'python3 ' + mw.getRunDir() + '/scripts/backup.py ' + args
os.system(cmd)
# 开始执行上传信息
prefix_dict = {
"site": "web",
"database": "db",
"path": "path",
}
find_path = mw.getBackupDir() + '/' + stype + '/' + \
prefix_dict[stype] + '_' + name
find_new_file = "ls " + find_path + \
"_* | grep '.gz' | cut -d \ -f 1 | awk 'END {print}'"
filename = mw.execShell(find_new_file)[0].strip()
if filename == "":
print("not find upload file!")
return False
ftp = FtpPSClient()
ftp.uploadFile(filename, stype)
return True
def backupSite():
# 备份站点
pass
def in_array(name, arr=[]):
for x in arr:
if name == x:
return True
return False
def installPreInspection():
return 'ok'
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 == 'install_pre_inspection':
print(installPreInspection())
elif func == 'conf':
print(getConf())
elif func == 'set_config':
print(setConf())
elif func == "get_list":
print(getList())
elif func == "create_dir":
print(createDir())
elif func == "delete_dir":
print(deleteDir())
elif func == 'delete_file':
print(deleteFile())
elif in_array(func, ['site', 'database', 'path']):
print(backupAllFunc(func))
else:
print('error')

View File

@@ -1,17 +0,0 @@
{
"title":"FTP存储空间",
"hook":["backup"],
"tip":"soft",
"name":"backup_ftp",
"type":"运行环境",
"ps":"将网站或数据库打包备份到FTP存储空间",
"versions":["1.0"],
"install_pre_inspection":false,
"shell":"install.sh",
"checks":"server/backup_ftp",
"path": "server/backup_ftp",
"author":"midoks",
"home":"",
"date":"2022-10-23",
"pid": "4"
}

View File

@@ -1,32 +0,0 @@
#!/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
sys_os=`uname`
Install_App()
{
mkdir -p ${serverPath}/backup_ftp
echo "${1}" > ${serverPath}/backup_ftp/version.pl
echo '安装完成' > $install_tmp
}
Uninstall_App()
{
rm -rf ${serverPath}/backup_ftp
}
action=$1
if [ "${1}" == 'install' ];then
Install_App $2
else
Uninstall_App $2
fi

View File

@@ -1,258 +0,0 @@
function bkfPost(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 });
$.post('/plugins/run', {name:'backup_ftp', func:method, args:_args}, function(data) {
layer.close(loadT);
if (!data.status){
layer.msg(data.msg,{icon:0,time:2000,shade: [0.3, '#000']});
return;
}
if(typeof(callback) == 'function'){
callback(data);
}
},'json');
}
function getFtpLocalTime(data){
var str = data.slice(0,4)+"/"+data.slice(4,6)+"/"+data.slice(6,8)
+ " " + data.slice(8,10)+":"+data.slice(10,12)+":"+data.slice(12,14);
return str;
}
// 自定义部分
var i = null;
//设置API
function upyunApi(){
bkfPost('conf', {}, function(rdata){
var rdata = $.parseJSON(rdata.data);
var token = rdata.data;
var check_status = token.use_sftp;
var sftp_checked = check_status === "true" ? " checked=\"checked\"" : "";
if (typeof(token.ftp_host) == 'undefined'){
token.ftp_host = '';
}
if (typeof(token.ftp_user) == 'undefined'){
token.ftp_user = '';
}
if (typeof(token.ftp_pass) == 'undefined'){
token.ftp_pass = '';
}
if (typeof(token.backup_path) == 'undefined'){
token.backup_path = '';
}
var apicon = '<div class="bingfa mtb15" style="padding-bottom:0px;">\
<p>\
<span class="span_tit">使用SFTP:</span>\ <input style="width: 20px; vertical-align:middle;" type="checkbox" name="use_sftp"'+sftp_checked+'> 是否使用SFTP进行数据传输 \
</p>\
<p>\
<span class="span_tit">Host:</span>\
<input placeholder="请输入主机地址" style="width: 200px;" type="text" name="upyun_service" value="'+token.ftp_host+'"> *服务器地址,FTP默认端口21, SFTP默认端口22\
</p>\
<p>\
<span class="span_tit">用户名:</span>\
<input style="width: 200px;" type="text" name="ftp_username" value="'+token.ftp_user+'"> *指定用户名\
</p>\
<p>\
<span class="span_tit">密码:</span>\
<input style="width: 200px;" type="password" name="ftp_password" value="'+token.ftp_pass+'"> *登录密码\
</p>\
<p>\
<span class="span_tit">存储位置:</span>\
<input placeholder="请输入存储位置" style="width: 200px;" type="text" name="backup_path" value="'+token.backup_path+'"> *相对于根目录的路径,默认是/backup\
</p>\
</div>';
layer.open({
type: 1,
area: "600px",
title: "FTP/SFTP帐户设置",
closeBtn: 1,
shift: 5,
shadeClose: false,
btn: ['确定','取消'],
content:apicon,
yes:function(index,layero){
var data = {
use_sftp:$("input[name='use_sftp']").prop('checked'),
ftp_user:$("input[name='ftp_username']").val(),
ftp_pass:$("input[name='ftp_password']").val(),
ftp_host:$("input[name='upyun_service']").val(),
backup_path:$("input[name='backup_path']").val()
}
bkfPost('set_config', data, function(rdata){
var rdata = $.parseJSON(rdata.data);
if (rdata.status){
showMsg(rdata.msg,function(){
layer.close(index);
osList("/");
},{icon:1},2000);
} else{
layer.msg(rdata.msg,{icon:2});
}
})
},
});
});
}
function createDir(){
layer.open({
type: 1,
area: "400px",
title: "创建目录",
closeBtn: 1,
shift: 5,
shadeClose: false,
btn: ['确定','取消'],
content:'<div class="bingfa bt-form c6" style="padding-bottom: 10px;">\
<p>\
<span class="span_tit">目录名称:</span>\
<input style="width: 200px;" type="text" name="newPath" value="">\
</p>\
</div>',
success:function(){
$("input[name='newPath']").focus().keyup(function(e){
if(e.keyCode == 13) $(".layui-layer-btn0").click();
});
},
yes:function(index,layero){
var name = $("input[name='newPath']").val();
if(name == ''){
layer.msg('目录名称不能为空!',{icon:2});
return;
}
var path = $("#myPath").val();
var dirname = name;
// var loadT = layer.msg('正在创建目录['+dirname+']...',{icon:16,time:0,shade: [0.3, '#000']});
bkfPost('create_dir', {path:path,name:dirname}, function(data){
var rdata = $.parseJSON(data.data);
if(rdata.status) {
showMsg(rdata.msg, function(){
layer.close(index);
osList(path);
} ,{icon:1}, 2000);
} else{
layer.msg(rdata.msg,{icon:2});
}
});
}
});
}
//删除文件
function deleteFile(name, is_dir){
if (is_dir === false){
safeMessage('删除文件','删除后将无法恢复,真的要删除['+name+']吗?',function(){
var path = $("#myPath").val();
var filename = name;
bkfPost('delete_file', {filename:filename,path:path}, function(rdata){
var rdata = $.parseJSON(rdata.data);
showMsg(rdata.msg,function(){
osList(path);
},{icon:rdata.status?1:2},2000);
});
});
} else {
safeMessage('删除文件夹','删除后将无法恢复,真的要删除['+name+']吗?',function(){
var path = $("#myPath").val();
bkfPost('delete_dir', {dir_name:name,path:path}, function(rdata){
var rdata = $.parseJSON(rdata.data);
showMsg(rdata.msg,function(){
osList(path);
},{icon:rdata.status?1:2},2000);
});
});
}
}
function osList(path){
bkfPost('get_list', {path:path}, function(rdata){
var rdata = $.parseJSON(rdata.data);
if(rdata.status === false){
showMsg(rdata.msg,function(){
upyunApi();
},{icon:2},2000);
return;
}
var mlist = rdata.data;
// console.log(mlist);
var listBody = ''
var listFiles = ''
for(var i=0;i<mlist.list.length;i++){
if(mlist.list[i].type == null){
listBody += '<tr><td class="cursor" onclick="osList(\''+(path+'/'+mlist.list[i].name).replace('//','/')+'\')"><span class="ico ico-folder"></span>\<span>'+mlist.list[i].name+'</span></td>\
<td>-</td>\
<td>-</td>\
<td class="text-right"><a class="btlink" onclick="deleteFile(\''+mlist.list[i].name+'\', true)">删除</a></td></tr>'
}else{
listFiles += '<tr><td class="cursor"><span class="ico ico-file"></span>\<span>'+mlist.list[i].name+'</span></td>\
<td>'+toSize(mlist.list[i].size)+'</td>\
<td>'+getFtpLocalTime(mlist.list[i].time)+'</td>\
<td class="text-right"><a target="_blank" href="'+mlist.list[i].download+'" class="btlink">下载</a> | <a class="btlink" onclick="deleteFile(\''+mlist.list[i].name+'\', false)">删除</a></td></tr>'
}
}
listBody += listFiles;
var pathLi='';
var tmp = path.split('/')
var pathname = '';
var n = 0;
for(var i=0;i<tmp.length;i++){
if(n > 0 && tmp[i] == '') continue;
var dirname = tmp[i];
if(dirname == '') {
dirname = '根目录';
n++;
}
pathname += '/' + tmp[i];
pathname = pathname.replace('//','/');
pathLi += '<li><a title="'+pathname+'" onclick="osList(\''+pathname+'\')">'+dirname+'</a></li>';
}
var um = 1;
if(tmp[tmp.length-1] == '') um = 2;
var backPath = tmp.slice(0,tmp.length-um).join('/') || '/';
$('#myPath').val(path);
$(".upyunCon .place-input ul").html(pathLi);
$(".upyunlist .list-list").html(listBody);
upPathLeft();
$('#backBtn').unbind().click(function() {
osList(backPath);
});
$('.upyunCon .refreshBtn').unbind().click(function(){
osList(path);
});
});
}
//计算当前目录偏移
function upPathLeft(){
var UlWidth = $(".place-input ul").width();
var SpanPathWidth = $(".place-input").width() - 20;
var Ml = UlWidth - SpanPathWidth;
if(UlWidth > SpanPathWidth ){
$(".place-input ul").css("left",-Ml)
}
else{
$(".place-input ul").css("left",0)
}
}
// $('.layui-layer-page').css('height','670px');

Binary file not shown.

Before

Width:  |  Height:  |  Size: 254 B

View File

@@ -1,28 +0,0 @@
<div class="bt-form">
<div class="bt-w-main">
<div class="bt-w-menu">
<p class="bgw" onclick="pluginService('clean');">服务</p>
<p onclick="pluginConfig('clean');">配置修改</p>
<p onclick="pluginLogs('clean',null,'run_log');">运行日志</p>
<p onclick="pRead()">说明</p>
</div>
<div class="bt-w-con pd15">
<div class="soft-man-con"></div>
</div>
</div>
</div>
<script type="text/javascript">
pluginService('clean');
function pRead(){
var readme = '<ul class="help-info-text c7">';
readme += '<li>启动后,加入到计划中[2:30]执行。</li>';
readme += '<li>【*】代表要删除。</li>';
readme += '<li>添加/删除清除日志,可在`配置修改`中添加/删除。</li>';
readme += '<li>如果是目录,会寻找【.log】后缀的文件清空。</li>';
readme += '</ul>';
$('.soft-man-con').html(readme);
}
</script>

View File

@@ -1,227 +0,0 @@
# coding:utf-8
import sys
import io
import os
import time
sys.path.append(os.getcwd() + "/class/core")
import mw
app_debug = False
if mw.isAppleSystem():
app_debug = True
def getPluginName():
return 'clean'
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 getConf():
path = getServerDir() + "/clean.conf"
return path
def getArgs():
args = sys.argv[2:]
tmp = {}
args_len = len(args)
if args_len == 1:
t = args[0].strip('{').strip('}')
t = t.split(':')
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 getLockFile():
return getServerDir() + "/installed_lock.pl"
def runLog():
return getServerDir() + "/clean.log"
def status():
initConf()
if os.path.exists(getLockFile()):
return "start"
return 'stop'
def initConf():
conf = getConf()
if not os.path.exists(conf):
content = ""
clog = [
"/var/spool/clientmqueue/*",
"/var/log/cron-*",
"/var/log/maillog-*",
"/var/log/secure-*",
"/var/log/spooler-*",
"/var/log/yum.log-*",
"/var/log/messages-*",
"/var/log/btmp-*",
"/var/log/audit/audit.log.*",
"/var/log/rhsm/rhsm.log-*",
"/var/log/rhsm/rhsmcertd.log-*",
"/tmp/yum_save_*",
"/tmp/tmp.*",
]
for i in clog:
content += i + "\n"
# 常用日志
clogcom = [
"/var/log/messages",
"/var/log/btmp",
"/var/log/wtmp",
"/var/log/secure",
"/var/log/lastlog",
"/var/log/cron",
"/www/server/cron"
]
for i in clogcom:
if os.path.exists(i):
content += i + "\n"
# 清理日志
rootDir = "/var/log"
l = os.listdir(rootDir)
for x in range(len(l)):
abspath = rootDir + "/" + l[x]
content += abspath + "\n"
mw.writeFile(conf, content)
def start():
initConf()
lock_file = getLockFile()
if not os.path.exists(lock_file):
mw.writeFile(lock_file, "")
import tool_task
tool_task.createBgTask()
return 'ok'
return 'fail'
def stop():
initConf()
lock_file = getLockFile()
if os.path.exists(lock_file):
os.remove(lock_file)
import tool_task
tool_task.removeBgTask()
return 'ok'
return 'fail'
def reload():
return 'ok'
def get_filePath_fileName_fileExt(filename):
(filepath, tempfilename) = os.path.split(filename)
(shotname, extension) = os.path.splitext(tempfilename)
return filepath, shotname, extension
def cleanFileLog(path):
filepath, shotname, extension = get_filePath_fileName_fileExt(path)
if extension == ".log":
cmd = "echo \"\" > " + path
tmp = mw.execShell(cmd)
if tmp[1] != "":
cmd += " | error:" + tmp[1].strip()
print(cmd)
def cleanSelfFileLog(path):
filepath, shotname, extension = get_filePath_fileName_fileExt(path)
if extension == ".log":
cmd = "echo \"\" > " + path
tmp = mw.execShell(cmd)
if tmp[1] != "":
cmd += " | error:" + tmp[1].strip()
print(cmd)
def cleanDirLog(path):
l = os.listdir(path)
for x in range(len(l)):
abspath = path + "/" + l[x]
if os.path.isfile(abspath):
cleanFileLog(abspath)
if os.path.isdir(abspath):
cleanDirLog(abspath)
def cleanLog():
conf = getConf()
clist = mw.readFile(conf).strip()
clist = clist.split("\n")
for x in clist:
abspath = x.strip()
if abspath.find('*') > 1:
cmd = 'rm -rf ' + abspath
print(cmd)
data = mw.execShell(cmd)
# print(data)
continue
if os.path.exists(abspath):
if os.path.isfile(abspath):
cleanSelfFileLog(abspath)
continue
if os.path.isdir(abspath):
cleanDirLog(abspath)
continue
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 == 'conf':
print(getConf())
elif func == 'run_log':
print(runLog())
elif func == 'clean':
cleanLog()
else:
print('error')

View File

@@ -1,16 +0,0 @@
{
"title":"日志清理",
"tip":"soft",
"name":"clean",
"type":"运行环境",
"ps":"日志清理",
"versions":"1.0",
"updates":"1.0",
"shell":"install.sh",
"checks":"server/clean",
"path": "server/clean",
"author":"clean",
"home":"https://github.com/midoks",
"date":"2021-11-23",
"pid": "5"
}

View File

@@ -1,43 +0,0 @@
#!/bin/bash
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH
# cd /Users/midoks/Desktop/mwdev/server/mdserver-web/plugins/mongodb && /bin/bash install.sh install 5.0.4
# cd /www/server/mdserver-web/plugins/mongodb && /bin/bash install.sh install 5.0.4
curPath=`pwd`
rootPath=$(dirname "$curPath")
rootPath=$(dirname "$rootPath")
serverPath=$(dirname "$rootPath")
install_tmp=${rootPath}/tmp/mw_install.pl
VERSION=$2
if [ -f ${rootPath}/bin/activate ];then
source ${rootPath}/bin/activate
fi
Install_app()
{
echo '正在安装脚本文件...' > $install_tmp
mkdir -p $serverPath/clean
cd ${rootPath} && python3 ${rootPath}/plugins/clean/index.py start
echo "${VERSION}" > $serverPath/clean/version.pl
echo '安装完成' > $install_tmp
}
Uninstall_app()
{
cd ${rootPath} && python3 ${rootPath}/plugins/clean/index.py stop
rm -rf $serverPath/clean
echo "Uninstall_clean" > $install_tmp
}
action=$1
if [ "${1}" == 'install' ];then
Install_app
else
Uninstall_app
fi

View File

@@ -1,156 +0,0 @@
# coding:utf-8
import sys
import io
import os
import time
import json
sys.path.append(os.getcwd() + "/class/core")
import mw
app_debug = False
if mw.isAppleSystem():
app_debug = True
def getPluginName():
return 'clean'
def getPluginDir():
return mw.getPluginDir() + '/' + getPluginName()
def getServerDir():
return mw.getServerDir() + '/' + getPluginName()
def getTaskConf():
conf = getServerDir() + "/task_config.json"
return conf
def getConfigData():
try:
return json.loads(mw.readFile(getTaskConf()))
except:
pass
return []
def getConfigTpl():
tpl = {
"name": "",
"task_id": -1,
}
return tpl
def createBgTask():
removeBgTask()
args = {
"period": "day",
"hour": "2",
"minute": "30",
}
createBgTaskByName(getPluginName(), args)
def createBgTaskByName(name, args):
cfg = getConfigTpl()
_name = "[勿删]日志清理[" + name + "]"
res = mw.M("crontab").field("id, name").where("name=?", (_name,)).find()
if res:
return True
if "task_id" in cfg.keys() and cfg["task_id"] > 0:
res = mw.M("crontab").field("id, name").where(
"id=?", (cfg["task_id"],)).find()
if res and res["id"] == cfg["task_id"]:
print("计划任务已经存在!")
return True
import crontab_api
api = crontab_api.crontab_api()
period = args['period']
_hour = ''
_minute = ''
_where1 = ''
_type_day = "day"
if period == 'day':
_type_day = 'day'
_hour = args['hour']
_minute = args['minute']
elif period == 'minute-n':
_type_day = 'minute-n'
_where1 = args['minute-n']
_minute = ''
mw_dir = mw.getRunDir()
cmd = '''
mw_dir=%s
rname=%s
plugin_path=%s
script_path=%s
logs_file=$plugin_path/${rname}.log
''' % (mw_dir, name, getServerDir(), getPluginDir())
cmd += 'echo "★【`date +"%Y-%m-%d %H:%M:%S"`】 STSRT★" >> $logs_file' + "\n"
cmd += 'echo ">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>" >> $logs_file' + "\n"
cmd += 'echo "python3 $script_path/index.py clean >> $logs_file 2>&1"' + "\n"
cmd += 'cd $mw_dir && python3 $script_path/index.py clean >> $logs_file 2>&1' + "\n"
cmd += 'echo "【`date +"%Y-%m-%d %H:%M:%S"`】 END★" >> $logs_file' + "\n"
cmd += 'echo "<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<" >> $logs_file' + "\n"
params = {
'name': _name,
'type': _type_day,
'week': "",
'where1': _where1,
'hour': _hour,
'minute': _minute,
'save': "",
'backup_to': "",
'stype': "toShell",
'sname': '',
'sbody': cmd,
'urladdress': '',
}
task_id = api.add(params)
if task_id > 0:
cfg["task_id"] = task_id
cfg["name"] = name
_dd = getConfigData()
_dd.append(cfg)
mw.writeFile(getTaskConf(), json.dumps(_dd))
def removeBgTask():
cfg_list = getConfigData()
for x in range(len(cfg_list)):
cfg = cfg_list[x]
if "task_id" in cfg.keys() and cfg["task_id"] > 0:
res = mw.M("crontab").field("id, name").where(
"id=?", (cfg["task_id"],)).find()
if res and res["id"] == cfg["task_id"]:
import crontab_api
api = crontab_api.crontab_api()
data = api.delete(cfg["task_id"])
if data[0]:
cfg["task_id"] = -1
cfg_list[x] = cfg
mw.writeFile(getTaskConf(), '[]')
return True
return False
if __name__ == "__main__":
if len(sys.argv) > 1:
action = sys.argv[1]
if action == "remove":
removeBgTask()
elif action == "add":
createBgTask()

View File

@@ -1,45 +0,0 @@
#!/bin/bash
echo `date`
GITADDR="{$GITROOTURL}/{$USERNAME}/{$PROJECT}.git"
GIT_SDIR="{$CODE_DIR}"
GIT_USER_DIR="${GIT_SDIR}/{$USERNAME}"
GIT_PROJECT_DIR="${GIT_USER_DIR}/{$PROJECT}"
git config --global credential.helper store
git config --global pull.rebase false
# echo $GIT_PROJECT_DIR
if [ ! -d $GIT_PROJECT_DIR ];then
mkdir -p $GIT_USER_DIR && cd $GIT_USER_DIR
git clone $GITADDR
fi
unset GIT_DIR
cd $GIT_PROJECT_DIR && git pull
# func 2
# cd $GIT_PROJECT_DIR && env -i git pull origin master
#更新的目的地址
WEB_PATH={$WEB_ROOT}/{$USERNAME}/{$PROJECT}
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
fi
sysName=`uname`
if [ $sysName == 'Darwin' ]; then
USER=$(who | sed -n "2,1p" |awk '{print $1}')
chown -R $USER:staff $WEB_PATH
else
chown -R www:www $WEB_PATH
fi

View File

@@ -1,38 +0,0 @@
#!/bin/bash
echo `date`
GITADDR="{$GITROOTURL}/{$USERNAME}/{$PROJECT}"
GIT_SDIR="{$CODE_DIR}"
GIT_USER_DIR="${GIT_SDIR}/{$USERNAME}"
GIT_PROJECT_DIR="${GIT_USER_DIR}/{$PROJECT}"
# echo $GIT_PROJECT_DIR
if [ ! -d $GIT_PROJECT_DIR ];then
mkdir -p $GIT_USER_DIR && cd $GIT_USER_DIR
git clone $GITADDR
fi
unset GIT_DIR
git config pull.rebase false
cd $GIT_PROJECT_DIR && git pull
# func 2
# cd $GIT_PROJECT_DIR && env -i git pull origin master
WEB_PATH={$WEB_ROOT}/{$USERNAME}/{$PROJECT}
mkdir -p $WEB_PATH
rsync -vauP --delete --exclude=".*" $GIT_PROJECT_DIR/ $WEB_PATH
sysName=`uname`
if [ $sysName == 'Darwin' ]; then
USER=$(who | sed -n "2,1p" |awk '{print $1}')
chown -R $USER:staff $WEB_PATH
else
chown -R www:www $WEB_PATH
fi

View File

@@ -1,3 +0,0 @@
#!/bin/bash
sh -x {$PATH}/commit 2>{$PATH}/sh.log

Binary file not shown.

Before

Width:  |  Height:  |  Size: 26 KiB

View File

@@ -1,24 +0,0 @@
<div class="bt-form">
<div class="bt-w-main">
<div class="bt-w-menu">
<p class="bgw" onclick="pluginService('gitea');">服务</p>
<p onclick="pluginInitD('gitea');">自启动</p>
<!-- <p onclick="pluginConfig('gitea',null, 'init_conf');">启动配置</p> -->
<p onclick="gogsEdit();">手动编辑</p>
<p onclick="pluginConfig('gitea',null, 'conf');">配置文件</p>
<p onclick="gogsSetConfig();">配置修改</p>
<p onclick="giteaUserList();">用户列表</p>
<p onclick="pluginLogs('gitea',null,'run_log');">运行日志</p>
<p onclick="pluginLogs('gitea',null,'post_receive_log');" title="提交post-receive日志">提交日志</p>
<p onclick="giteaRead();">使用说明</p>
</div>
<div class="bt-w-con pd15">
<div class="soft-man-con"></div>
</div>
</div>
</div>
<script type="text/javascript">
$.getScript( "/plugins/file?name=gitea&f=js/gitea.js",function(){
pluginService('gitea');
});
</script>

View File

@@ -1,767 +0,0 @@
# coding: utf-8
import time
import os
import sys
import re
sys.path.append(os.getcwd() + "/class/core")
import mw
# cmd = 'ls /usr/local/lib/ | grep python | cut -d \\ -f 1 | awk \'END {print}\''
# info = mw.execShell(cmd)
# p = "/usr/local/lib/" + info[0].strip() + "/site-packages"
# sys.path.append(p)
app_debug = False
if mw.isAppleSystem():
app_debug = True
def getPluginName():
return 'gitea'
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 getArgs():
args = sys.argv[2:]
tmp = {}
args_len = len(args)
if args_len == 1:
t = args[0].strip('{').strip('}')
t = t.split(':', 1)
tmp[t[0]] = t[1]
elif args_len > 1:
for i in range(len(args)):
t = args[i].split(':', 1)
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 getInitdConfTpl():
path = getPluginDir() + "/init.d/gitea.tpl"
return path
def getInitdConf():
path = getServerDir() + "/init.d/gitea"
return path
def getConf():
path = getServerDir() + "/custom/conf/app.ini"
if not os.path.exists(path):
return mw.returnJson(False, "请先安装初始化!<br/>默认地址:http://" + mw.getLocalIp() + ":3000")
return path
def getConfTpl():
path = getPluginDir() + "/conf/app.ini"
return path
def status():
data = mw.execShell(
"ps -ef|grep " + getPluginName() + " |grep -v grep | grep -v python | awk '{print $2}'")
if data[0] == '':
return 'stop'
return 'start'
def getHomeDir():
if mw.isAppleSystem():
user = mw.execShell(
"who | sed -n '2, 1p' |awk '{print $1}'")[0].strip()
return '/Users/' + user
else:
return 'www'
def getRunUser():
if mw.isAppleSystem():
user = mw.execShell(
"who | sed -n '2, 1p' |awk '{print $1}'")[0].strip()
return user
else:
return 'www'
__SR = '''#!/bin/bash
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH
export USER=%s
export HOME=%s && ''' % ( getRunUser(), getHomeDir())
def contentReplace(content):
service_path = mw.getServerDir()
content = content.replace('{$ROOT_PATH}', mw.getRootDir())
content = content.replace('{$SERVER_PATH}', service_path)
content = content.replace('{$RUN_USER}', getRunUser())
content = content.replace('{$HOME_DIR}', getHomeDir())
return content
def initDreplace():
file_tpl = getInitdConfTpl()
service_path = mw.getServerDir()
git_dir = mw.getServerDir() + '/git'
if not os.path.exists(git_dir):
mw.execShell('mkdir -p ' + git_dir)
initD_path = getServerDir() + '/init.d'
if not os.path.exists(initD_path):
os.mkdir(initD_path)
file_bin = initD_path + '/' + getPluginName()
if not os.path.exists(file_bin):
content = mw.readFile(file_tpl)
content = contentReplace(content)
mw.writeFile(file_bin, content)
mw.execShell('chmod +x ' + file_bin)
# systemd
systemDir = mw.systemdCfgDir()
systemService = systemDir + '/gitea.service'
systemServiceTpl = getPluginDir() + '/init.d/gitea.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('{$SERVER_PATH}', service_path)
mw.writeFile(systemService, se_content)
mw.execShell('systemctl daemon-reload')
log_path = getServerDir() + '/log'
if not os.path.exists(log_path):
os.mkdir(log_path)
return file_bin
def getRootUrl():
content = mw.readFile(getConf())
rep = 'ROOT_URL\s*=\s*(.*)'
tmp = re.search(rep, content)
if tmp:
return tmp.groups()[0]
rep = 'EXTERNAL_URL\s*=\s*(.*)'
tmp = re.search(rep, content)
if tmp:
return tmp.groups()[0]
return ''
def getSshPort():
content = mw.readFile(getConf())
rep = 'SSH_PORT\s*=\s*(.*)'
tmp = re.search(rep, content)
if not tmp:
return ''
return tmp.groups()[0]
def getHttpPort():
content = mw.readFile(getConf())
rep = 'HTTP_PORT\s*=\s*(.*)'
tmp = re.search(rep, content)
if not tmp:
return ''
return tmp.groups()[0]
def getRootPath():
content = mw.readFile(getConf())
rep = 'ROOT\s*=\s*(.*)'
tmp = re.search(rep, content)
if not tmp:
return ''
return tmp.groups()[0]
def getDbConfValue():
conf = getConf()
if not os.path.exists(conf):
return {}
content = mw.readFile(conf)
rep_scope = "\[database\](.*?)\["
tmp = re.findall(rep_scope, content, re.S)
rep = '(\w*)\s*=\s*(.*)'
tmp = re.findall(rep, tmp[0])
r = {}
for x in range(len(tmp)):
k = tmp[x][0]
v = tmp[x][1]
r[k] = v
return r
def pMysqlDb(conf):
host = conf['HOST'].split(':')
# pymysql
db = mw.getMyORM()
# MySQLdb |
# db = mw.getMyORMDb()
db.setPort(int(host[1]))
db.setUser(conf['USER'])
if 'PASSWD' in conf:
db.setPwd(conf['PASSWD'])
else:
db.setPwd(conf['PASSWORD'])
db.setDbName(conf['NAME'])
# db.setSocket(getSocketFile())
db.setCharset("utf8")
return db
def pSqliteDb(conf):
# print(conf)
import db
psDb = db.Sql()
# 默认
gsdir = getServerDir() + '/data'
dbname = 'gitea'
if conf['PATH'][0] == '/':
# 绝对路径
pass
else:
path = conf['PATH'].split('/')
gsdir = getServerDir() + '/' + path[0]
dbname = path[1].split('.')[0]
# print(gsdir, dbname)
psDb.dbPos(gsdir, dbname)
return psDb
def getGiteaDbType(conf):
if 'DB_TYPE' in conf:
return conf['DB_TYPE']
if 'TYPE' in conf:
return conf['TYPE']
return 'NONE'
def pQuery(sql):
conf = getDbConfValue()
gtype = getGiteaDbType(conf)
if gtype == 'sqlite3':
db = pSqliteDb(conf)
data = db.query(sql, []).fetchall()
return data
elif gtype == 'mysql':
db = pMysqlDb(conf)
return db.query(sql)
print("仅支持mysql|sqlite3配置")
exit(0)
def isSqlError(mysqlMsg):
# 检测数据库执行错误
_mysqlMsg = str(mysqlMsg)
# print _mysqlMsg
if "MySQLdb" in _mysqlMsg:
return mw.returnData(False, 'MySQLdb组件缺失! <br>进入SSH命令行输入: pip install mysql-python')
if "2002," in _mysqlMsg:
return mw.returnData(False, '数据库连接失败,请检查数据库服务是否启动!')
if "using password:" in _mysqlMsg:
return mw.returnData(False, '数据库管理密码错误!')
if "Connection refused" in _mysqlMsg:
return mw.returnData(False, '数据库连接失败,请检查数据库服务是否启动!')
if "1133," in _mysqlMsg:
return mw.returnData(False, '数据库用户不存在!')
if "1007," in _mysqlMsg:
return mw.returnData(False, '数据库已经存在!')
if "1044," in _mysqlMsg:
return mw.returnData(False, mysqlMsg[1])
if "2003," in _mysqlMsg:
return mw.returnData(False, "Can't connect to MySQL server on '127.0.0.1' (61)")
return mw.returnData(True, 'OK')
def appOp(method):
file = initDreplace()
if not mw.isAppleSystem():
data = mw.execShell('systemctl ' + method + ' ' + getPluginName())
if data[1] == '':
return 'ok'
return 'fail'
data = mw.execShell(__SR + file + ' ' + method)
if data[1] == '':
return 'ok'
return data[0]
def start():
return appOp('start')
def stop():
return appOp('stop')
def restart():
return appOp('restart')
def reload():
return appOp('reload')
def initdStatus():
if mw.isAppleSystem():
return "Apple Computer does not support"
shell_cmd = 'systemctl status gitea | 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 gitea')
return 'ok'
def initdUinstall():
if mw.isAppleSystem():
return "Apple Computer does not support"
mw.execShell('systemctl disable gitea')
return 'ok'
def runLog():
log_path = getServerDir() + '/log/gitea.log'
return log_path
def postReceiveLog():
log_path = getServerDir() + '/log/hooks/post-receive.log'
return log_path
def getGogsConf():
conf = getConf()
if not os.path.exists(conf):
return mw.returnJson(False, "请先安装初始化!<br/>默认地址:http://" + mw.getLocalIp() + ":3000")
gets = [
{'name': 'DOMAIN', 'type': -1, 'ps': '服务器域名'},
{'name': 'ROOT_URL', 'type': -1, 'ps': '公开的完整URL路径'},
{'name': 'HTTP_ADDR', 'type': -1, 'ps': '应用HTTP监听地址'},
{'name': 'HTTP_PORT', 'type': -1, 'ps': '应用 HTTP 监听端口号'},
{'name': 'START_SSH_SERVER', 'type': 2, 'ps': '启动内置SSH服务器'},
{'name': 'SSH_PORT', 'type': -1, 'ps': 'SSH 端口号'},
{'name': 'REQUIRE_SIGNIN_VIEW', 'type': 2, 'ps': '强制登录浏览'},
{'name': 'ENABLE_CAPTCHA', 'type': 2, 'ps': '启用验证码服务'},
{'name': 'DISABLE_REGISTRATION', 'type': 2, 'ps': '禁止注册,只能由管理员创建帐号'},
{'name': 'ENABLE_NOTIFY_MAIL', 'type': 2, 'ps': '是否开启邮件通知'},
{'name': 'FORCE_PRIVATE', 'type': 2, 'ps': '强制要求所有新建的仓库都是私有'},
{'name': 'SHOW_FOOTER_BRANDING', 'type': 2, 'ps': 'Gogs推广信息'},
{'name': 'SHOW_FOOTER_VERSION', 'type': 2, 'ps': 'Gogs版本信息'},
{'name': 'SHOW_FOOTER_TEMPLATE_LOAD_TIME', 'type': 2, 'ps': 'Gogs模板加载时间'},
]
conf = mw.readFile(conf)
result = []
for g in gets:
rep = g['name'] + '\s*=\s*(.*)'
tmp = re.search(rep, conf)
if not tmp:
continue
g['value'] = tmp.groups()[0]
result.append(g)
return mw.returnJson(True, 'OK', result)
def submitGogsConf():
gets = ['DOMAIN',
'ROOT_URL',
'HTTP_ADDR',
'HTTP_PORT',
'START_SSH_SERVER',
'SSH_PORT',
'REQUIRE_SIGNIN_VIEW',
'FORCE_PRIVATE',
'ENABLE_CAPTCHA',
'DISABLE_REGISTRATION',
'ENABLE_NOTIFY_MAIL',
'SHOW_FOOTER_BRANDING',
'SHOW_FOOTER_VERSION',
'SHOW_FOOTER_TEMPLATE_LOAD_TIME']
args = getArgs()
filename = getConf()
conf = mw.readFile(filename)
for g in gets:
if g in args:
rep = g + '\s*=\s*(.*)'
val = g + ' = ' + args[g]
conf = re.sub(rep, val, conf)
mw.writeFile(filename, conf)
restart()
return mw.returnJson(True, '设置成功')
def userList():
conf = getConf()
if not os.path.exists(conf):
return mw.returnJson(False, "请先安装初始化!<br/>默认地址:http://" + mw.getLocalIp() + ":3000")
conf = getDbConfValue()
gtype = getGiteaDbType(conf)
if gtype != 'mysql':
return mw.returnJson(False, "仅支持mysql数据操作!")
import math
args = getArgs()
data = checkArgs(args, ['page', 'page_size'])
if not data[0]:
return data[1]
page = int(args['page'])
page_size = int(args['page_size'])
tojs = args['tojs']
search = ''
if 'search' in args:
search = args['search']
data = {}
data['root_url'] = getRootUrl()
start = (page - 1) * page_size
list_count = pQuery('select count(id) as num from user')
count = list_count[0]["num"]
list_data = pQuery(
'select id,name,email from user order by id desc limit ' + str(start) + ',' + str(page_size))
data['list'] = mw.getPage(
{'count': count, 'p': page, 'row': page_size, 'tojs': tojs})
data['page'] = page
data['page_size'] = page_size
data['page_count'] = int(math.ceil(count / page_size))
data['data'] = list_data
return mw.returnJson(True, 'OK', data)
def getAllUserProject(user, search=''):
path = getRootPath() + '/' + user
dlist = []
if os.path.exists(path):
for filename in os.listdir(path):
tmp = {}
filePath = path + '/' + filename
if os.path.isdir(filePath):
if search == '':
tmp['name'] = filename.replace('.git', '')
dlist.append(tmp)
else:
if filename.find(search) != -1:
tmp['name'] = filename.replace('.git', '')
dlist.append(tmp)
return dlist
def checkProjectListIsHasScript(user, data):
path = getRootPath() + '/' + user
for x in range(len(data)):
name = data[x]['name'] + '.git'
path_tmp = path + '/' + name + '/hooks/post-receive.d/post-receive'
if os.path.exists(path_tmp):
data[x]['has_hook'] = True
else:
data[x]['has_hook'] = False
return data
def userProjectList():
import math
args = getArgs()
# print args
page = 1
page_size = 5
search = ''
if not 'name' in args:
return mw.returnJson(False, '缺少参数name')
if 'page' in args:
page = int(args['page'])
if 'page_size' in args:
page_size = int(args['page_size'])
if 'search' in args:
search = args['search']
data = {}
ulist = getAllUserProject(args['name'])
dlist_sum = len(ulist)
start = (page - 1) * page_size
ret_data = ulist[start:start + page_size]
ret_data = checkProjectListIsHasScript(args['name'], ret_data)
data['root_url'] = getRootUrl()
data['data'] = ret_data
data['args'] = args
data['list'] = mw.getPage(
{'count': dlist_sum, 'p': page, 'row': page_size, 'tojs': 'userProjectList'})
return mw.returnJson(True, 'OK', data)
def projectScriptEdit():
args = getArgs()
if not 'user' in args:
return mw.returnJson(True, 'username missing')
if not 'name' in args:
return mw.returnJson(True, 'project name missing')
user = args['user']
name = args['name'] + '.git'
post_receive = getRootPath() + '/' + user + '/' + name + \
'/custom_hooks/commit'
if os.path.exists(post_receive):
return mw.returnJson(True, 'OK', {'path': post_receive})
else:
return mw.returnJson(False, 'file does not exist')
def projectScriptLoad():
args = getArgs()
if not 'user' in args:
return mw.returnJson(True, 'username missing')
if not 'name' in args:
return mw.returnJson(True, 'project name missing')
user = args['user']
name = args['name'] + '.git'
path = getRootPath() + '/' + user + '/' + name
post_receive_tpl = getPluginDir() + '/hook/post-receive.tpl'
post_receive = path + '/hooks/post-receive.d/post-receive'
if not os.path.exists(path + '/custom_hooks'):
mw.execShell('mkdir -p ' + path + '/custom_hooks')
mw.execShell('chown -R www:www ' + path + '/custom_hooks')
pct_content = mw.readFile(post_receive_tpl)
pct_content = pct_content.replace('{$PATH}', path + '/custom_hooks')
mw.writeFile(post_receive, pct_content)
mw.execShell('chmod 777 ' + post_receive)
mw.execShell('chown -R www:www ' + post_receive)
commit_tpl = getPluginDir() + '/hook/commit.tpl'
commit = path + '/custom_hooks/commit'
codeDir = mw.getRootDir() + '/git'
cc_content = mw.readFile(commit_tpl)
gitPath = getRootPath()
cc_content = cc_content.replace('{$GITROOTURL}', gitPath)
cc_content = cc_content.replace('{$CODE_DIR}', codeDir)
cc_content = cc_content.replace('{$USERNAME}', user)
cc_content = cc_content.replace('{$PROJECT}', args['name'])
cc_content = cc_content.replace('{$WEB_ROOT}', mw.getWwwDir())
mw.writeFile(commit, cc_content)
mw.execShell('chmod 777 ' + commit)
mw.execShell('chown -R www:www ' + commit)
return 'ok'
def projectScriptUnload():
args = getArgs()
data = checkArgs(args, ['user', 'name'])
if not data[0]:
return data[1]
user = args['user']
name = args['name'] + '.git'
post_receive = getRootPath() + '/' + user + '/' + name + \
'/hooks/post-receive.d/post-receive'
mw.execShell('rm -f ' + post_receive)
commit = getRootPath() + '/' + user + '/' + name + \
'/custom_hooks/commit'
mw.execShell('rm -f ' + commit)
return 'ok'
def projectScriptDebug():
args = getArgs()
data = checkArgs(args, ['user', 'name'])
if not data[0]:
return data[1]
user = args['user']
name = args['name'] + '.git'
commit_log = getRootPath() + '/' + user + '/' + name + \
'/custom_hooks/sh.log'
data = {}
if os.path.exists(commit_log):
data['status'] = True
data['path'] = commit_log
else:
data['status'] = False
data['msg'] = '没有日志文件'
return mw.getJson(data)
def gogsEdit():
data = {}
data['post_receive'] = getPluginDir() + '/hook/post-receive.tpl'
data['commit'] = getPluginDir() + '/hook/commit.tpl'
return mw.getJson(data)
def getRsaPublic():
path = getHomeDir()
path += '/.ssh/id_rsa.pub'
content = mw.readFile(path)
data = {}
data['mw'] = content
return mw.getJson(data)
def getTotalStatistics():
st = status()
data = {}
if st.strip() == 'start':
list_count = pQuery('select count(id) as num from repository')
count = list_count[0]["num"]
data['status'] = True
data['count'] = count
data['ver'] = mw.readFile(getServerDir() + '/version.pl').strip()
return mw.returnJson(True, 'ok', data)
data['status'] = False
data['count'] = 0
return mw.returnJson(False, 'fail', data)
def uninstallPreInspection():
repo_dir = getServerDir() + "/data/gitea-repositories"
if not os.path.exists(repo_dir):
return 'ok'
dir_list = os.listdir(repo_dir)
if len(dir_list) > 0:
return "有项目数据!请手动删除Gitea<br/> rm -rf {}".format(getServerDir())
return 'ok'
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 == 'uninstall_pre_inspection':
print(uninstallPreInspection())
elif func == 'run_log':
print(runLog())
elif func == 'post_receive_log':
print(postReceiveLog())
elif func == 'conf':
print(getConf())
elif func == 'init_conf':
print(getInitdConf())
elif func == 'get_gogs_conf':
print(getGogsConf())
elif func == 'submit_gogs_conf':
print(submitGogsConf())
elif func == 'user_list':
print(userList())
elif func == 'user_project_list':
print(userProjectList())
elif func == 'project_script_edit':
print(projectScriptEdit())
elif func == 'project_script_load':
print(projectScriptLoad())
elif func == 'project_script_unload':
print(projectScriptUnload())
elif func == 'project_script_debug':
print(projectScriptDebug())
elif func == 'gogs_edit':
print(gogsEdit())
elif func == 'get_rsa_public':
print(getRsaPublic())
elif func == 'get_total_statistics':
print(getTotalStatistics())
else:
print('fail')

View File

@@ -1,18 +0,0 @@
{
"ps": "Gitea 是一个开源社区驱动的轻量级代码托管解决方案。",
"name": "gitea",
"title": "Gitea",
"versions": ["1.17.2"],
"tip": "soft",
"install_pre_inspection":false,
"uninstall_pre_inspection":true,
"checks": "server/gitea",
"path":"server/gitea",
"author": "gitea",
"date": "2022-10-03",
"home": "https://dl.gitea.io/",
"type": "Git服务器",
"shell": "install.sh",
"pid": "3",
"sort": 7
}

View File

@@ -1,20 +0,0 @@
[Unit]
Description=Gitea (Git with a cup of tea)
After=syslog.target
After=network.target
[Service]
RestartSec=2s
Type=simple
User=www
Group=www
WorkingDirectory={$SERVER_PATH}/gitea
ExecStart={$SERVER_PATH}/gitea/gitea web
Restart=always
Environment=USER=www HOME=/home/www GITEA_WORK_DIR={$SERVER_PATH}/gitea
RemainAfterExit=yes
#AmbientCapabilities=CAP_NET_BIND_SERVICE
#CapabilityBoundingSet=CAP_NET_BIND_SERVICE
[Install]
WantedBy=multi-user.target

View File

@@ -1,110 +0,0 @@
#!/bin/sh
#
# /etc/rc.d/init.d/Gitea
#
# Runs the Gogs
#
#
# chkconfig: - 85 15
#
### BEGIN INIT INFO
# Provides: Gitea
# Required-Start: $remote_fs $syslog
# Required-Stop: $remote_fs $syslog
# Should-Start: mysql postgresql
# Should-Stop: mysql postgresql
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
# Short-Description: Start Gitea at boot time.
# Description: Control Gitea.
### END INIT INFO
# Source function library.
if [ -f /etc/init.d/functions ];then
. /etc/init.d/functions
fi
if [ -f /etc/rc.d/init.d/functions ];then
. /etc/rc.d/init.d/functions
fi
# Default values
export HOME={$HOME_DIR}
export USER={$RUN_USER}
NAME=gitea
GOGS_HOME={$SERVER_PATH}/gitea
GOGS_PATH=${GOGS_HOME}/$NAME
GOGS_USER={$RUN_USER}
SERVICENAME="gitea"
LOCKFILE=/tmp/gitea.lock
LOGPATH=${GOGS_HOME}/log
LOGFILE=${LOGPATH}/gitea.log
RETVAL=0
[ -r /etc/sysconfig/$NAME ] && . /etc/sysconfig/$NAME
DAEMON_OPTS="--check $NAME"
[ ! -z "$GOGS_USER" ] && DAEMON_OPTS="$DAEMON_OPTS --user=${GOGS_USER}"
status(){
isStart=`ps -ef|grep 'gitea web' |grep -v grep|awk '{print $2}'`
if [ "$isStart" == '' ];then
echo -e "${SERVICENAME} not running"
else
echo -e "${SERVICENAME}(pid $(echo $isStart)) already running"
fi
}
start() {
isStart=`ps -ef|grep 'gitea web' |grep -v grep|awk '{print $2}'`
if [ "$isStart" != '' ];then
echo "${SERVICENAME}(pid $(echo $isStart)) already running"
return $RETVAL
fi
cd ${GOGS_HOME}
echo -e "Starting ${SERVICENAME}: \c"
${GOGS_PATH} web > ${LOGFILE} 2>&1 &
RETVAL=$?
[ $RETVAL = 0 ] && touch ${LOCKFILE} && echo -e "\033[32mdone\033[0m"
return $RETVAL
}
stop() {
pids=`ps -ef|grep 'gitea web' |grep -v grep|awk '{print $2}'`
arr=($pids)
echo -e "Stopping gitea... \c"
for p in ${arr[@]}
do
kill -9 $p
done
echo -e "\033[32mdone\033[0m"
}
case "$1" in
start)
start
;;
stop)
stop
;;
status)
status
;;
restart)
stop
start
;;
reload)
stop
start
;;
*)
echo "Usage: ${NAME} {start|stop|status|restart}"
exit 1
;;
esac
exit $RETVAL

View File

@@ -1,128 +0,0 @@
#!/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
if [ -f ${rootPath}/bin/activate ];then
source ${rootPath}/bin/activate
fi
URL_DOWNLOAD=https://dl.gitea.io/
bash ${rootPath}/scripts/getos.sh
OSNAME=`cat ${rootPath}/data/osname.pl`
OSNAME_ID=`cat /etc/*-release | grep VERSION_ID | awk -F = '{print $2}' | awk -F "\"" '{print $2}'`
getBit(){
echo `getconf LONG_BIT`
}
Install_Rsync(){
if [ "$OSNAME" == "debian" ] || [ "$OSNAME" == "ubuntu" ];then
apt install -y rsync
elif [[ "$OSNAME" == "arch" ]]; then
echo y | pacman -Sy rsync
elif [[ "$OSNAME" == "macos" ]]; then
# brew install rsync
# brew install lsyncd
echo "ok"
else
yum install -y rsync
fi
}
Install_App()
{
Install_Rsync
mkdir -p $serverPath/source/gitea
if id www &> /dev/null ;then
echo "www uid is `id -u www`"
echo "www shell is `grep "^www:" /etc/passwd |cut -d':' -f7 `"
else
groupadd www
useradd -g www www
fi
if [ "macos" != "$OSNAME" ];then
if [ ! -d /home/www ];then
mkdir -p /home/www
chown -R www:www /home/www
fi
fi
echo '正在安装脚本文件...' > $install_tmp
version=$1
git config --global push.default simple
if [ "macos" == "$OSNAME" ];then
file=gitea-${version}-darwin-10.12-amd64
else
file=gitea-${version}-linux-amd64
fi
file_xz="${file}.xz"
echo "wget -O $serverPath/source/gitea/$file_xz ${URL_DOWNLOAD}/gitea/${version}/${file_xz}"
if [ ! -f $serverPath/source/gitea/$file_xz ];then
wget --no-check-certificate -O $serverPath/source/gitea/$file_xz ${URL_DOWNLOAD}/gitea/${version}/${file_xz}
fi
cd $serverPath/source/gitea && xz -k -d $file_xz
if [ -f $file ];then
mkdir -p $serverPath/gitea
mv $serverPath/source/gitea/$file $serverPath/gitea/gitea
chmod +x $serverPath/gitea/gitea
chown -R www:www $serverPath/gitea
fi
if [ -d $serverPath/gitea ];then
echo $version > $serverPath/gitea/version.pl
cd ${rootPath} && python3 plugins/gitea/index.py start
cd ${rootPath} && python3 plugins/gitea/index.py initd_install
fi
echo 'install success' > $install_tmp
}
Uninstall_App()
{
if [ -f /usr/lib/systemd/system/gitea.service ];then
systemctl stop gitea
systemctl disable gitea
rm -rf /usr/lib/systemd/system/gitea.service
systemctl daemon-reload
fi
if [ -f $serverPath/gitea/initd/gitea ];then
$serverPath/gitea/initd/gitea stop
fi
rm -rf $serverPath/gitea
echo 'uninstall success' > $install_tmp
}
action=$1
version=$2
if [ "${1}" == 'install' ];then
Install_App $version
else
Uninstall_App $version
fi

View File

@@ -1,369 +0,0 @@
function gogsPost(method,args,callback, title){
var _args = null;
if (typeof(args) == 'string'){
_args = JSON.stringify(toArrayObject(args));
} else {
_args = JSON.stringify(args);
}
var _title = '正在获取...';
if (typeof(title) != 'undefined'){
_title = title;
}
var loadT = layer.msg(_title, { icon: 16, time: 0, shade: 0.3 });
$.post('/plugins/run', {name:'gitea', func:method, args:_args}, function(data) {
layer.close(loadT);
if (!data.status){
layer.msg(data.msg,{icon:0,time:2000,shade: [0.3, '#000']});
return;
}
if(typeof(callback) == 'function'){
callback(data);
}
},'json');
}
function gogsSetConfig(){
gogsPost('get_gogs_conf', '', function(data){
var rrdata = $.parseJSON(data.data);
if (!rrdata.status){
layer.msg(rrdata.msg,{icon:0,time:2000,shade: [0.3, '#000']});
return;
}
var rdata = rrdata.data;
var mlist = '';
for (var i = 0; i < rdata.length; i++) {
var w = '140';
if (rdata[i].name == 'error_reporting') w = '250';
var ibody = '<input style="width: ' + w + 'px;" class="bt-input-text mr5" name="' + rdata[i].name + '" value="' + rdata[i].value + '" type="text" >';
switch (rdata[i].type) {
case 0:
var selected_1 = (rdata[i].value == 1) ? 'selected' : '';
var selected_0 = (rdata[i].value == 0) ? 'selected' : '';
ibody = '<select class="bt-input-text mr5" name="' + rdata[i].name + '" style="width: ' + w + 'px;">\
<option value="1" ' + selected_1 + '>开启</option>\
<option value="0" ' + selected_0 + '>关闭</option>\
</select>';
break;
case 1:
var selected_1 = (rdata[i].value == 'On') ? 'selected' : '';
var selected_0 = (rdata[i].value == 'Off') ? 'selected' : '';
ibody = '<select class="bt-input-text mr5" name="' + rdata[i].name + '" style="width: ' + w + 'px;">\
<option value="On" ' + selected_1 + '>开启</option>\
<option value="Off" ' + selected_0 + '>关闭</option></select>'
break;
case 2:
var selected_1 = (rdata[i].value == 'true') ? 'selected' : '';
var selected_0 = (rdata[i].value == 'false') ? 'selected' : '';
ibody = '<select class="bt-input-text mr5" name="' + rdata[i].name + '" style="width: ' + w + 'px;">\
<option value="true" ' + selected_1 + '>开启</option>\
<option value="false" ' + selected_0 + '>关闭</option></select>'
break;
}
mlist += '<p><span>' + rdata[i].name + '</span>' + ibody + ', <font>' + rdata[i].ps + '</font></p>'
}
var html = '<style>.conf_p p{margin-bottom: 2px}</style><div class="conf_p" style="margin-bottom:0">\
' + mlist + '\
<div style="margin-top:10px; padding-right:15px" class="text-right">\
<button class="btn btn-success btn-sm mr5" onclick="gogsSetConfig()">刷新</button>\
<button class="btn btn-success btn-sm" onclick="submitGogsConf()">保存</button></div>\
</div>';
$(".soft-man-con").html(html);
});
}
//提交PHP配置
function submitGogsConf() {
var data = {
DOMAIN: $("input[name='DOMAIN']").val(),
ROOT_URL: $("input[name='ROOT_URL']").val(),
HTTP_ADDR: $("select[name='HTTP_ADDR']").val(),
HTTP_PORT: $("input[name='HTTP_PORT']").val(),
START_SSH_SERVER: $("select[name='START_SSH_SERVER']").val() || 'false',
SSH_PORT: $("input[name='SSH_PORT']").val(),
REQUIRE_SIGNIN_VIEW: $("select[name='REQUIRE_SIGNIN_VIEW']").val() || 'false',
ENABLE_CAPTCHA: $("select[name='ENABLE_CAPTCHA']").val() || 'true',
DISABLE_REGISTRATION: $("select[name='DISABLE_REGISTRATION']").val() || 'false',
ENABLE_NOTIFY_MAIL: $("select[name='ENABLE_NOTIFY_MAIL']").val() || 'false',
FORCE_PRIVATE: $("select[name='FORCE_PRIVATE']").val() || 'false',
SHOW_FOOTER_BRANDING: $("select[name='SHOW_FOOTER_BRANDING']").val() || 'false',
SHOW_FOOTER_VERSION: $("select[name='SHOW_FOOTER_VERSION']").val() || 'false',
SHOW_FOOTER_TEMPLATE_LOAD_TIME: $("select[name='SHOW_FOOTER_TEMPLATE_LOAD_TIME']").val() || 'false',
};
gogsPost('submit_gogs_conf', data, function(ret_data){
var rdata = $.parseJSON(ret_data.data);
layer.msg(rdata.msg, { icon: rdata.status ? 1 : 2 });
gogsSetConfig();
});
}
function gogsEdit(){
gogsPost('gogs_edit',{} , function(data){
// console.log(data);
var rdata = $.parseJSON(data.data);
var edit = '<p class="status">通用的手动编辑:</p>';
edit +='<div class="sfm-opt">\
<button class="btn btn-default btn-sm" onclick="onlineEditFile(0,\''+rdata['post_receive']+'\');">post-receive.tpl</button>\
<button class="btn btn-default btn-sm" onclick="onlineEditFile(0,\''+rdata['commit']+'\');">commit.tpl</button>\
</div>';
$(".soft-man-con").html(edit);
});
}
function giteaUserList(page, search) {
var _data = {};
if (typeof(page) =='undefined'){
var page = 1;
}
_data['page'] = page;
_data['page_size'] = 10;
if(typeof(search) != 'undefined'){
_data['search'] = search;
}
_data['tojs'] = 'giteaUserList';
gogsPost('user_list', _data, function(data){
var rdata = $.parseJSON(data.data);
if (!rdata.status){
layer.msg(rdata.msg,{icon:0,time:2000,shade: [0.3, '#000']});
return;
}
content = '<div class="finduser"><input class="bt-input-text mr5 outline_no" type="text" placeholder="查找用户名" id="find_user" style="height: 28px; border-radius: 3px;width: 435px;">';
content += '<button class="btn btn-success btn-sm" onclick="userFind();">查找</button></div>';
content += '<div class="divtable" style="margin-top:5px;"><table class="table table-hover" width="100%" cellspacing="0" cellpadding="0" border="0">';
content += '<thead><tr>';
content += '<th>序号</th>';
content += '<th>用户或组织</th>';
content += '<th>邮件地址</th>';
content += '<th>操作(<a href="'+rdata['data']['root_url']+'" class="btlink" target="_blank">WEB管理</a>)</th>';
content += '</tr></thead>';
content += '<tbody>';
ulist = rdata['data']['data'];
for (i in ulist){
email = ulist[i]["email"] == '' ? '无' : ulist[i]["email"];
content += '<tr><td>'+ulist[i]["id"]+'</td>'+
'<td>'+ulist[i]["name"]+'</td>'+
'<td>'+email+'</td>'+
'<td><a class="btlink" onclick="userProjectList(\''+ulist[i]["name"]+'\')">项目管理</a></td>'+
'</tr>';
}
content += '</tbody>';
content += '</table></div>';
var page = '<div class="dataTables_paginate paging_bootstrap pagination" style="margin-top:0px;"><ul id="softPage" class="page"><div>';
page += rdata['data']['list'];
page += '</div></ul></div>';
content += page;
$(".soft-man-con").html(content);
});
}
function userProjectList(user, search){
var loadOpen = layer.open({
type: 1,
title: '用户('+user+')项目列表',
area: '500px',
content:"<div class='bt-form pd20 c6'>\
<div>\
<div id='gitea_table' class='divtable' style='margin-top:5px;'>\
<table class='table table-hover'>\
<thead><tr><th>项目</th><th>操作</th></tr></thead>\
<tbody></tbody>\
</table>\
<div class='dataTables_paginate paging_bootstrap pagination' style='margin-top:0px;'><ul id='gitea_page' class='page'></ul></div>\
</div>\
</div>\
</div>",
success:function(){
userProjectListPost(user,search);
}
});
}
function userProjectListPost(user, search){
var req = {};
if (!isNaN(user)){
req['page'] = user;
req['name'] = user = getCookie('gogsUserSelected');
} else {
req['page'] = 1;
req['name'] = user;
setCookie('gogsUserSelected', user);
}
req['page_size'] = 5;
req['search'] = '';
if(typeof(search) != 'undefined'){
req['search'] = search;
}
gogsPost('user_project_list', req, function(data){
var rdata = [];
try {
rdata = $.parseJSON(data.data);
} catch(e){}
if (!rdata['status']){
layer.msg(rdata['msg'], { icon: 2 });
return;
}
var list = '';
// console.log(rdata);
var project_list = rdata['data']['data'];
for (i in project_list) {
var name = project_list[i]['name'];
list += '<tr><td>'+name+'</td>\
<td>\
<a class="btlink" target="_blank" href="'+rdata['data']['root_url']+user+'/'+name+'">源码</a> | \
<a class="btlink" onclick="projectScript(\''+user+'\',\''+name+'\','+project_list[i]['has_hook']+');">脚本</a>\
</td>\
</tr>';
}
$('#gitea_table tbody').html(list);
var page = rdata['data']['list'];
$('#gitea_table .gitea_page').html(page);
});
}
function projectScript(user, name,has_hook){
// console.log(user,name,has_hook);
var html = '';
if (has_hook){
html += '<button class="btn btn-default btn-sm hook_edit">手动编辑</button>';
html += '<button class="btn btn-default btn-sm hook_log">调试日志</button>';
html += '<button class="btn btn-default btn-sm hook_load">重新加载</button>';
html += '<button class="btn btn-default btn-sm hook_unload">卸载脚本</button>';
} else {
html += '<button class="btn btn-default btn-sm hook_load">加载脚本</button>';
}
var loadOpen = layer.open({
type: 1,
title: '['+user+']['+name+']脚本设置',
area: '240px',
content:'<div class="change-default pd20">'+html+'</div>',
success:function(layero,index) {
$('.hook_edit').click(function(){
projectScriptEdit(user,name,index);
});
$('.hook_log').click(function(){
projectScriptDebug(user,name,index);
});
$('.hook_load').click(function(){
projectScriptLoad(user,name,index);
});
$('.hook_unload').click(function(){
projectScriptUnload(user,name,index);
});
}
});
}
function projectScriptEdit(user,name,index){
gogsPost('project_script_edit', {'user':user,'name':name}, function(data){
var rdata = $.parseJSON(data.data);
if (rdata['status']){
onlineEditFile(0, rdata['data']['path']);
} else {
layer.msg(rdata.msg,{icon:1,time:2000,shade: [0.3, '#000']});
}
});
}
function projectScriptLoad(user,name,index){
gogsPost('project_script_load', {'user':user,'name':name}, function(data){
if (data.data != 'ok'){
layer.msg(data.data,{icon:0,time:2000,shade: [0.3, '#000']});
return;
}
showMsg('加载成功!',function(){
layer.close(index);
userProjectListPost(1);
},{icon:1,time:2000,shade: [0.3, '#000']},2000);
});
}
function projectScriptUnload(user,name,index){
gogsPost('project_script_unload', {'user':user,'name':name}, function(data){
if (data.data != 'ok'){
layer.msg(data.data,{icon:0,time:2000,shade: [0.3, '#000']});
return;
}
showMsg('卸载成功!',function(){
layer.close(index);
userProjectListPost(1);
},{icon:1,time:2000,shade: [0.3, '#000']},2000);
});
}
function projectScriptDebug(user,name,index){
gogsPost('project_script_debug', {'user':user,'name':name}, function(data){
var rdata = $.parseJSON(data.data);
if (rdata['status']){
onlineEditFile(0, rdata['path']);
} else {
showMsg(rdata.msg,function(){
},{icon:1,time:2000,shade: [0.3, '#000']},2000);
}
});
}
function getRsaPublic(){
gogsPost('get_rsa_public', {}, function(data){
var rdata = $.parseJSON(data.data);
var con = '<div class="tab-con">\
<div class="myKeyCon ptb15">\
<textarea style="margin:0px;width:580px;height:110px;outline:none;" spellcheck="false">'+rdata.mw+'</textarea>\
</div>\
<ul class="help-info-text c7 pull-left"></ul>\
</div>'
layer.open({
type: 1,
area: "600px",
title: '本机公钥',
closeBtn: 2,
shift: 5,
shadeClose: false,
content:con
});
});
}
function giteaRead(){
var readme = '<ul class="help-info-text c7">';
readme += '<li>默认使用MySQL,第一个启动加载各种配置,并修改成正确的数据库配置</li>';
readme += '<li>邮件端口使用456,gogs仅支持使用STARTTLS的SMTP协议</li>';
readme += '<li>如果使用项目中脚本本地同步,<a target="_blank" href="https://github.com/midoks/mdserver-web/wiki/%E6%8F%92%E4%BB%B6%E7%AE%A1%E7%90%86%5Bgogs%E4%BD%BF%E7%94%A8%E8%AF%B4%E6%98%8E%5D#%E5%90%AF%E5%8A%A8gogs%E5%90%8E%E5%A6%82%E6%9E%9C%E8%A6%81%E4%BD%BF%E7%94%A8hook%E8%84%9A%E6%9C%AC%E5%90%8C%E6%AD%A5%E4%BB%A3%E7%A0%81%E9%9C%80%E8%A6%81%E5%BC%80%E5%90%AFssh%E7%AB%AF%E5%8F%A3">点击查看</></li>';
readme += '<li><a href="#" onclick="getRsaPublic();">点击查看本机公钥</></li>';
readme += '</ul>';
$('.soft-man-con').html(readme);
}

View File

@@ -1,63 +0,0 @@
APP_NAME = Gogs
RUN_USER = {$RUN_USER}
RUN_MODE = prod
[database]
DB_TYPE = mysql
HOST = 127.0.0.1:3306
NAME = gogs
USER = gogs
PASSWD = gogs
SSL_MODE = disable
PATH = data/gogs.db
[repository]
ROOT = {$ROOT_PATH}/gogs-repositories
FORCE_PRIVATE = true
[server]
DOMAIN = 127.0.0.1
HTTP_PORT = 3000
ROOT_URL = http://127.0.0.1:3000/
DISABLE_SSH = true
SSH_PORT = 2000
START_SSH_SERVER = false
OFFLINE_MODE = false
LANDING_PAGE = home
[mailer]
ENABLED = false
HOST = smtp.163.com:465
USER = midoks@163.com
PASSWD = ***
USE_PLAIN_TEXT = true
[service]
REGISTER_EMAIL_CONFIRM = false
ENABLE_NOTIFY_MAIL = false
DISABLE_REGISTRATION = true
ENABLE_CAPTCHA = true
REQUIRE_SIGNIN_VIEW = true
[picture]
DISABLE_GRAVATAR = false
ENABLE_FEDERATED_AVATAR = false
[session]
PROVIDER = file
[log]
MODE = console, file
LEVEL = Info
ROOT_PATH = {$SERVER_PATH}/gogs/log
[security]
INSTALL_LOCK = true
SECRET_KEY = jmGSJXDBH5Ng4wt
[other]
SHOW_FOOTER_BRANDING = false
SHOW_FOOTER_VERSION = false
SHOW_FOOTER_TEMPLATE_LOAD_TIME = false

View File

@@ -1,39 +0,0 @@
#!/bin/bash
echo `date`
GITADDR="{$GITROOTURL}/{$USERNAME}/{$PROJECT}.git"
GIT_SDIR="{$CODE_DIR}"
GIT_USER_DIR="${GIT_SDIR}/{$USERNAME}"
GIT_PROJECT_DIR="${GIT_USER_DIR}/{$PROJECT}"
git config --global credential.helper store
# echo $GIT_PROJECT_DIR
if [ ! -d $GIT_PROJECT_DIR ];then
mkdir -p $GIT_USER_DIR && cd $GIT_USER_DIR
git clone $GITADDR
fi
unset GIT_DIR
cd $GIT_PROJECT_DIR && git pull
# func 2
# cd $GIT_PROJECT_DIR && env -i git pull origin master
#更新的目的地址
WEB_PATH={$WEB_ROOT}/{$USERNAME}/{$PROJECT}
mkdir -p $WEB_PATH
rsync -vauP --delete --exclude=".*" $GIT_PROJECT_DIR/ $WEB_PATH
sysName=`uname`
if [ $sysName == 'Darwin' ]; then
USER=$(who | sed -n "2,1p" |awk '{print $1}')
chown -R $USER:staff $WEB_PATH
else
chown -R www:www $WEB_PATH
fi

View File

@@ -1,36 +0,0 @@
#!/bin/bash
echo `date`
GITADDR="{$GITROOTURL}/{$USERNAME}/{$PROJECT}"
GIT_SDIR="{$CODE_DIR}"
GIT_USER_DIR="${GIT_SDIR}/{$USERNAME}"
GIT_PROJECT_DIR="${GIT_USER_DIR}/{$PROJECT}"
# echo $GIT_PROJECT_DIR
if [ ! -d $GIT_PROJECT_DIR ];then
mkdir -p $GIT_USER_DIR && cd $GIT_USER_DIR
git clone $GITADDR
fi
unset GIT_DIR
cd $GIT_PROJECT_DIR && git pull
# func 2
# cd $GIT_PROJECT_DIR && env -i git pull origin master
WEB_PATH={$WEB_ROOT}/{$USERNAME}/{$PROJECT}
mkdir -p $WEB_PATH
rsync -vauP --delete --exclude=".*" $GIT_PROJECT_DIR/ $WEB_PATH
sysName=`uname`
if [ $sysName == 'Darwin' ]; then
USER=$(who | sed -n "2,1p" |awk '{print $1}')
chown -R $USER:staff $WEB_PATH
else
chown -R www:www $WEB_PATH
fi

View File

@@ -1,3 +0,0 @@
#!/bin/bash
sh -x {$PATH}/commit 2>{$PATH}/sh.log

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

View File

@@ -1,23 +0,0 @@
<div class="bt-form">
<div class="bt-w-main">
<div class="bt-w-menu">
<p class="bgw" onclick="pluginService('gogs');">服务</p>
<p onclick="pluginInitD('gogs');">自启动</p>
<!-- <p onclick="pluginConfig('gogs',null, 'init_conf');">启动配置</p> -->
<p onclick="gogsEdit();">手动编辑</p>
<p onclick="pluginConfig('gogs',null, 'conf');">配置文件</p>
<p onclick="gogsSetConfig();">配置修改</p>
<p onclick="gogsUserList();">用户列表</p>
<p onclick="pluginLogs('gogs',null,'run_log');">运行日志</p>
<p onclick="pluginLogs('gogs',null,'post_receive_log');" title="提交post-receive日志">提交日志</p>
<p onclick="gogsRead();">使用说明</p>
</div>
<div class="bt-w-con pd15">
<div class="soft-man-con"></div>
</div>
</div>
</div>
<script type="text/javascript">
$.getScript( "/plugins/file?name=gogs&f=js/gogs.js");
pluginService('gogs');
</script>

View File

@@ -1,754 +0,0 @@
# coding: utf-8
import time
import os
import sys
import re
sys.path.append(os.getcwd() + "/class/core")
import mw
# cmd = 'ls /usr/local/lib/ | grep python | cut -d \\ -f 1 | awk \'END {print}\''
# info = mw.execShell(cmd)
# p = "/usr/local/lib/" + info[0].strip() + "/site-packages"
# sys.path.append(p)
import psutil
app_debug = False
if mw.isAppleSystem():
app_debug = True
def getPluginName():
return 'gogs'
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 getArgs():
args = sys.argv[2:]
tmp = {}
args_len = len(args)
if args_len == 1:
t = args[0].strip('{').strip('}')
t = t.split(':', 1)
tmp[t[0]] = t[1]
elif args_len > 1:
for i in range(len(args)):
t = args[i].split(':', 1)
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 getInitdConfTpl():
path = getPluginDir() + "/init.d/gogs.tpl"
return path
def getInitdConf():
path = getServerDir() + "/init.d/gogs"
return path
def getConf():
path = getServerDir() + "/custom/conf/app.ini"
return path
def getConfTpl():
path = getPluginDir() + "/conf/app.ini"
return path
def status():
data = mw.execShell(
"ps -ef|grep " + getPluginName() + " |grep -v grep | grep -v python | awk '{print $2}'")
if data[0] == '':
return 'stop'
return 'start'
def getHomeDir():
if mw.isAppleSystem():
user = mw.execShell(
"who | sed -n '2, 1p' |awk '{print $1}'")[0].strip()
return '/Users/' + user
else:
return '/root'
def getRunUser():
if mw.isAppleSystem():
user = mw.execShell(
"who | sed -n '2, 1p' |awk '{print $1}'")[0].strip()
return user
else:
return 'root'
__SR = '''#!/bin/bash
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH
export USER=%s
export HOME=%s && ''' % ( getRunUser(), getHomeDir())
def contentReplace(content):
service_path = mw.getServerDir()
content = content.replace('{$ROOT_PATH}', mw.getRootDir())
content = content.replace('{$SERVER_PATH}', service_path)
content = content.replace('{$RUN_USER}', getRunUser())
content = content.replace('{$HOME_DIR}', getHomeDir())
return content
def initDreplace():
file_tpl = getInitdConfTpl()
service_path = mw.getServerDir()
initD_path = getServerDir() + '/init.d'
if not os.path.exists(initD_path):
os.mkdir(initD_path)
file_bin = initD_path + '/' + getPluginName()
if not os.path.exists(file_bin):
content = mw.readFile(file_tpl)
content = contentReplace(content)
mw.writeFile(file_bin, content)
mw.execShell('chmod +x ' + file_bin)
# conf_bin = getConf()
# if not os.path.exists(conf_bin):
# mw.execShell('mkdir -p ' + getServerDir() + '/custom/conf')
# conf_tpl = getConfTpl()
# content = mw.readFile(conf_tpl)
# content = contentReplace(content)
# mw.writeFile(conf_bin, content)
# systemd
systemDir = mw.systemdCfgDir()
systemService = systemDir + '/gogs.service'
systemServiceTpl = getPluginDir() + '/init.d/gogs.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('{$SERVER_PATH}', service_path)
mw.writeFile(systemService, se_content)
mw.execShell('systemctl daemon-reload')
log_path = getServerDir() + '/log'
if not os.path.exists(log_path):
os.mkdir(log_path)
return file_bin
def getRootUrl():
content = mw.readFile(getConf())
rep = 'ROOT_URL\s*=\s*(.*)'
tmp = re.search(rep, content)
if tmp:
return tmp.groups()[0]
rep = 'EXTERNAL_URL\s*=\s*(.*)'
tmp = re.search(rep, content)
if tmp:
return tmp.groups()[0]
return ''
def getSshPort():
content = mw.readFile(getConf())
rep = 'SSH_PORT\s*=\s*(.*)'
tmp = re.search(rep, content)
if not tmp:
return ''
return tmp.groups()[0]
def getHttpPort():
content = mw.readFile(getConf())
rep = 'HTTP_PORT\s*=\s*(.*)'
tmp = re.search(rep, content)
if not tmp:
return ''
return tmp.groups()[0]
def getRootPath():
content = mw.readFile(getConf())
rep = 'ROOT\s*=\s*(.*)'
tmp = re.search(rep, content)
if not tmp:
return ''
return tmp.groups()[0]
def getDbConfValue():
conf = getConf()
if not os.path.exists(conf):
return {}
content = mw.readFile(conf)
rep_scope = "\[database\](.*?)\["
tmp = re.findall(rep_scope, content, re.S)
rep = '(\w*)\s*=\s*(.*)'
tmp = re.findall(rep, tmp[0])
r = {}
for x in range(len(tmp)):
k = tmp[x][0]
v = tmp[x][1]
r[k] = v
return r
def pMysqlDb(conf):
host = conf['HOST'].split(':')
# pymysql
db = mw.getMyORM()
# MySQLdb |
# db = mw.getMyORMDb()
db.setPort(int(host[1]))
db.setUser(conf['USER'])
if 'PASSWD' in conf:
db.setPwd(conf['PASSWD'])
else:
db.setPwd(conf['PASSWORD'])
db.setDbName(conf['NAME'])
# db.setSocket(getSocketFile())
db.setCharset("utf8")
return db
def pSqliteDb(conf):
# print(conf)
import db
psDb = db.Sql()
# 默认
gsdir = getServerDir() + '/data'
dbname = 'gogs'
if conf['PATH'][0] == '/':
# 绝对路径
pass
else:
path = conf['PATH'].split('/')
gsdir = getServerDir() + '/' + path[0]
dbname = path[1].split('.')[0]
# print(gsdir, dbname)
psDb.dbPos(gsdir, dbname)
return psDb
def getGogsDbType(conf):
if 'DB_TYPE' in conf:
return conf['DB_TYPE']
if 'TYPE' in conf:
return conf['TYPE']
return 'NONE'
def pQuery(sql):
conf = getDbConfValue()
gtype = getGogsDbType(conf)
if gtype == 'sqlite3':
db = pSqliteDb(conf)
data = db.query(sql, []).fetchall()
return data
elif gtype == 'mysql':
db = pMysqlDb(conf)
return db.query(sql)
print("仅支持mysql|sqlite3配置")
exit(0)
def isSqlError(mysqlMsg):
# 检测数据库执行错误
_mysqlMsg = str(mysqlMsg)
# print _mysqlMsg
if "MySQLdb" in _mysqlMsg:
return mw.returnData(False, 'MySQLdb组件缺失! <br>进入SSH命令行输入: pip install mysql-python')
if "2002," in _mysqlMsg:
return mw.returnData(False, '数据库连接失败,请检查数据库服务是否启动!')
if "using password:" in _mysqlMsg:
return mw.returnData(False, '数据库管理密码错误!')
if "Connection refused" in _mysqlMsg:
return mw.returnData(False, '数据库连接失败,请检查数据库服务是否启动!')
if "1133," in _mysqlMsg:
return mw.returnData(False, '数据库用户不存在!')
if "1007," in _mysqlMsg:
return mw.returnData(False, '数据库已经存在!')
if "1044," in _mysqlMsg:
return mw.returnData(False, mysqlMsg[1])
if "2003," in _mysqlMsg:
return mw.returnData(False, "Can't connect to MySQL server on '127.0.0.1' (61)")
return mw.returnData(True, 'OK')
def gogsOp(method):
file = initDreplace()
if not mw.isAppleSystem():
data = mw.execShell('systemctl ' + method + ' gogs')
if data[1] == '':
return 'ok'
return 'fail'
data = mw.execShell(__SR + file + ' ' + method)
if data[1] == '':
return 'ok'
return data[0]
def start():
return gogsOp('start')
def stop():
return gogsOp('stop')
def restart():
return gogsOp('restart')
def reload():
return gogsOp('reload')
def initdStatus():
if mw.isAppleSystem():
return "Apple Computer does not support"
shell_cmd = 'systemctl status gogs | 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 gogs')
return 'ok'
def initdUinstall():
if mw.isAppleSystem():
return "Apple Computer does not support"
mw.execShell('systemctl disable gogs')
return 'ok'
def runLog():
log_path = getServerDir() + '/log/gogs.log'
return log_path
def postReceiveLog():
log_path = getServerDir() + '/log/hooks/post-receive.log'
return log_path
def getGogsConf():
conf = getConf()
if not os.path.exists(conf):
return mw.returnJson(False, "请先安装初始化!<br/>默认地址:http://" + mw.getLocalIp() + ":3000")
gets = [
{'name': 'DOMAIN', 'type': -1, 'ps': '服务器域名'},
{'name': 'ROOT_URL', 'type': -1, 'ps': '公开的完整URL路径'},
{'name': 'HTTP_ADDR', 'type': -1, 'ps': '应用HTTP监听地址'},
{'name': 'HTTP_PORT', 'type': -1, 'ps': '应用 HTTP 监听端口号'},
{'name': 'START_SSH_SERVER', 'type': 2, 'ps': '启动内置SSH服务器'},
{'name': 'SSH_PORT', 'type': -1, 'ps': 'SSH 端口号'},
{'name': 'REQUIRE_SIGNIN_VIEW', 'type': 2, 'ps': '强制登录浏览'},
{'name': 'ENABLE_CAPTCHA', 'type': 2, 'ps': '启用验证码服务'},
{'name': 'DISABLE_REGISTRATION', 'type': 2, 'ps': '禁止注册,只能由管理员创建帐号'},
{'name': 'ENABLE_NOTIFY_MAIL', 'type': 2, 'ps': '是否开启邮件通知'},
{'name': 'FORCE_PRIVATE', 'type': 2, 'ps': '强制要求所有新建的仓库都是私有'},
{'name': 'SHOW_FOOTER_BRANDING', 'type': 2, 'ps': 'Gogs推广信息'},
{'name': 'SHOW_FOOTER_VERSION', 'type': 2, 'ps': 'Gogs版本信息'},
{'name': 'SHOW_FOOTER_TEMPLATE_LOAD_TIME', 'type': 2, 'ps': 'Gogs模板加载时间'},
]
conf = mw.readFile(conf)
result = []
for g in gets:
rep = g['name'] + '\s*=\s*(.*)'
tmp = re.search(rep, conf)
if not tmp:
continue
g['value'] = tmp.groups()[0]
result.append(g)
return mw.returnJson(True, 'OK', result)
def submitGogsConf():
gets = ['DOMAIN',
'ROOT_URL',
'HTTP_ADDR',
'HTTP_PORT',
'START_SSH_SERVER',
'SSH_PORT',
'REQUIRE_SIGNIN_VIEW',
'FORCE_PRIVATE',
'ENABLE_CAPTCHA',
'DISABLE_REGISTRATION',
'ENABLE_NOTIFY_MAIL',
'SHOW_FOOTER_BRANDING',
'SHOW_FOOTER_VERSION',
'SHOW_FOOTER_TEMPLATE_LOAD_TIME']
args = getArgs()
filename = getConf()
conf = mw.readFile(filename)
for g in gets:
if g in args:
rep = g + '\s*=\s*(.*)'
val = g + ' = ' + args[g]
conf = re.sub(rep, val, conf)
mw.writeFile(filename, conf)
reload()
return mw.returnJson(True, '设置成功')
def userList():
conf = getConf()
if not os.path.exists(conf):
return mw.returnJson(False, "请先安装初始化!<br/>默认地址:http://" + mw.getLocalIp() + ":3000")
conf = getDbConfValue()
gtype = getGogsDbType(conf)
if gtype != 'mysql':
return mw.returnJson(False, "仅支持mysql数据操作!")
import math
args = getArgs()
data = checkArgs(args, ['page', 'page_size'])
if not data[0]:
return data[1]
page = int(args['page'])
page_size = int(args['page_size'])
search = ''
if 'search' in args:
search = args['search']
data = {}
data['root_url'] = getRootUrl()
start = (page - 1) * page_size
list_count = pQuery('select count(id) as num from user')
count = list_count[0]["num"]
list_data = pQuery(
'select id,name,email from user order by id desc limit ' + str(start) + ',' + str(page_size))
data['list'] = mw.getPage({'count': count, 'p': page,
'row': page_size, 'tojs': 'gogsUserList'})
data['page'] = page
data['page_size'] = page_size
data['page_count'] = int(math.ceil(count / page_size))
data['data'] = list_data
return mw.returnJson(True, 'OK', data)
def getAllUserProject(user, search=''):
path = getRootPath() + '/' + user
dlist = []
if os.path.exists(path):
for filename in os.listdir(path):
tmp = {}
filePath = path + '/' + filename
if os.path.isdir(filePath):
if search == '':
tmp['name'] = filename.replace('.git', '')
dlist.append(tmp)
else:
if filename.find(search) != -1:
tmp['name'] = filename.replace('.git', '')
dlist.append(tmp)
return dlist
def checkProjectListIsHasScript(user, data):
path = getRootPath() + '/' + user
for x in range(len(data)):
name = data[x]['name'] + '.git'
path_tmp = path + '/' + name + '/custom_hooks/post-receive'
if os.path.exists(path_tmp):
data[x]['has_hook'] = True
else:
data[x]['has_hook'] = False
return data
def userProjectList():
import math
args = getArgs()
# print args
page = 1
page_size = 5
search = ''
if not 'name' in args:
return mw.returnJson(False, '缺少参数name')
if 'page' in args:
page = int(args['page'])
if 'page_size' in args:
page_size = int(args['page_size'])
if 'search' in args:
search = args['search']
data = {}
ulist = getAllUserProject(args['name'])
dlist_sum = len(ulist)
start = (page - 1) * page_size
ret_data = ulist[start:start + page_size]
ret_data = checkProjectListIsHasScript(args['name'], ret_data)
data['root_url'] = getRootUrl()
data['data'] = ret_data
data['args'] = args
data['list'] = mw.getPage(
{'count': dlist_sum, 'p': page, 'row': page_size, 'tojs': 'userProjectList'})
return mw.returnJson(True, 'OK', data)
def projectScriptEdit():
args = getArgs()
if not 'user' in args:
return mw.returnJson(True, 'username missing')
if not 'name' in args:
return mw.returnJson(True, 'project name missing')
user = args['user']
name = args['name'] + '.git'
post_receive = getRootPath() + '/' + user + '/' + name + \
'/custom_hooks/commit'
if os.path.exists(post_receive):
return mw.returnJson(True, 'OK', {'path': post_receive})
else:
return mw.returnJson(False, 'file does not exist')
def projectScriptLoad():
args = getArgs()
if not 'user' in args:
return mw.returnJson(True, 'username missing')
if not 'name' in args:
return mw.returnJson(True, 'project name missing')
user = args['user']
name = args['name'] + '.git'
path = getRootPath() + '/' + user + '/' + name
post_receive_tpl = getPluginDir() + '/hook/post-receive.tpl'
post_receive = path + '/custom_hooks/post-receive'
if not os.path.exists(path + '/custom_hooks'):
mw.execShell('mkdir -p ' + path + '/custom_hooks')
pct_content = mw.readFile(post_receive_tpl)
pct_content = pct_content.replace('{$PATH}', path + '/custom_hooks')
mw.writeFile(post_receive, pct_content)
mw.execShell('chmod 777 ' + post_receive)
commit_tpl = getPluginDir() + '/hook/commit.tpl'
commit = path + '/custom_hooks/commit'
codeDir = mw.getRootDir() + '/git'
cc_content = mw.readFile(commit_tpl)
gitPath = getRootPath()
cc_content = cc_content.replace('{$GITROOTURL}', gitPath)
cc_content = cc_content.replace('{$CODE_DIR}', codeDir)
cc_content = cc_content.replace('{$USERNAME}', user)
cc_content = cc_content.replace('{$PROJECT}', args['name'])
cc_content = cc_content.replace('{$WEB_ROOT}', mw.getWwwDir())
mw.writeFile(commit, cc_content)
mw.execShell('chmod 777 ' + commit)
return 'ok'
def projectScriptUnload():
args = getArgs()
data = checkArgs(args, ['user', 'name'])
if not data[0]:
return data[1]
user = args['user']
name = args['name'] + '.git'
post_receive = getRootPath() + '/' + user + '/' + name + \
'/custom_hooks/post-receive'
mw.execShell('rm -f ' + post_receive)
commit = getRootPath() + '/' + user + '/' + name + \
'/custom_hooks/commit'
mw.execShell('rm -f ' + commit)
return 'ok'
def projectScriptDebug():
args = getArgs()
data = checkArgs(args, ['user', 'name'])
if not data[0]:
return data[1]
user = args['user']
name = args['name'] + '.git'
commit_log = getRootPath() + '/' + user + '/' + name + \
'/custom_hooks/sh.log'
data = {}
if os.path.exists(commit_log):
data['status'] = True
data['path'] = commit_log
else:
data['status'] = False
data['msg'] = '没有日志文件'
return mw.getJson(data)
def gogsEdit():
data = {}
data['post_receive'] = getPluginDir() + '/hook/post-receive.tpl'
data['commit'] = getPluginDir() + '/hook/commit.tpl'
return mw.getJson(data)
def getRsaPublic():
path = getHomeDir()
path += '/.ssh/id_rsa.pub'
content = mw.readFile(path)
data = {}
data['mw'] = content
return mw.getJson(data)
def getTotalStatistics():
st = status()
data = {}
if st.strip() == 'start':
list_count = pQuery('select count(id) as num from repository')
count = list_count[0]["num"]
data['status'] = True
data['count'] = count
data['ver'] = mw.readFile(getServerDir() + '/version.pl').strip()
return mw.returnJson(True, 'ok', data)
data['status'] = False
data['count'] = 0
return mw.returnJson(False, 'fail', data)
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 == 'run_log':
print(runLog())
elif func == 'post_receive_log':
print(postReceiveLog())
elif func == 'conf':
print(getConf())
elif func == 'init_conf':
print(getInitdConf())
elif func == 'get_gogs_conf':
print(getGogsConf())
elif func == 'submit_gogs_conf':
print(submitGogsConf())
elif func == 'user_list':
print(userList())
elif func == 'user_project_list':
print(userProjectList())
elif func == 'project_script_edit':
print(projectScriptEdit())
elif func == 'project_script_load':
print(projectScriptLoad())
elif func == 'project_script_unload':
print(projectScriptUnload())
elif func == 'project_script_debug':
print(projectScriptDebug())
elif func == 'gogs_edit':
print(gogsEdit())
elif func == 'get_rsa_public':
print(getRsaPublic())
elif func == 'get_total_statistics':
print(getTotalStatistics())
else:
print('fail')

View File

@@ -1,16 +0,0 @@
{
"ps": "Gogs是一款极易搭建的自助Git服务",
"name": "gogs",
"title": "Gogs",
"versions": ["0.11.86","0.12.9"],
"tip": "soft",
"checks": "server/gogs",
"path":"server/gogs",
"author": "midoks",
"date": "2018-04-01",
"home": "https://gogs.io",
"type": "Git服务器",
"shell": "install.sh",
"pid": "3",
"sort": 7
}

View File

@@ -1,29 +0,0 @@
[Unit]
Description=Gogs
After=network.target
[Service]
# Modify these two values and uncomment them if you have
# repos with lots of files and get an HTTP error 500 because
# of that
###
#LimitMEMLOCK=infinity
#LimitNOFILE=65535
Type=simple
User=git
Group=git
WorkingDirectory={$SERVER_PATH}/gogs
ExecStart={$SERVER_PATH}/gogs/gogs web
ExecReload=/bin/kill -USR2 $MAINPID
Restart=always
Environment=USER=git HOME=/home/git
# Some distributions may not support these hardening directives. If you cannot start the service due
# to an unknown option, comment out the ones not supported by your version of systemd.
ProtectSystem=full
PrivateDevices=yes
PrivateTmp=yes
NoNewPrivileges=true
[Install]
WantedBy=multi-user.target

View File

@@ -1,12 +0,0 @@
[Unit]
Description=Gogs
After=network.target
[Service]
Type=forking
ExecStart={$SERVER_PATH}/gogs/init.d/gogs start
ExecStop={$SERVER_PATH}/gogs/init.d/gogs stop
RemainAfterExit=yes
[Install]
WantedBy=multi-user.target

View File

@@ -1,110 +0,0 @@
#!/bin/sh
#
# /etc/rc.d/init.d/gogs
#
# Runs the Gogs
#
#
# chkconfig: - 85 15
#
### BEGIN INIT INFO
# Provides: gogs
# Required-Start: $remote_fs $syslog
# Required-Stop: $remote_fs $syslog
# Should-Start: mysql postgresql
# Should-Stop: mysql postgresql
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
# Short-Description: Start gogs at boot time.
# Description: Control gogs.
### END INIT INFO
# Source function library.
if [ -f /etc/init.d/functions ];then
. /etc/init.d/functions
fi
if [ -f /etc/rc.d/init.d/functions ];then
. /etc/rc.d/init.d/functions
fi
# Default values
export HOME={$HOME_DIR}
export USER={$RUN_USER}
NAME=gogs
GOGS_HOME={$SERVER_PATH}/gogs
GOGS_PATH=${GOGS_HOME}/$NAME
GOGS_USER={$RUN_USER}
SERVICENAME="Gogs"
LOCKFILE=/tmp/gogs.lock
LOGPATH=${GOGS_HOME}/log
LOGFILE=${LOGPATH}/gogs.log
RETVAL=0
[ -r /etc/sysconfig/$NAME ] && . /etc/sysconfig/$NAME
DAEMON_OPTS="--check $NAME"
[ ! -z "$GOGS_USER" ] && DAEMON_OPTS="$DAEMON_OPTS --user=${GOGS_USER}"
status(){
isStart=`ps -ef|grep 'gogs web' |grep -v grep|awk '{print $2}'`
if [ "$isStart" == '' ];then
echo -e "${SERVICENAME} not running"
else
echo -e "${SERVICENAME}(pid $(echo $isStart)) already running"
fi
}
start() {
isStart=`ps -ef|grep 'gogs web' |grep -v grep|awk '{print $2}'`
if [ "$isStart" != '' ];then
echo "${SERVICENAME}(pid $(echo $isStart)) already running"
return $RETVAL
fi
cd ${GOGS_HOME}
echo -e "Starting ${SERVICENAME}: \c"
${GOGS_PATH} web > ${LOGFILE} 2>&1 &
RETVAL=$?
[ $RETVAL = 0 ] && touch ${LOCKFILE} && echo -e "\033[32mdone\033[0m"
return $RETVAL
}
stop() {
pids=`ps -ef|grep 'gogs web' |grep -v grep|awk '{print $2}'`
arr=($pids)
echo -e "Stopping gogs... \c"
for p in ${arr[@]}
do
kill -9 $p
done
echo -e "\033[32mdone\033[0m"
}
case "$1" in
start)
start
;;
stop)
stop
;;
status)
status
;;
restart)
stop
start
;;
reload)
stop
start
;;
*)
echo "Usage: ${NAME} {start|stop|status|restart}"
exit 1
;;
esac
exit $RETVAL

View File

@@ -1,105 +0,0 @@
#!/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
GOGS_DOWNLOAD='https://dl.gogs.io'
getOs(){
os=`uname`
if [ "Darwin" == "$os" ];then
echo 'darwin'
else
echo 'linux'
fi
return 0
}
getBit(){
echo `getconf LONG_BIT`
}
Install_gogs()
{
mkdir -p $serverPath/source/gogs
echo '正在安装脚本文件...' > $install_tmp
version=$1
os=`getOs`
# if id git &> /dev/null ;then
# echo "git uid is `id -u git`"
# echo "git shell is `grep "^git:" /etc/passwd |cut -d':' -f7 `"
# else
# groupadd git
# useradd -g git git
# fi
git config --global push.default simple
if [ "darwin" == "$os" ];then
file=gogs_${version}_darwin_amd64.zip
else
file=gogs_${version}_linux_amd64.zip
fi
if [ ! -f $serverPath/source/gogs/$file ];then
wget --no-check-certificate -O $serverPath/source/gogs/$file ${GOGS_DOWNLOAD}/${version}/${file}
fi
cd $serverPath/source/gogs && unzip -o $file -d gogs_${version}
mv $serverPath/source/gogs/gogs_${version}/gogs/ $serverPath/gogs
if [ -d $serverPath/gogs ];then
echo $version > $serverPath/gogs/version.pl
cd ${rootPath} && python3 ${rootPath}/plugins/gogs/index.py start
cd ${rootPath} && python3 ${rootPath}/plugins/gogs/index.py initd_install
fi
# if id -u gogs > /dev/null 2>&1; then
# echo "gogs user exists"
# else
# useradd gogs
# cp /etc/sudoers{,.`date +"%Y-%m-%d_%H-%M-%S"`}
# echo "gogs ALL=(ALL) NOPASSWD: ALL" >> /etc/sudoers
# fi
echo 'install success' > $install_tmp
}
Uninstall_gogs()
{
if [ -f /usr/lib/systemd/system/gogs.service ];then
systemctl stop gogs
systemctl disable gogs
rm -rf /usr/lib/systemd/system/gogs.service
systemctl daemon-reload
fi
if [ -f $serverPath/gogs/initd/gogs ];then
$serverPath/gogs/initd/gogs stop
fi
rm -rf $serverPath/gogs
echo 'uninstall success' > $install_tmp
}
action=$1
version=$2
if [ "${1}" == 'install' ];then
Install_gogs $version
else
Uninstall_gogs $version
fi

View File

@@ -1,352 +0,0 @@
function str2Obj(str){
var data = {};
kv = str.split('&');
for(i in kv){
v = kv[i].split('=');
data[v[0]] = v[1];
}
return data;
}
function gogsPost(method,args,callback, title){
var _args = null;
if (typeof(args) == 'string'){
_args = JSON.stringify(str2Obj(args));
} else {
_args = JSON.stringify(args);
}
var _title = '正在获取...';
if (typeof(title) != 'undefined'){
_title = title;
}
var loadT = layer.msg(_title, { icon: 16, time: 0, shade: 0.3 });
$.post('/plugins/run', {name:'gogs', func:method, args:_args}, function(data) {
layer.close(loadT);
if (!data.status){
layer.msg(data.msg,{icon:0,time:2000,shade: [0.3, '#000']});
return;
}
if(typeof(callback) == 'function'){
callback(data);
}
},'json');
}
function gogsSetConfig(){
gogsPost('get_gogs_conf', '', function(data){
var rrdata = $.parseJSON(data.data);
if (!rrdata.status){
layer.msg(rrdata.msg,{icon:0,time:2000,shade: [0.3, '#000']});
return;
}
var rdata = rrdata.data;
var mlist = '';
for (var i = 0; i < rdata.length; i++) {
var w = '140';
if (rdata[i].name == 'error_reporting') w = '250';
var ibody = '<input style="width: ' + w + 'px;" class="bt-input-text mr5" name="' + rdata[i].name + '" value="' + rdata[i].value + '" type="text" >';
switch (rdata[i].type) {
case 0:
var selected_1 = (rdata[i].value == 1) ? 'selected' : '';
var selected_0 = (rdata[i].value == 0) ? 'selected' : '';
ibody = '<select class="bt-input-text mr5" name="' + rdata[i].name + '" style="width: ' + w + 'px;">\
<option value="1" ' + selected_1 + '>开启</option>\
<option value="0" ' + selected_0 + '>关闭</option>\
</select>';
break;
case 1:
var selected_1 = (rdata[i].value == 'On') ? 'selected' : '';
var selected_0 = (rdata[i].value == 'Off') ? 'selected' : '';
ibody = '<select class="bt-input-text mr5" name="' + rdata[i].name + '" style="width: ' + w + 'px;">\
<option value="On" ' + selected_1 + '>开启</option>\
<option value="Off" ' + selected_0 + '>关闭</option></select>'
break;
case 2:
var selected_1 = (rdata[i].value == 'true') ? 'selected' : '';
var selected_0 = (rdata[i].value == 'false') ? 'selected' : '';
ibody = '<select class="bt-input-text mr5" name="' + rdata[i].name + '" style="width: ' + w + 'px;">\
<option value="true" ' + selected_1 + '>开启</option>\
<option value="false" ' + selected_0 + '>关闭</option></select>'
break;
}
mlist += '<p><span>' + rdata[i].name + '</span>' + ibody + ', <font>' + rdata[i].ps + '</font></p>'
}
var html = '<style>.conf_p p{margin-bottom: 2px}</style><div class="conf_p" style="margin-bottom:0">\
' + mlist + '\
<div style="margin-top:10px; padding-right:15px" class="text-right">\
<button class="btn btn-success btn-sm mr5" onclick="gogsSetConfig()">刷新</button>\
<button class="btn btn-success btn-sm" onclick="submitGogsConf()">保存</button></div>\
</div>';
$(".soft-man-con").html(html);
});
}
//提交PHP配置
function submitGogsConf() {
var data = {
DOMAIN: $("input[name='DOMAIN']").val(),
ROOT_URL: $("input[name='ROOT_URL']").val(),
HTTP_ADDR: $("select[name='HTTP_ADDR']").val(),
HTTP_PORT: $("input[name='HTTP_PORT']").val(),
START_SSH_SERVER: $("select[name='START_SSH_SERVER']").val() || 'false',
SSH_PORT: $("input[name='SSH_PORT']").val(),
REQUIRE_SIGNIN_VIEW: $("select[name='REQUIRE_SIGNIN_VIEW']").val() || 'false',
ENABLE_CAPTCHA: $("select[name='ENABLE_CAPTCHA']").val() || 'true',
DISABLE_REGISTRATION: $("select[name='DISABLE_REGISTRATION']").val() || 'false',
ENABLE_NOTIFY_MAIL: $("select[name='ENABLE_NOTIFY_MAIL']").val() || 'false',
FORCE_PRIVATE: $("select[name='FORCE_PRIVATE']").val() || 'false',
SHOW_FOOTER_BRANDING: $("select[name='SHOW_FOOTER_BRANDING']").val() || 'false',
SHOW_FOOTER_VERSION: $("select[name='SHOW_FOOTER_VERSION']").val() || 'false',
SHOW_FOOTER_TEMPLATE_LOAD_TIME: $("select[name='SHOW_FOOTER_TEMPLATE_LOAD_TIME']").val() || 'false',
};
gogsPost('submit_gogs_conf', data, function(ret_data){
var rdata = $.parseJSON(ret_data.data);
layer.msg(rdata.msg, { icon: rdata.status ? 1 : 2 });
gogsSetConfig();
});
}
function gogsEdit(){
gogsPost('gogs_edit',{} , function(data){
// console.log(data);
var rdata = $.parseJSON(data.data);
var edit = '<p class="status">通用的手动编辑:</p>';
edit +='<div class="sfm-opt">\
<button class="btn btn-default btn-sm" onclick="onlineEditFile(0,\''+rdata['post_receive']+'\');">post-receive.tpl</button>\
<button class="btn btn-default btn-sm" onclick="onlineEditFile(0,\''+rdata['commit']+'\');">commit.tpl</button>\
</div>';
$(".soft-man-con").html(edit);
});
}
function gogsUserList(page, search) {
var _data = {};
if (typeof(page) =='undefined'){
var page = 1;
}
_data['page'] = page;
_data['page_size'] = 10;
if(typeof(search) != 'undefined'){
_data['search'] = search;
}
gogsPost('user_list', _data, function(data){
var rdata = $.parseJSON(data.data);
if (!rdata.status){
layer.msg(rdata.msg,{icon:0,time:2000,shade: [0.3, '#000']});
return;
}
content = '<div class="finduser"><input class="bt-input-text mr5 outline_no" type="text" placeholder="查找用户名" id="find_user" style="height: 28px; border-radius: 3px;width: 435px;">';
content += '<button class="btn btn-success btn-sm" onclick="userFind();">查找</button></div>';
content += '<div class="divtable" style="margin-top:5px;"><table class="table table-hover" width="100%" cellspacing="0" cellpadding="0" border="0">';
content += '<thead><tr>';
content += '<th>序号</th>';
content += '<th>用户或组织</th>';
content += '<th>邮件地址</th>';
content += '<th>操作(<a href="'+rdata['data']['root_url']+'" class="btlink" target="_blank">WEB管理</a>)</th>';
content += '</tr></thead>';
content += '<tbody>';
ulist = rdata['data']['data'];
for (i in ulist){
email = ulist[i]["email"] == '' ? '无' : ulist[i]["email"];
content += '<tr><td>'+ulist[i]["id"]+'</td>'+
'<td>'+ulist[i]["name"]+'</td>'+
'<td>'+email+'</td>'+
'<td><a class="btlink" onclick="userProjectList(\''+ulist[i]["name"]+'\')">项目管理</a></td>'+
'</tr>';
}
content += '</tbody>';
content += '</table></div>';
var page = '<div class="dataTables_paginate paging_bootstrap pagination" style="margin-top:0px;"><ul id="softPage" class="page"><div>';
page += rdata['data']['list'];
page += '</div></ul></div>';
content += page;
$(".soft-man-con").html(content);
});
}
function userProjectList(user, search){
var req = {};
if (!isNaN(user)){
req['page'] = user;
req['name'] = user = getCookie('gogsUserSelected');
} else {
req['page'] = 1;
req['name'] = user;
setCookie('gogsUserSelected', user);
}
req['page_size'] = 5;
req['search'] = '';
if(typeof(search) != 'undefined'){
req['search'] = search;
}
$('.layui-layer-close1').click();
gogsPost('user_project_list', req, function(data){
var rdata = [];
try {
rdata = $.parseJSON(data.data);
} catch(e){}
if (!rdata['status']){
layer.msg(rdata['msg'], { icon: 2 });
return;
}
var list = '';
// console.log(rdata);
var project_list = rdata['data']['data'];
for (i in project_list) {
var name = project_list[i]['name'];
list += '<tr><td>'+name+'</td>\
<td>\
<a class="btlink" target="_blank" href="'+rdata['data']['root_url']+user+'/'+name+'">源码</a> | \
<a class="btlink" onclick="projectScript(\''+user+'\',\''+name+'\','+project_list[i]['has_hook']+');">脚本</a>\
</td>\
</tr>';
}
var page = '<div class="dataTables_paginate paging_bootstrap pagination" style="margin-top:0px;"><ul id="softPage" class="page"><div>';
page += rdata['data']['list'];
page += '</div></ul></div>';
var loadOpen = layer.open({
type: 1,
title: '用户('+user+')项目列表',
area: '500px',
content:"<div class='bt-form pd20 c6'>\
<div>\
<div class='divtable' style='margin-top:5px;'>\
<table class='table table-hover'>\
<thead><tr><th>项目</th><th>操作</th></tr></thead>\
<tbody>" + list + "</tbody>\
</table>" +
page +
"</div></div></div>"
});
});
}
function projectScript(user, name,has_hook){
// console.log(user,name,has_hook);
var html = '';
if (has_hook){
html += '<button onclick="projectScriptEdit(\''+user+'\',\''+name+'\')" class="btn btn-default btn-sm">手动编辑</button>';
html += '<button onclick="projectScriptDebug(\''+user+'\',\''+name+'\')" class="btn btn-default btn-sm">调试日志</button>';
html += '<button onclick="projectScriptLoad(\''+user+'\',\''+name+'\')" class="btn btn-default btn-sm">重新加载</button>';
html += '<button onclick="projectScriptUnload(\''+user+'\',\''+name+'\')" class="btn btn-default btn-sm">卸载脚本</button>';
} else {
html += '<button onclick="projectScriptLoad(\''+user+'\',\''+name+'\')" class="btn btn-default btn-sm">加载脚本</button>';
}
var loadOpen = layer.open({
type: 1,
title: '['+user+']['+name+']脚本设置',
area: '240px',
content:'<div class="change-default pd20">'+html+'</div>'
});
}
function projectScriptEdit(user,name){
gogsPost('project_script_edit', {'user':user,'name':name}, function(data){
var rdata = $.parseJSON(data.data);
if (rdata['status']){
onlineEditFile(0, rdata['data']['path']);
} else {
layer.msg(rdata.msg,{icon:1,time:2000,shade: [0.3, '#000']});
}
});
}
function projectScriptLoad(user,name){
gogsPost('project_script_load', {'user':user,'name':name}, function(data){
if (data.data != 'ok'){
layer.msg(data.data,{icon:0,time:2000,shade: [0.3, '#000']});
return;
}
layer.msg('加载成功!',{icon:1,time:2000,shade: [0.3, '#000']});
setTimeout(function(){
userProjectList(1);
}, 2000);
});
}
function projectScriptUnload(user,name){
gogsPost('project_script_unload', {'user':user,'name':name}, function(data){
if (data.data != 'ok'){
layer.msg(data.data,{icon:0,time:2000,shade: [0.3, '#000']});
return;
}
layer.msg('卸载成功!',{icon:1,time:2000,shade: [0.3, '#000']});
setTimeout(function(){
userProjectList(1);
}, 2000);
});
}
function projectScriptDebug(user,name){
gogsPost('project_script_debug', {'user':user,'name':name}, function(data){
var rdata = $.parseJSON(data.data);
if (rdata['status']){
onlineEditFile(0, rdata['path']);
} else {
layer.msg(rdata.msg,{icon:1,time:2000,shade: [0.3, '#000']});
}
});
}
function getRsaPublic(){
gogsPost('get_rsa_public', {}, function(data){
var rdata = $.parseJSON(data.data);
var con = '<div class="tab-con">\
<div class="myKeyCon ptb15">\
<textarea style="margin:0px;width:580px;height:110px;outline:none;" spellcheck="false">'+rdata.mw+'</textarea>\
</div>\
<ul class="help-info-text c7 pull-left"></ul>\
</div>'
layer.open({
type: 1,
area: "600px",
title: '本机公钥',
closeBtn: 2,
shift: 5,
shadeClose: false,
content:con
});
});
}
function gogsRead(){
var readme = '<ul class="help-info-text c7">';
readme += '<li>默认使用MySQL,第一个启动加载各种配置,并修改成正确的数据库配置</li>';
readme += '<li>邮件端口使用456,gogs仅支持使用STARTTLS的SMTP协议</li>';
readme += '<li>如果使用项目中脚本本地同步,<a target="_blank" href="https://github.com/midoks/mdserver-web/wiki/%E6%8F%92%E4%BB%B6%E7%AE%A1%E7%90%86%5Bgogs%E4%BD%BF%E7%94%A8%E8%AF%B4%E6%98%8E%5D#%E5%90%AF%E5%8A%A8gogs%E5%90%8E%E5%A6%82%E6%9E%9C%E8%A6%81%E4%BD%BF%E7%94%A8hook%E8%84%9A%E6%9C%AC%E5%90%8C%E6%AD%A5%E4%BB%A3%E7%A0%81%E9%9C%80%E8%A6%81%E5%BC%80%E5%90%AFssh%E7%AB%AF%E5%8F%A3">点击查看</></li>';
readme += '<li><a href="#" onclick="getRsaPublic();">点击查看本机公钥</></li>';
readme += '</ul>';
$('.soft-man-con').html(readme);
}

View File

@@ -1,37 +0,0 @@
global
daemon
log 127.0.0.1 local0
pidfile /tmp/haproxy.pid
maxconn 4000
user haproxy
group haproxy
defaults
mode http
log global
option httplog
timeout connect 10s
timeout client 15s
timeout server 15s
listen stats
mode http
bind *:10800
stats enable
stats uri /haproxy
stats realm Haproxy\ Statistics
stats auth admin:admin
frontend http-in
bind *:1080
default_backend servers
option forwardfor
option httpclose
backend servers
balance roundrobin
server web1 0.0.0.0:9090 check

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.5 KiB

View File

@@ -1,18 +0,0 @@
<div class="bt-form">
<div class="bt-w-main">
<div class="bt-w-menu">
<p class="bgw" onclick="pluginService('haproxy');">服务</p>
<p onclick="pluginInitD('haproxy');">自启动</p>
<p onclick="pluginConfigTpl('haproxy');">配置修改</p>
<p onclick="pluginLogs('haproxy','','run_log',30);">运行日志</p>
</div>
<div class="bt-w-con pd15">
<div class="soft-man-con"></div>
</div>
</div>
</div>
<script type="text/javascript">
$.getScript( "/plugins/file?name=haproxy&f=js/haproxy.js", function(){
pluginService('haproxy');
});
</script>

View File

@@ -1,253 +0,0 @@
# coding:utf-8
import sys
import io
import os
import time
import re
import string
import subprocess
sys.path.append(os.getcwd() + "/class/core")
import mw
app_debug = False
if mw.isAppleSystem():
app_debug = True
def getPluginName():
return 'haproxy'
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 getConfTpl():
path = getPluginDir() + "/conf/haproxy.conf"
return path
def getConf():
path = getServerDir() + "/haproxy.conf"
return path
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('}')
t = t.split(':')
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 configTpl():
path = getPluginDir() + '/tpl'
pathFile = os.listdir(path)
tmp = []
for one in pathFile:
file = path + '/' + one
tmp.append(file)
return mw.getJson(tmp)
def readConfigTpl():
args = getArgs()
data = checkArgs(args, ['file'])
if not data[0]:
return data[1]
content = mw.readFile(args['file'])
content = contentReplace(content)
return mw.returnJson(True, 'ok', content)
def contentReplace(content):
service_path = mw.getServerDir()
content = content.replace('{$ROOT_PATH}', mw.getRootDir())
content = content.replace('{$SERVER_PATH}', service_path)
content = content.replace('{$SERVER_APP}', service_path + '/haproxy')
return content
def status():
data = mw.execShell(
"ps -ef|grep haproxy |grep -v grep | grep -v python | awk '{print $2}'")
if data[0] == '':
return 'stop'
return 'start'
def initDreplace():
file_tpl = getInitDTpl()
service_path = os.path.dirname(os.getcwd())
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 = contentReplace(content)
mw.writeFile(file_bin, content)
mw.execShell('chmod +x ' + file_bin)
# config replace
conf_bin = getConf()
if not os.path.exists(conf_bin):
conf_content = mw.readFile(getConfTpl())
conf_content = contentReplace(conf_content)
mw.writeFile(getServerDir() + '/haproxy.conf', conf_content)
# systemd
systemDir = mw.systemdCfgDir()
systemService = systemDir + '/haproxy.service'
systemServiceTpl = getPluginDir() + '/init.d/haproxy.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('{$SERVER_PATH}', service_path)
mw.writeFile(systemService, se_content)
mw.execShell('systemctl daemon-reload')
return file_bin
def haOp(method):
file = initDreplace()
if not mw.isAppleSystem():
data = mw.execShell('systemctl ' + method + ' haproxy')
if data[1] == '':
return 'ok'
return 'fail'
data = mw.execShell(file + ' ' + method)
if data[1] == '':
return 'ok'
return data[1]
def start():
return haOp('start')
def stop():
return haOp('stop')
def restart():
return haOp('restart')
def reload():
return haOp('reload')
def initdStatus():
if mw.isAppleSystem():
return "Apple Computer does not support"
shell_cmd = 'systemctl status haproxy | 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 haproxy')
return 'ok'
def initdUinstall():
if mw.isAppleSystem():
return "Apple Computer does not support"
mw.execShell('systemctl disable haproxy')
return 'ok'
def runLog():
path = getConf()
content = mw.readFile(path)
rep = 'log\s*=\s*(.*)'
tmp = re.search(rep, content)
return tmp.groups()[0]
def getPort():
path = getConf()
content = mw.readFile(path)
rep = 'listen\s*=\s*(.*)'
tmp = re.search(rep, content)
return tmp.groups()[0]
if __name__ == "__main__":
func = sys.argv[1]
if func == 'status':
print(status())
elif func == 'start':
print(start())
elif func == 'stop':
print(stop())
elif func == 'restart':
print(restart())
elif func == 'reload':
print(reload())
elif func == 'initd_status':
print(initdStatus())
elif func == 'initd_install':
print(initdInstall())
elif func == 'initd_uninstall':
print(initdUinstall())
elif func == 'conf':
print(getConf())
elif func == 'config_tpl':
print(configTpl())
elif func == 'read_config_tpl':
print(readConfigTpl())
elif func == 'run_log':
print(runLog())
elif func == 'query_log':
print(queryLog())
else:
print('error')

View File

@@ -1,19 +0,0 @@
{
"sort": 7,
"ps": "可靠、高性能的TCP/HTTP负载平衡器",
"name": "haproxy",
"title": "haproxy",
"shell": "install.sh",
"versions":["2.6"],
"updates":["2.6"],
"tip": "soft",
"checks": "server/haproxy",
"path": "server/haproxy",
"display": 1,
"author": "midoks",
"date": "2022-08-26",
"home": "https://www.haproxy.org/",
"doc1": "https://www.haproxy.org/",
"type": 0,
"pid": "4"
}

View File

@@ -1,13 +0,0 @@
[Unit]
Description=he Reliable, High Performance TCP/HTTP Load Balancer
After=network.target
[Service]
Type=forking
ExecStartPre={$SERVER_PATH}/haproxy/sbin/haproxy -c -f {$SERVER_PATH}/haproxy/haproxy.conf
ExecStart={$SERVER_PATH}/haproxy/sbin/haproxy -D -f {$SERVER_PATH}/haproxy/haproxy.conf
ExecReload={$SERVER_PATH}/haproxy/sbin/haproxy -f {$SERVER_PATH}/haproxy/haproxy.conf -c -q
Restart=on-failure
[Install]
WantedBy=multi-user.target

View File

@@ -1,119 +0,0 @@
#!/bin/sh
#
# chkconfig: - 85 15
# description: HA-Proxy is a TCP/HTTP reverse proxy which is particularly suited \
# for high availability environments.
# processname: haproxy
# config: /etc/haproxy/haproxy.cfg
# pidfile: /var/run/haproxy.pid
# Script Author: Simon Matter <simon.matter@invoca.ch>
# Version: 2004060600
# Source function library.
if [ -f /etc/init.d/functions ]; then
. /etc/init.d/functions
elif [ -f /etc/rc.d/init.d/functions ] ; then
. /etc/rc.d/init.d/functions
else
echo ".."
fi
# Source networking configuration.
if [ -f /etc/sysconfig/network ];then
. /etc/sysconfig/network
fi
# Check that networking is up.
# [ ${NETWORKING} = "no" ] && exit 0
HAPROXYDIR={$SERVER_PATH}/haproxy
BASENAME=haproxy
# This is our service name
#BASENAME=`basename $0`
#if [ -L $0 ]; then
# BASENAME=`find $0 -name $BASENAME -printf %l`
# BASENAME=`basename $BASENAME`
#fi
[ -f $HAPROXYDIR/etc/$BASENAME.conf ] || exit 1
RETVAL=0
start() {
$HAPROXYDIR/sbin/$BASENAME -c -q -f $HAPROXYDIR/etc/$BASENAME.conf
if [ $? -ne 0 ]; then
echo "Errors found in configuration file, check it with '$BASENAME check'."
return 1
fi
echo -n "Starting $BASENAME: "
daemon $HAPROXYDIR/sbin/$BASENAME -D -f $HAPROXYDIR/etc/$BASENAME.conf
RETVAL=$?
# echo
# [ $RETVAL -eq 0 ] && touch /var/lock/subsys/$BASENAME
return $RETVAL
}
stop() {
echo -n "Shutting down $BASENAME: "
killproc $BASENAME -USR1
RETVAL=$?
echo
[ $RETVAL -eq 0 ] && rm -f /var/lock/subsys/$BASENAME
[ $RETVAL -eq 0 ] && rm -f /var/run/$BASENAME.pid
return $RETVAL
}
restart() {
$HAPROXYDIR/sbin/$BASENAME -c -q -f $HAPROXYDIR/etc/$BASENAME.conf
if [ $? -ne 0 ]; then
echo "Errors found in configuration file, check it with '$BASENAME check'."
return 1
fi
stop
start
}
reload() {
$HAPROXYDIR/sbin/$BASENAME -c -q -f $HAPROXYDIR/etc/$BASENAME.conf
if [ $? -ne 0 ]; then
echo "Errors found in configuration file, check it with '$BASENAME check'."
return 1
fi
$HAPROXYDIR/sbin/$BASENAME -D -f $HAPROXYDIR/$BASENAME.conf -p /var/run/$BASENAME.pid -sf $(cat /var/run/$BASENAME.pid)
}
check() {
$HAPROXYDIR/sbin/$BASENAME -c -q -V -f $HAPROXYDIR/etc/$BASENAME.conf
}
rhstatus() {
status $BASENAME
}
condrestart() {
[ -e /var/lock/subsys/$BASENAME ] && restart || :
}
# See how we were called.
case "$1" in
start) start ;;
stop) stop ;;
restart) restart;;
reload) reload;;
condrestart) condrestart ;;
status) rhstatus ;;
check) check ;;
*)
echo $"Usage: $BASENAME {start|stop|restart|reload|condrestart|status|check}"
exit 1
esac
exit $?

View File

@@ -1,52 +0,0 @@
#!/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
action=$1
type=$2
echo $action $type
if id haproxy &> /dev/null ;then
echo "haproxy UID is `id -u haproxy`"
echo "haproxy Shell is `grep "^haproxy:" /etc/passwd |cut -d':' -f7 `"
else
groupadd haproxy
useradd -g haproxy haproxy
fi
if [ "${2}" == "" ];then
echo '缺少安装脚本...' > $install_tmp
exit 0
fi
if [ ! -d $curPath/versions/$2 ];then
echo '缺少安装脚本2...' > $install_tmp
exit 0
fi
if [ "${action}" == "uninstall" ];then
if [ -f /usr/lib/systemd/system/haproxy.service ] || [ -f /lib/systemd/system/haproxy.service ];then
systemctl stop haproxy
systemctl disable haproxy
rm -rf /usr/lib/systemd/system/haproxy.service
rm -rf /lib/systemd/system/haproxy.service
systemctl daemon-reload
fi
fi
sh -x $curPath/versions/$2/install.sh $1
if [ "${action}" == "install" ] && [ -d $serverPath/haproxy ];then
#初始化
cd ${rootPath} && python3 ${rootPath}/plugins/haproxy/index.py start ${type}
cd ${rootPath} && python3 ${rootPath}/plugins/haproxy/index.py initd_install ${type}
fi

View File

@@ -1,53 +0,0 @@
function spPostMin(method, args, callback){
var req_data = {};
req_data['name'] = 'haproxy';
req_data['func'] = method;
if (typeof(args) != 'undefined' && args!=''){
req_data['args'] = JSON.stringify(args);
}
$.post('/plugins/run', req_data, function(data) {
if (!data.status){
layer.msg(data.msg,{icon:0,time:2000,shade: [0.3, '#000']});
return;
}
if(typeof(callback) == 'function'){
callback(data);
}
},'json');
}
function spPost(method, args, callback){
var loadT = layer.msg('正在获取...', { icon: 16, time: 0, shade: 0.3 });
spPostMin(method,args,function(data){
layer.close(loadT);
if(typeof(callback) == 'function'){
callback(data);
}
});
}
function secToTime(s) {
var t;
if(s > -1){
var hour = Math.floor(s/3600);
var min = Math.floor(s/60) % 60;
var sec = s % 60;
if(hour < 10) {
t = '0'+ hour + ":";
} else {
t = hour + ":";
}
if(min < 10){t += "0";}
t += min + ":";
if(sec < 10){t += "0";}
t += sec.toFixed(2);
}
return t;
}

View File

@@ -1,70 +0,0 @@
#!/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")
sysName=`uname`
install_tmp=${rootPath}/tmp/mw_install.pl
bash ${rootPath}/scripts/getos.sh
OSNAME=`cat ${rootPath}/data/osname.pl`
OSNAME_ID=`cat /etc/*-release | grep VERSION_ID | awk -F = '{print $2}' | awk -F "\"" '{print $2}'`
VERSION=2.6.4
MIN_VERSION=2.6
Install_App()
{
echo '正在安装脚本文件...' > $install_tmp
mkdir -p $serverPath/haproxy
APP_DIR=${serverPath}/source/haproxy
mkdir -p $APP_DIR
if [ ! -f ${APP_DIR}/haproxy-${VERSION}.tar.gz ];then
if [ $sysName == 'Darwin' ]; then
wget --no-check-certificate -O ${APP_DIR}/haproxy-${VERSION}.tar.gz https://www.haproxy.org/download/${MIN_VERSION}/src/haproxy-${VERSION}.tar.gz
else
curl -sSLo ${APP_DIR}/haproxy-${VERSION}.tar.gz https://www.haproxy.org/download/${MIN_VERSION}/src/haproxy-${VERSION}.tar.gz
fi
fi
if [ ! -f ${APP_DIR}/haproxy-${VERSION}.tar.gz ];then
curl -sSLo ${APP_DIR}/haproxy-${VERSION}.tar.gz https://www.haproxy.org/download/${MIN_VERSION}/src/haproxy-${VERSION}.tar.gz
fi
cd ${APP_DIR} && tar -zxvf haproxy-${VERSION}.tar.gz
if [ "$OSNAME" == "macos" ];then
cd ${APP_DIR}/haproxy-${VERSION} && make TARGET=osx && make install PREFIX=$serverPath/haproxy
else
cd ${APP_DIR}/haproxy-${VERSION} && make TARGET=linux-glibc && make install PREFIX=$serverPath/haproxy
fi
echo $MIN_VERSION > $serverPath/haproxy/version.pl
echo 'Install_HA' > $install_tmp
}
Uninstall_App()
{
if [ -f $serverPath/haproxy/initd/haproxy ];then
$serverPath/haproxy/initd/haproxy stop
fi
rm -rf $serverPath/haproxy
echo "Uninstall_HA" > $install_tmp
}
action=$1
if [ "${1}" == 'install' ];then
Install_App
else
Uninstall_App
fi

View File

@@ -1,201 +0,0 @@
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.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.1 KiB

View File

@@ -1,20 +0,0 @@
<div class="bt-form">
<div class="bt-w-main">
<div class="bt-w-menu">
<p class="bgw" onclick="pluginService('imail');">服务</p>
<p onclick="pluginInitD('imail');">自启动</p>
<p onclick="pluginConfig('imail',null, 'conf');">配置文件</p>
<p onclick="pluginLogs('imail',null,'run_log', 50);">运行日志</p>
</div>
<div class="bt-w-con pd15">
<div class="soft-man-con"></div>
</div>
</div>
</div>
<script type="text/javascript">
$.getScript( "/plugins/file?name=imail&f=js/imail.js", function(){
mail.init();
pluginService('imail');
});
</script>

View File

@@ -1,218 +0,0 @@
# coding:utf-8
import sys
import io
import os
import time
import re
import socket
import json
from datetime import datetime
sys.path.append(os.getcwd() + "/class/core")
import mw
app_debug = False
if mw.isAppleSystem():
app_debug = True
class App:
__setupPath = '/www/server/imail'
__SR = ''
def __init__(self):
self.__setupPath = self.getServerDir()
self.__SR = '''#!/bin/bash
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH
export USER=%s
export HOME=%s && ''' % ( self.getRunUser(), self.getHomeDir())
def getArgs(self):
args = sys.argv[3:]
tmp = {}
args_len = len(args)
if args_len == 1:
t = args[0].strip('{').strip('}')
t = t.split(':')
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 __release_port(self, port):
from collections import namedtuple
try:
import firewall_api
firewall_api.firewall_api().addAcceptPortArgs(port, 'IMail-Server', 'port')
return port
except Exception as e:
return "Release failed {}".format(e)
def openPort(self):
for i in ["25", "110", "143", "465", "995", "993", "587"]:
self.__release_port(i)
return True
def getPluginName(self):
return 'imail'
def getPluginDir(self):
return mw.getPluginDir() + '/' + self.getPluginName()
def getServerDir(self):
return mw.getServerDir() + '/' + self.getPluginName()
def getInitdConfTpl(self):
path = self.getPluginDir() + "/init.d/imail.tpl"
return path
def getHomeDir(self):
if mw.isAppleSystem():
user = mw.execShell(
"who | sed -n '2, 1p' |awk '{print $1}'")[0].strip()
return '/Users/' + user
else:
return '/root'
def getRunUser(self):
if mw.isAppleSystem():
user = mw.execShell(
"who | sed -n '2, 1p' |awk '{print $1}'")[0].strip()
return user
else:
return 'root'
def status(self):
data = mw.execShell(
"ps -ef|grep " + self.getPluginName() + " |grep -v grep | grep -v python | awk '{print $2}'")
if data[0] == '':
return 'stop'
return 'start'
def contentReplace(self, content):
service_path = mw.getServerDir()
content = content.replace('{$ROOT_PATH}', mw.getRootDir())
content = content.replace('{$SERVER_PATH}', service_path)
content = content.replace('{$RUN_USER}', self.getRunUser())
content = content.replace('{$HOME_DIR}', self.getHomeDir())
return content
def initDreplace(self):
file_tpl = self.getInitdConfTpl()
service_path = mw.getServerDir()
initD_path = self.getServerDir() + '/init.d'
if not os.path.exists(initD_path):
os.mkdir(initD_path)
self.openPort()
file_bin = initD_path + '/' + self.getPluginName()
if not os.path.exists(file_bin):
content = mw.readFile(file_tpl)
content = self.contentReplace(content)
mw.writeFile(file_bin, content)
mw.execShell('chmod +x ' + file_bin)
# systemd
systemDir = mw.systemdCfgDir()
systemService = systemDir + '/imail.service'
systemServiceTpl = self.getPluginDir() + '/init.d/imail.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('{$SERVER_PATH}', service_path)
mw.writeFile(systemService, se_content)
mw.execShell('systemctl daemon-reload')
log_path = self.getServerDir() + '/logs'
if not os.path.exists(log_path):
os.mkdir(log_path)
return file_bin
def imOp(self, method):
file = self.initDreplace()
if not mw.isAppleSystem():
cmd = 'systemctl {} {}'.format(method, self.getPluginName())
data = mw.execShell(cmd)
if data[1] == '':
return 'ok'
return 'fail'
data = mw.execShell(self.__SR + file + ' ' + method)
if data[1] == '':
return 'ok'
return data[0]
def start(self):
return self.imOp('start')
def stop(self):
return self.imOp('stop')
def restart(self):
return self.imOp('restart')
def reload(self):
return self.imOp('reload')
def initd_status(self):
if mw.isAppleSystem():
return "Apple Computer does not support"
cmd = 'systemctl status imail | grep loaded | grep "enabled;"'
data = mw.execShell(cmd)
if data[0] == '':
return 'fail'
return 'ok'
def initd_install(self):
if mw.isAppleSystem():
return "Apple Computer does not support"
mw.execShell('systemctl enable imail')
return 'ok'
def initd_uinstall(self):
if mw.isAppleSystem():
return "Apple Computer does not support"
mw.execShell('systemctl disable imail')
return 'ok'
def conf(self):
conf_path = self.getServerDir() + '/custom/conf/app.conf'
if not os.path.exists(conf_path):
return mw.returnJson(False, "请先安装初始化!<br/>默认地址:http://" + mw.getLocalIp() + ":1080")
return self.getServerDir() + '/custom/conf/app.conf'
def run_log(self):
ilog = self.getServerDir() + '/logs/imail.log'
if not os.path.exists(ilog):
return mw.returnJson(False, "请先安装初始化!<br/>默认地址:http://" + mw.getLocalIp() + ":1080")
return self.getServerDir() + '/logs/imail.log'
if __name__ == "__main__":
func = sys.argv[1]
classApp = App()
try:
data = eval("classApp." + func + "()")
print(data)
except Exception as e:
print('error:' + str(e))

View File

@@ -1,18 +0,0 @@
{
"sort": 7,
"ps": "简单邮件服务[DEV]",
"name": "imail",
"title": "邮件服务",
"shell": "install.sh",
"versions":["0.0.19"],
"updates":["0.0.19"],
"tip": "soft",
"checks": "server/imail",
"path": "server/imail",
"display": 1,
"author": "midoks",
"date": "2022-09-26",
"home": "https://github.com/midoks/mdserver-web",
"type": 0,
"pid": "5"
}

View File

@@ -1,12 +0,0 @@
[Unit]
Description=Imail Simple Mail Server
After=network.target
[Service]
Type=forking
ExecStart={$SERVER_PATH}/imail/init.d/imail start
ExecStop={$SERVER_PATH}/imail/init.d/imail stop
RemainAfterExit=yes
[Install]
WantedBy=multi-user.target

View File

@@ -1,89 +0,0 @@
#!/bin/bash
# chkconfig: 2345 55 25
# description: Imail Service
### BEGIN INIT INFO
# Provides: bt
# Required-Start: $all
# Required-Stop: $all
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
# Short-Description: starts Imail
# Description: starts the Imail
### END INIT INFO
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
# Source function library.
if [ -f /etc/init.d/functions ];then
. /etc/init.d/functions
fi
if [ -f /etc/rc.d/init.d/functions ];then
. /etc/rc.d/init.d/functions
fi
app_path={$SERVER_PATH}/imail
SERVICENAME="imail"
im_start(){
isStart=`ps -ef|grep 'imail service' |grep -v grep|awk '{print $2}'`
if [ "$isStart" == '' ];then
echo -e "Starting imail... \c"
cd $app_path && ${app_path}/imail service &
isStart=""
while [[ "$isStart" == "" ]];
do
echo -e ".\c"
sleep 0.5
isStart=$(lsof -n -P -i:25|grep LISTEN|grep -v grep|awk '{print $2}'|xargs)
let n+=1
if [ $n -gt 15 ];then
break;
fi
done
if [ "$isStart" == '' ];then
echo -e "\033[31mfailed\033[0m"
echo '------------------------------------------------------'
tail -n 20 ${app_path}/logs/run_away.log
echo '------------------------------------------------------'
echo -e "\033[31mError: ${SERVICENAME} service startup failed.\033[0m"
return;
fi
echo -e "\033[32mdone\033[0m"
else
echo "Starting ${SERVICENAME}(pid $(echo $isStart)) already running"
fi
}
im_stop(){
pids=`ps -ef|grep 'imail service' |grep -v grep|awk '{print $2}'`
arr=($pids)
echo -e "Stopping ${SERVICENAME}... \c"
for p in ${arr[@]}
do
kill -9 $p
done
echo -e "\033[32mdone\033[0m"
}
im_status(){
isStart=`ps -ef|grep 'imail service' |grep -v grep|awk '{print $2}'`
if [ "$isStart" == '' ];then
echo -e "${SERVICENAME} not running"
else
echo -e "${SERVICENAME}(pid $(echo $isStart)) already running"
fi
}
case "$1" in
'start') im_start;;
'stop') im_stop;;
'status') im_status;;
'reload')
im_stop
im_start;;
'restart')
im_stop
im_start;;
esac

View File

@@ -1,25 +0,0 @@
#!/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
action=$1
type=$2
if [ "${2}" == "" ];then
echo '缺少安装脚本...' > $install_tmp
exit 0
fi
if [ ! -d $curPath/versions/$2 ];then
echo '缺少安装脚本2...' > $install_tmp
exit 0
fi
sh -x $curPath/versions/$2/install.sh $1

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