Compare commits

..
471 Commits
Author SHA1 Message Date
Mr Chen 9615fb2203 Update config.py 2018-12-25 10:09:33 +08:00
Mr Chen 49c181c577 Update cli.sh 2018-12-25 10:07:16 +08:00
midoks 0fa5e4fb48 update 2018-12-24 22:33:49 +08:00
Mr Chen 8e7130c382 Update app.py 2018-12-24 15:38:55 +08:00
Mr Chen 33c1ac82c8 Update index.html 2018-12-24 11:48:07 +08:00
Mr Chen df5aec2e8f Update install.sh 2018-12-24 11:37:53 +08:00
Mr Chen 6c864177ee Update install.sh 2018-12-24 11:37:35 +08:00
Mr Chen 9ee09b0e7d update 2018-12-24 11:14:51 +08:00
Mr Chen b7da91f92b update 2018-12-21 11:44:38 +08:00
Mr Chen 17e6703c07 Update site.js 2018-12-21 10:52:21 +08:00
Mr Chen a5f77a37f2 Update mw.tpl 2018-12-21 10:07:24 +08:00
midoks 78fc9fd90f u 2018-12-20 23:41:26 +08:00
midoks 700f042033 防盗链 ok 2018-12-20 22:30:00 +08:00
midoks e44768e608 update 2018-12-20 21:45:09 +08:00
midoks 2acef86207 Update config.py 2018-12-20 21:35:49 +08:00
midoks 6256bb02da Update cli.sh 2018-12-20 21:34:52 +08:00
midoks 04f5a27492 Update system_api.py 2018-12-20 21:34:47 +08:00
Mr Chen 7cd476696a Update site.js 2018-12-20 19:28:47 +08:00
Mr Chen 2ecaf0e2a3 update 2018-12-20 19:22:33 +08:00
Mr Chen c5d3494a94 update 2018-12-20 14:15:13 +08:00
Mr Chen af0658fd41 update 2018-12-20 14:05:57 +08:00
Mr Chen cccbd88145 站点添加优化 2018-12-20 13:39:43 +08:00
Mr Chen 48aad5b248 Update index.py 2018-12-20 13:29:05 +08:00
Mr Chen d843213c39 流量控制功能ok 2018-12-20 13:27:50 +08:00
Mr Chen 7ad33c5c59 update 2018-12-20 11:12:39 +08:00
Mr Chen ee56cfe5ab Update README.md 2018-12-20 11:07:36 +08:00
Mr Chen c6b38aa996 update 2018-12-20 10:45:43 +08:00
midoks c5205c2d55 文件保存 2018-12-20 00:48:41 +08:00
midoks 1be07f96ff Update install.sh 2018-12-20 00:26:07 +08:00
midoks 9f4af88c1e update 2018-12-19 23:37:31 +08:00
Mr Chen ebc0559dff up 2018-12-19 20:43:33 +08:00
Mr Chen fe320ecd3b phpinfo ok 2018-12-19 17:17:07 +08:00
Mr Chen 02c06e7ff1 update 2018-12-19 16:50:15 +08:00
Mr Chen 6d90d6fde5 update 2018-12-19 15:50:07 +08:00
Mr Chen bfc0b9efa3 update 2018-12-19 15:41:00 +08:00
Mr Chen 6e4e7bcf68 update 2018-12-19 15:21:31 +08:00
Mr Chen 5da505084d update 2018-12-19 13:54:19 +08:00
Mr Chen f19df17ef5 update 2018-12-19 10:42:37 +08:00
Mr Chen d99e89744d Update index.py 2018-12-19 10:34:18 +08:00
midoks 9695fe4b9b Update index.py 2018-12-18 23:22:08 +08:00
Mr Chen 5907dc36c5 Update index.py 2018-12-18 19:50:57 +08:00
Mr Chen d694af9358 update 2018-12-18 19:43:41 +08:00
Mr Chen 4dc2828c59 update 2018-12-18 19:37:46 +08:00
Mr Chen 37519b4c92 超时限制,上传限制 2018-12-18 18:09:54 +08:00
Mr Chen 90999c7ae1 u 2018-12-18 15:37:02 +08:00
Mr Chen f16d9ea269 update 2018-12-18 15:23:48 +08:00
Mr Chen 86098541e0 PHP update 2018-12-18 15:13:33 +08:00
Mr Chen 9ef04a4d9e Create php.ini 2018-12-18 15:13:22 +08:00
Mr Chen 752f8fae5a Update csvn.js 2018-12-18 15:13:19 +08:00
Mr Chen c5fdecf459 up 2018-12-18 13:43:24 +08:00
Mr Chen 19284cd5ef update 2018-12-18 13:17:37 +08:00
Mr Chen da92ab1c89 update 2018-12-18 10:38:46 +08:00
midoks 18ca47e8b3 up 2018-12-17 23:26:00 +08:00
midoks 8b624297ba Update site_api.py 2018-12-17 21:40:08 +08:00
midoks f0d9ff2ae9 Update cli.sh 2018-12-17 21:38:15 +08:00
midoks 21b2c17220 Update site_api.py 2018-12-17 21:38:11 +08:00
midoks 0df898a7d9 Update install.sh 2018-12-17 21:38:08 +08:00
midoks cf1f4c8538 Update default.sql 2018-12-17 21:35:13 +08:00
Mr Chen 89ecdcd29a update 2018-12-17 19:17:51 +08:00
Mr Chen 0c8fa5e616 Update site.py 2018-12-17 18:21:06 +08:00
Mr Chen f9252da1b0 update 2018-12-17 18:16:59 +08:00
Mr Chen e31cbad7dd update 2018-12-17 17:21:33 +08:00
Mr Chen 5bed9f0802 update 2018-12-17 16:47:40 +08:00
Mr Chen 1ae4b54779 update 2018-12-17 15:20:30 +08:00
Mr Chen 2b2a75adf0 Update openresty.js 2018-12-17 11:23:05 +08:00
Mr Chen 55184341d7 Update install.sh 2018-12-17 11:17:58 +08:00
Mr Chen 6504decc30 update 2018-12-17 11:11:33 +08:00
Mr Chen 843d798b91 update 2018-12-17 11:06:58 +08:00
midoks 087a6b4809 update 2018-12-16 23:29:35 +08:00
midoks 44438bd9aa Update install.sh 2018-12-15 11:18:33 +08:00
midoks c33cba0e85 update 2018-12-15 01:22:00 +08:00
midoks cdc3c95367 update 2018-12-14 23:29:47 +08:00
midoks 222cebe1b3 update 2018-12-14 21:53:33 +08:00
midoks 2fc2eff97e Update install.sh 2018-12-14 21:35:59 +08:00
midoks 79f8baeeb3 update 2018-12-14 21:35:55 +08:00
midoks 0399219df6 Update pick_filter.txt 2018-12-14 21:34:13 +08:00
midoks cd0d5ede16 Update pick_filter.txt 2018-12-14 21:31:58 +08:00
Mr Chen 1e6d1c6127 Update info.json 2018-12-14 19:21:23 +08:00
Mr Chen ff87dbe258 Update index.py 2018-12-14 18:38:27 +08:00
Mr Chen 7d6ddb40db update 2018-12-14 18:29:53 +08:00
Mr Chen 1cb57d3f65 update 2018-12-14 18:04:50 +08:00
Mr Chen 9ed434d274 update 2018-12-14 17:39:47 +08:00
Mr Chen f532a376a7 update 2018-12-14 16:06:59 +08:00
Mr Chen 4405f7720b Update info.json 2018-12-14 15:20:16 +08:00
Mr Chen fbde4aab88 Update commit.tpl 2018-12-14 15:20:11 +08:00
Mr Chen 864691d357 同步数据处理 2018-12-14 14:52:33 +08:00
Mr Chen 95bf0cb1e8 修正权限格式 2018-12-14 13:56:16 +08:00
midoks 126f2d553d Update index.py 2018-12-13 23:17:49 +08:00
midoks 2fb88be080 update 2018-12-13 23:15:33 +08:00
midoks fa307cdb49 Update commit.tpl 2018-12-13 23:05:55 +08:00
midoks 0850b6ed44 Update index.py 2018-12-13 22:52:20 +08:00
midoks ec1e99f9ca Update index.py 2018-12-13 22:47:45 +08:00
midoks 546dc4d7eb Update index.py 2018-12-13 22:45:16 +08:00
midoks 9b0b4ac305 Update index.py 2018-12-13 22:40:37 +08:00
midoks 23a1f3936d update 2018-12-13 22:37:27 +08:00
Mr Chen 4cb22a1e35 update 2018-12-13 20:01:29 +08:00
Mr Chen 3e3cdbe18c update 2018-12-13 18:20:27 +08:00
Mr Chen bdc46d9401 Update pick_filter.txt 2018-12-13 18:08:55 +08:00
Mr Chen 64986dd125 gogs ready 2018-12-13 18:07:44 +08:00
Mr Chen 7ee00ab0b5 Update csvn.js 2018-12-13 17:13:48 +08:00
Mr Chen 488d92ba19 update 2018-12-13 17:09:23 +08:00
Mr Chen 9cebfb64cb 查找更新 2018-12-13 15:36:19 +08:00
Mr Chen 7800429f8d update 2018-12-13 15:20:47 +08:00
Mr Chen 7954714bb7 update 2018-12-13 14:57:30 +08:00
Mr Chen 1d396b73f0 Update index.html 2018-12-13 14:37:49 +08:00
Mr Chen 5f95a1d56a update 2018-12-13 14:37:38 +08:00
Mr Chen 51951a2685 update 2018-12-13 14:37:31 +08:00
Mr Chen 737453600d Update csvn.js 2018-12-13 13:51:14 +08:00
Mr Chen 17ef460f4c update 2018-12-13 13:34:06 +08:00
Mr Chen 38be955738 add debug 2018-12-13 12:56:06 +08:00
Mr Chen daf5c7c2b0 Update pick_filter.txt 2018-12-13 12:51:22 +08:00
Mr Chen acdb35f395 Update install.sh 2018-12-13 11:14:54 +08:00
midoks 5e8a15c230 update 2018-12-13 01:28:54 +08:00
midoks 34bcdd915c Update setting.py 2018-12-13 01:14:25 +08:00
midoks aa718adfea Update setting.py 2018-12-13 01:10:07 +08:00
midoks 936592661b update 2018-12-13 01:09:38 +08:00
midoks c740b4affb Update config.py 2018-12-13 00:54:13 +08:00
midoks 042b07efa2 Update csvn.js 2018-12-13 00:49:11 +08:00
midoks 6a6d22fd2d Update index.py 2018-12-12 23:37:11 +08:00
midoks f99502d2c8 Update config.py 2018-12-12 21:57:24 +08:00
midoks 1cf41348b7 Update config.py 2018-12-12 21:40:24 +08:00
midoks acdb226df9 update 2018-12-12 21:35:04 +08:00
Mr Chen 6259f21196 update 2018-12-12 19:30:42 +08:00
Mr Chen 6ad1292003 Update task.py 2018-12-12 19:26:28 +08:00
Mr Chen a682ba3e43 Update mw.tpl 2018-12-12 19:21:44 +08:00
Mr Chen 7509b259e7 Update setting.py 2018-12-12 18:47:42 +08:00
Mr Chen 8d170d87e4 update 2018-12-12 18:45:33 +08:00
Mr Chen bc4e5f6a87 update 2018-12-12 18:06:02 +08:00
Mr Chen 954f66c8dc update 2018-12-12 15:11:55 +08:00
Mr Chen a7f860de2b Update index.py 2018-12-12 14:06:10 +08:00
Mr Chen 6e7e291da3 update 2018-12-12 13:57:43 +08:00
Mr Chen 2bca78c32d update 2018-12-12 10:31:01 +08:00
midoks fb4311cdbf update 2018-12-12 00:07:01 +08:00
Mr Chen d889bf2cf3 Update index.py 2018-12-11 19:51:01 +08:00
Mr Chen 511a7ac12b Update index.py 2018-12-11 19:30:02 +08:00
Mr Chen 03e126cb48 Update index.py 2018-12-11 17:56:05 +08:00
Mr Chen 2acbe56e21 update 2018-12-11 17:53:37 +08:00
Mr Chen 8425aa4942 update 2018-12-11 14:49:26 +08:00
Mr Chen 17ad9abe6b update 2018-12-11 14:25:12 +08:00
Mr Chen 11166bbab1 Update files.py 2018-12-11 13:54:50 +08:00
Mr Chen f5f3f7ded5 update 2018-12-11 13:46:27 +08:00
Mr Chen 26bae283be update 2018-12-11 13:29:27 +08:00
Mr Chen bfb12692a6 Update csvn.js 2018-12-11 11:01:28 +08:00
Mr Chen 6bc6f17783 Update csvn.js 2018-12-11 10:37:45 +08:00
Mr Chen cd6f86ebfa Update csvn.js 2018-12-11 10:34:20 +08:00
Mr Chen 5abb2613a1 update 2018-12-11 10:31:53 +08:00
midoks a08dee242a Update csvn.js 2018-12-11 01:17:09 +08:00
midoks 2700b6b67b Update csvn.js 2018-12-11 01:15:09 +08:00
midoks e41148d72d Update index.py 2018-12-11 00:46:36 +08:00
midoks 9b5c80bc72 Update csvn.js 2018-12-11 00:44:29 +08:00
midoks 958755d18d update 2018-12-11 00:28:09 +08:00
Mr Chen 9af7e0f95b up 2018-12-10 19:37:38 +08:00
Mr Chen 90e86cda0d update 2018-12-10 19:17:40 +08:00
Mr Chen ea2ed9d003 Update crontab.html 2018-12-10 17:35:46 +08:00
Mr Chen 67eb3590c7 update 2018-12-10 17:27:09 +08:00
Mr Chen e66afbbb2d Update index.py 2018-12-10 16:41:01 +08:00
Mr Chen f959391b85 Create mw.sh 2018-12-10 16:29:04 +08:00
Mr Chen 663e95288a update 2018-12-10 16:18:35 +08:00
Mr Chen 0af3c7622d update 2018-12-10 15:53:06 +08:00
Mr Chen 40e2186a5b update 2018-12-10 15:32:31 +08:00
Mr Chen 121c972951 优化显示 2018-12-10 15:10:07 +08:00
Mr Chen 229cb977a1 update 2018-12-10 14:22:22 +08:00
Mr Chen ae845a702c u 2018-12-10 13:12:33 +08:00
Mr Chen 35b0b1d153 Update public.js 2018-12-10 11:43:20 +08:00
Mr Chen 4801850373 openresty error_logs 2018-12-10 11:42:13 +08:00
Mr Chen 8b3d0cbba4 update 2018-12-10 10:45:30 +08:00
Mr Chen 4f580d0427 update 2018-12-10 10:44:54 +08:00
Mr Chen 2f794c5b73 update 2018-12-10 10:33:03 +08:00
midoks 21cd34d272 Update index.html 2018-12-09 23:30:57 +08:00
midoks c58e1f7278 Update index.py 2018-12-09 23:30:54 +08:00
midoks 5b49f24a8d Update php56.tpl 2018-12-09 23:30:52 +08:00
midoks ce89a2a275 Update php.js 2018-12-09 23:30:50 +08:00
midoks 35f905e94c Update public.js 2018-12-09 23:30:46 +08:00
midoks 1c431286e4 Update index.py 2018-12-09 21:23:56 +08:00
midoks 705cef730b Update public.js 2018-12-09 20:33:16 +08:00
midoks 2a5da5593b Update soft.js 2018-12-09 20:33:14 +08:00
midoks 22692d8ed4 Update index.py 2018-12-09 20:31:39 +08:00
midoks 5cdae707e9 Create php56.tpl 2018-12-09 20:31:31 +08:00
midoks 5be9f1ae66 Update public.js 2018-12-09 20:31:28 +08:00
midoks ba931a0c2a Update index.py 2018-12-09 19:10:14 +08:00
midoks 5b87572bff up 2018-12-09 18:58:41 +08:00
midoks d51de312ac Update index.py 2018-12-09 14:07:13 +08:00
midoks bcd2e7298b update 2018-12-09 13:37:52 +08:00
midoks a985511b3b Update index.py 2018-12-09 13:28:36 +08:00
midoks abb9fb72f0 Update index.py 2018-12-09 13:23:37 +08:00
midoks b36ad3a595 Update index.py 2018-12-09 13:20:05 +08:00
midoks a118f33f57 Update index.py 2018-12-09 13:17:15 +08:00
midoks 58b888edcd Update index.py 2018-12-09 13:06:15 +08:00
midoks 43d906139e Update index.py 2018-12-09 13:00:18 +08:00
midoks b91bc040d4 Update index.py 2018-12-09 12:59:36 +08:00
midoks c6eba5bbf1 Update index.py 2018-12-09 12:52:41 +08:00
midoks 1adbf733d8 Update index.py 2018-12-09 12:50:30 +08:00
midoks 913d483e63 Update index.py 2018-12-09 12:40:43 +08:00
midoks 9951f7151e Update index.py 2018-12-09 12:37:49 +08:00
midoks 167a386a0d update 2018-12-09 11:59:00 +08:00
midoks 5473ee74cb Update install.sh 2018-12-09 11:43:57 +08:00
midoks dbeefc06c8 update 2018-12-09 11:42:11 +08:00
midoks 444730e553 Update install.sh 2018-12-09 11:36:18 +08:00
midoks 8a2e17198e Update index.py 2018-12-09 11:16:32 +08:00
midoks bc41788d49 u 2018-12-08 23:26:34 +08:00
midoks ebd8e93b16 update 2018-12-08 23:02:58 +08:00
midoks a45994c3c7 update 2018-12-08 22:33:32 +08:00
midoks f41f616daf update 2018-12-08 17:30:58 +08:00
midoks da5b9351e2 Update install.sh 2018-12-08 17:19:17 +08:00
midoks 3c080e0264 Update index.py 2018-12-08 17:09:36 +08:00
midoks dc62aabfd4 Update index.html 2018-12-08 17:09:33 +08:00
midoks c6da2e56ee Create csvn.js 2018-12-08 17:09:30 +08:00
midoks 461f3dcb0a Update index.html 2018-12-08 17:09:25 +08:00
midoks ee5b9f35ad Update soft.js 2018-12-08 17:07:51 +08:00
midoks 3ad8496daf Update public.js 2018-12-08 16:49:37 +08:00
midoks 77023263a6 Update index.py 2018-12-08 16:25:45 +08:00
midoks 6497627931 update 2018-12-08 16:24:36 +08:00
midoks 2ae3426c82 Update index.py 2018-12-08 15:47:40 +08:00
midoks dbeb919f05 update 2018-12-08 14:43:32 +08:00
midoks 2106746f6c Delete phpfpm_status.conf 2018-12-08 14:43:21 +08:00
midoks 31d5b90670 公共js 2018-12-08 14:43:17 +08:00
midoks ceaf9c42db update 2018-12-08 12:42:02 +08:00
Mr Chen 9067778bf3 u 2018-12-07 19:12:18 +08:00
Mr Chen 99053b67ce 调整 2018-12-07 18:47:33 +08:00
Mr Chen 07b8d91f31 redis调整ok 2018-12-07 17:44:32 +08:00
Mr Chen e9d4e09049 Update index.py 2018-12-07 16:44:08 +08:00
Mr Chen 629416760f Update install.sh 2018-12-07 15:55:12 +08:00
Mr Chen d1ade4355e Update index.py 2018-12-07 15:52:40 +08:00
Mr Chen 4508e30ea7 加入自启动选项功能
1.memcached 调整ok
2018-12-07 15:49:59 +08:00
Mr Chen b1c5c90bef update 2018-12-07 14:18:30 +08:00
midoks e9a7db2c1d update 2018-12-06 23:29:41 +08:00
Mr Chen 63e5f26491 u 2018-12-06 19:49:54 +08:00
Mr Chen ed93ccc0ee Update plugin_api.py 2018-12-06 15:41:57 +08:00
midoks 929a50c137 up 2018-12-06 00:09:25 +08:00
Mr Chen fe87092750 Update soft.js 2018-12-05 20:26:38 +08:00
Mr Chen c144cead18 u 2018-12-05 20:15:00 +08:00
Mr Chen afdc9720fc u 2018-12-05 18:46:45 +08:00
Mr Chen 194c7b9d45 Delete control.conf 2018-12-05 18:45:57 +08:00
Mr Chen 162557230a u 2018-12-05 17:35:24 +08:00
Mr Chen f4dfe502cb u 2018-12-05 17:34:32 +08:00
Mr Chen 7a3d49a9cf u 2018-12-05 17:04:20 +08:00
Mr Chen 49f1407c42 u 2018-12-05 16:24:39 +08:00
Mr Chen 319f72865d Update install.sh 2018-12-05 15:13:28 +08:00
Mr Chen 0eaa6f38e0 update 2018-12-05 14:56:50 +08:00
Mr Chen eeef8d488c Update info.json 2018-12-05 14:20:34 +08:00
Mr Chen 4e69f9540a u 2018-12-05 14:12:10 +08:00
Mr Chen a0e8d75af7 u 2018-12-05 14:00:31 +08:00
Mr Chen f54f7d4ce3 Update soft.js 2018-12-05 11:22:38 +08:00
Mr Chen 5f4893c115 u 2018-12-05 11:18:31 +08:00
Mr Chen a48b869fe9 abkill 2018-12-05 11:15:06 +08:00
Mr Chen 266a8cf806 up 2018-12-05 11:09:48 +08:00
Mr Chen 13e4dcf8ad u 2018-12-05 11:01:25 +08:00
Mr Chen 1ca469518d csvn update 2018-12-05 10:36:41 +08:00
midoks 4284a3daf4 Update site.html 2018-12-04 23:35:33 +08:00
midoks 84995650ee Update task.py 2018-12-04 23:16:47 +08:00
midoks fa662d75d6 update 2018-12-04 23:15:00 +08:00
Mr Chen 842c0daf70 up 2018-12-04 19:38:27 +08:00
Mr Chen 798608287e Update info.json 2018-12-04 19:22:10 +08:00
Mr Chen 437225374e u 2018-12-04 19:20:54 +08:00
Mr Chen 3a442570db Update system_api.py 2018-12-04 19:19:26 +08:00
Mr Chen 366c3677c2 u 2018-12-04 18:55:42 +08:00
Mr Chen 6dcf1db856 u 2018-12-04 18:55:27 +08:00
Mr Chen b3ca295e51 Update info.json 2018-12-04 13:59:48 +08:00
Mr Chen 03f06ee384 Update .gitignore 2018-12-04 13:56:47 +08:00
Mr Chen 2dc96cd04e u 2018-12-04 13:38:08 +08:00
Mr Chen a875293116 u 2018-12-04 13:26:58 +08:00
Mr Chen 8735fb9238 u 2018-12-04 13:22:22 +08:00
Mr Chen 58fcf0e8f9 Update install.sh 2018-12-04 13:13:36 +08:00
Mr Chen fccc30ebb1 Update system_api.py 2018-12-04 13:05:15 +08:00
Mr Chen 6b9da57ff2 u 2018-12-03 19:37:46 +08:00
Mr Chen cd6b2ad65e 代码打包 2018-12-03 18:22:04 +08:00
Mr Chen 60b2834adf Update system_api.py 2018-12-03 18:03:28 +08:00
Mr Chen c74e0428fd Update info.json 2018-12-03 17:57:08 +08:00
Mr Chen 20d486629e u 2018-12-03 17:52:11 +08:00
Mr Chen f984e4ecc7 u 2018-12-03 17:50:43 +08:00
Mr Chen 0bdea14560 Update .gitignore 2018-12-03 17:24:57 +08:00
Mr Chen 0c3599facb u 2018-12-03 17:24:49 +08:00
Mr Chen 05eb434bf8 Update system_api.py 2018-12-03 16:44:35 +08:00
Mr Chen 12dccc1df0 u 2018-12-03 16:44:29 +08:00
Mr Chen 11e7316911 Update info.json 2018-12-03 14:24:40 +08:00
Mr Chen 26a6a80504 u 2018-12-03 13:52:12 +08:00
Mr Chen 498799578a u 2018-12-03 13:44:20 +08:00
Mr Chen 6a5b009766 u 2018-12-03 13:39:06 +08:00
Mr Chen 8e23411be8 u 2018-12-03 13:11:43 +08:00
Mr Chen b597255d68 u 2018-12-03 12:49:03 +08:00
Mr Chen 8979011ec3 u 2018-12-03 10:58:41 +08:00
midoks 11a21f9a8b Update crontab.html 2018-12-02 23:27:47 +08:00
midoks 1a12f625cd up 2018-12-02 22:58:14 +08:00
midoks 11e44d5b0e 监控完成 2018-12-02 21:59:33 +08:00
midoks 47c1394de6 u 2018-12-02 12:58:59 +08:00
midoks 5e7fa28feb Update task.py 2018-12-01 18:04:59 +08:00
midoks d04f891ee7 up 2018-12-01 18:02:30 +08:00
midoks 3d28e0a3cb u 2018-12-01 17:20:10 +08:00
Mr Chen 37112546b9 Delete system.db 2018-11-30 19:13:20 +08:00
Mr Chen cb719c9973 Update .gitignore 2018-11-30 19:13:01 +08:00
Mr Chen 7ec5fe84b2 update 2018-11-30 19:12:16 +08:00
Mr Chen c68cce0198 up 2018-11-30 17:39:38 +08:00
Mr Chen 0d7fd11de5 up 2018-11-30 16:58:50 +08:00
Mr Chen 643a2e8cf8 u 2018-11-30 14:54:06 +08:00
midoks 35ee7d3d10 优化 2018-11-30 02:03:05 +08:00
midoks 2ce3871ee2 Update .gitignore 2018-11-29 21:04:39 +08:00
midoks 812ce86187 u 2018-11-29 21:04:13 +08:00
Mr Chen 3d44b3014f u 2018-11-29 19:16:12 +08:00
Mr Chen 092494a216 Update soft.js 2018-11-29 19:09:27 +08:00
Mr Chen a21ad254b7 Update soft.js 2018-11-29 19:07:28 +08:00
Mr Chen ebd6b9f389 u 2018-11-29 18:19:18 +08:00
midoks 33b90a976b 版本显示优化 2018-11-28 23:56:10 +08:00
midoks 2518d32019 wd 2018-11-28 21:35:26 +08:00
Mr Chen 4ded4679f3 up 2018-11-28 19:11:55 +08:00
midoks 28d3996d17 openresty 2018-11-27 23:29:28 +08:00
midoks b76950ed3b php 72 2018-11-27 23:05:01 +08:00
Mr Chen d49aa168eb u 2018-11-27 19:20:46 +08:00
Mr Chen 5304d0b228 u 2018-11-27 14:37:24 +08:00
Mr Chen 56e4ef1a4e u 2018-11-27 13:17:06 +08:00
Mr Chen 91fc83e0ba a 2018-11-26 19:18:50 +08:00
Mr Chen fc0fdd5772 u 2018-11-26 16:08:19 +08:00
Mr Chen 0d0007d9a1 u 2018-11-26 15:57:49 +08:00
Mr Chen a74ace856a u 2018-11-26 15:54:42 +08:00
Mr Chen f46af799a9 代码调整 2018-11-26 14:35:52 +08:00
Mr Chen 3e4facbf57 u 2018-11-26 13:57:06 +08:00
Mr Chen ff9466d0a4 wd 2018-11-26 11:51:27 +08:00
Mr Chen 9cbcff5dbc 优化 2018-11-26 10:55:53 +08:00
midoks 90834415bf update 2018-11-25 23:30:19 +08:00
midoks c2862ab5d4 Update index.js 2018-11-25 18:11:30 +08:00
midoks 664a7864bc 首页界面更新 2018-11-25 18:05:39 +08:00
midoks 9cf6a40493 update 2018-11-25 17:48:48 +08:00
midoks 028422337e mem 2018-11-25 16:46:23 +08:00
midoks 5f10d9433c redis 设置 2018-11-25 16:13:59 +08:00
Mr Chen ed49f0b0bd Update redis.js 2018-11-23 19:14:18 +08:00
Mr Chen 090fc0f243 redis文件修改 2018-11-23 15:02:02 +08:00
Mr Chen f2df81694f Update install.sh 2018-11-23 11:45:53 +08:00
Mr Chen 6cf2e2d6b6 wd 2018-11-23 11:39:15 +08:00
Mr Chen defdc3d3b8 Update README.md 2018-11-23 10:09:19 +08:00
Mr Chen 6d1d1b882e Update install.sh 2018-11-22 18:58:46 +08:00
Mr Chen 54f90c9300 Update install.sh 2018-11-22 18:56:28 +08:00
Mr Chen a1aa7d465d Update README.md 2018-11-22 18:49:41 +08:00
Mr Chen 62214c2a88 Update plugins.py 2018-11-22 18:44:19 +08:00
Mr Chen d5a4958566 wd 2018-11-22 17:47:19 +08:00
Mr Chen 0bca4a3589 wd 2018-11-22 17:08:26 +08:00
Mr Chen 30bc8b1ec1 u 2018-11-21 19:27:26 +08:00
Mr Chen 0472102a3a u 2018-11-21 19:15:37 +08:00
Mr Chen 2da863d98a u 2018-11-21 18:18:17 +08:00
Mr Chen 88bab4b15b wq 2018-11-21 10:15:35 +08:00
Mr Chen d1b4a1cde1 up 2018-11-20 19:11:09 +08:00
Mr Chen d88f6903da Update task.py 2018-11-20 18:54:29 +08:00
Mr Chen 3a53b360b7 o 2018-11-20 17:43:29 +08:00
Mr Chen 3e5b951a41 wd 2018-11-20 14:01:17 +08:00
Mr Chen 272b6fdeea up 2018-11-19 13:32:17 +08:00
Mr Chen 49ac781e87 wq 2018-11-13 14:18:06 +08:00
Mr Chen b4f9fa0976 Update route/dashboard.py 2018-11-12 17:24:59 +08:00
Mr Chen 96baa99bdc wq 2018-11-12 17:22:47 +08:00
Mr Chen 5db4f39c76 o 2018-11-12 14:43:14 +08:00
Mr Chen 23dadd7f6f wq 2018-11-12 14:38:35 +08:00
Mr Chen 7d8d48f3d5 wq 2018-11-12 12:55:17 +08:00
Mr Chen 74fd4bbe08 o 2018-11-12 11:41:00 +08:00
midoks 30240443e6 update 2018-11-11 23:51:11 +08:00
Mr Chen e069c3a24e w 2018-11-09 16:41:11 +08:00
Mr Chen 73e75abb5f w 2018-11-09 16:23:17 +08:00
Mr Chen b35d516a8e o 2018-11-09 15:46:28 +08:00
Mr Chen aa1875612b Update templates/default/layout.html 2018-11-09 11:10:33 +08:00
Mr Chen b189d5b485 Update templates/default/layout.html 2018-11-09 11:10:11 +08:00
Mr Chen 33b43b253c Update templates/default/layout.html 2018-11-09 11:09:47 +08:00
midoks 86cfb1b9cf q 2018-11-08 22:56:12 +08:00
Mr Chen f93afb772c Update templates/default/login.html 2018-11-08 17:59:31 +08:00
Mr Chen bd49bd3004 Update scripts/install.sh 2018-11-08 17:54:27 +08:00
Mr Chen db26b09eba q 2018-11-08 17:38:15 +08:00
Mr Chen 85da65ad1e Update templates/default/login.html 2018-11-08 17:24:44 +08:00
Mr Chen d9a409ffcc Update app.py 2018-11-08 17:22:03 +08:00
Mr Chen ea923a45a6 q 2018-11-08 17:19:13 +08:00
Mr Chen 7480cfb5d8 Update static/css/install.css 2018-11-08 17:18:59 +08:00
Mr Chen be513af18e q 2018-11-08 17:09:25 +08:00
Mr Chen 1c5e61a3de q 2018-11-08 17:09:06 +08:00
Mr Chen d26474f222 a 2018-11-08 17:08:41 +08:00
Mr Chen 03c796775a Update static/js/jquery.qrcode.min.js 2018-11-08 17:06:06 +08:00
Mr Chen 182d434f84 d 2018-11-08 17:04:57 +08:00
Mr Chen 7c1d409e61 o 2018-11-08 16:53:01 +08:00
midoks dab12535f2 Update templates/default/login.html 2018-11-08 00:02:08 +08:00
midoks deffb3d61b Update route/dashboard.py 2018-11-08 00:02:05 +08:00
Mr Chen a4a45f2005 Update route/files.py 2018-11-07 18:45:34 +08:00
Mr Chen 32d6a89bd4 Update route/dashboard.py 2018-11-07 18:28:48 +08:00
Mr Chen f41c2454ee u 2018-11-07 18:13:05 +08:00
midoks 76dba077c0 o 2018-11-06 23:45:28 +08:00
Mr Chen 1c34488abb Update templates/default/login.html 2018-11-06 19:10:33 +08:00
Mr Chen 6742d971eb u 2018-11-06 13:42:24 +08:00
Mr Chen 3de01da4ba Update models/index.html 2018-11-06 13:23:16 +08:00
midoks 4ba526ca2b up 2018-11-06 00:28:02 +08:00
midoks a4e8b6a137 u 2018-11-05 23:47:37 +08:00
midoks 50ad3ed7e7 ww 2018-11-05 23:42:01 +08:00
Mr Chen 64d29659f7 u 2018-11-05 19:25:07 +08:00
Mr Chen 2794f49df4 u 2018-11-05 19:05:33 +08:00
Mr Chen 3e27f85719 u 2018-11-05 17:19:02 +08:00
Mr Chen a4d70f2294 up 2018-11-05 16:49:37 +08:00
midoks f3cd1f597e up 2018-11-04 23:37:33 +08:00
midoks 489c363daf u 2018-11-04 23:20:06 +08:00
midoks 77f44481b3 up 2018-11-04 23:02:45 +08:00
midoks fcd4ba024a u 2018-11-03 18:19:47 +08:00
Mr Chen ce6b15939d dd 2018-11-02 14:43:41 +08:00
Mr Chen baaccb7960 u 2018-11-02 13:38:20 +08:00
Mr Chen fb559e9f11 u 2018-11-02 10:18:58 +08:00
Mr Chen 283a2c052b u 2018-10-29 20:19:59 +08:00
Mr Chen a7bd9e3290 csvn dev 2018-10-29 14:10:41 +08:00
Mr Chen b64dbb9aa4 Update data/system.db 2018-10-26 16:56:15 +08:00
Mr Chen d9f63c3a9a u 2018-10-26 16:53:20 +08:00
Mr Chen 3c02af41bc u 2018-10-26 15:54:23 +08:00
Mr Chen ac87a6c8b1 Update app.py 2018-10-26 15:02:57 +08:00
Mr Chen f19dd2a2b9 u 2018-10-26 15:01:33 +08:00
Mr Chen cef7215fdc u 2018-10-26 13:53:39 +08:00
Mr Chen edb4fc96e1 u 2018-10-26 13:17:19 +08:00
midoks af9f8b73ba 修改模板 2018-10-25 23:23:37 +08:00
Mr Chen c85152e85a update 2018-10-25 19:32:21 +08:00
Mr Chen 2ee2440c68 u 2018-10-25 17:33:34 +08:00
Mr Chen ecfafc9723 u 2018-10-25 16:40:45 +08:00
Mr Chen dbb4414e65 Update .idea/vcs.xml 2018-10-24 17:27:45 +08:00
midoks a028e93881 u 2018-10-17 22:57:37 +08:00
midoks 475cb5176d u 2018-10-17 00:29:33 +08:00
midoks 8b07bd80e6 u 2018-10-17 00:17:59 +08:00
midoks 50d26d932f Update templates/default/config.html 2018-10-17 00:08:44 +08:00
midoks 22b5ab1179 Update del_pyc.sh 2018-10-14 20:55:05 +08:00
midoks f7c76fb261 画 2018-10-14 20:49:20 +08:00
midoks 6531f686ad Update templates/default/database.html 2018-10-14 18:20:27 +08:00
midoks b716f05fb4 u 2018-10-14 17:37:43 +08:00
midoks 9a98f1c859 u 2018-10-14 12:47:54 +08:00
midoks e92fc4cfd1 u 2018-10-14 11:49:24 +08:00
midoks da1a76cbef u 2018-10-13 22:54:11 +08:00
Mr Chen fa6b7c21ac u 2018-10-12 19:09:54 +08:00
Mr Chen 11748fd33b Update plugins/deployment/icon.png 2018-10-12 18:46:22 +08:00
midoks 267bbe3865 u 2018-10-12 10:35:29 +08:00
midoks dd27e4b076 Update class/page.py 2018-10-12 10:28:52 +08:00
Mr Chen 459229ba15 Update plugins/nginx/install.sh 2018-10-11 19:30:01 +08:00
Mr Chen 94c5161fde u 2018-10-11 19:26:06 +08:00
Mr Chen 5646d1404c u 2018-10-11 09:55:27 +08:00
Mr Chen ef8a10b68d Update views/plugins.py 2018-10-09 19:53:03 +08:00
Mr Chen c772696f3e Update views/plugins.py 2018-10-09 19:51:23 +08:00
Mr Chen 45faf4e9dd Update views/plugins.py 2018-10-08 20:10:44 +08:00
Mr Chen 5cbca9232c Update app.py 2018-10-08 11:52:55 +08:00
midoks 742bb6d86d u 2018-10-07 23:51:22 +08:00
midoks b14accd83e u 2018-10-07 20:54:10 +08:00
midoks 8ae01d8d36 u 2018-10-07 20:38:30 +08:00
midoks 5db1e58e6a u 2018-10-07 18:51:52 +08:00
midoks a2e6d2f280 u 2018-10-07 18:25:51 +08:00
midoks 83f3d224ed d 2018-10-07 17:57:08 +08:00
midoks 73693620df u 2018-10-07 17:42:46 +08:00
midoks 72eb67dfda u 2018-10-07 16:29:42 +08:00
midoks c55b4a02c8 插件 2018-10-02 21:50:10 +08:00
midoks 721efd6a16 up 2018-10-02 18:00:48 +08:00
midoks 61c8ce4b6f up 2018-10-02 17:24:26 +08:00
midoks ce95ca6905 up 2018-10-02 16:59:56 +08:00
Mr Chen 01dee5fe1f up 2018-09-26 20:03:12 +08:00
Mr Chen 1a72edee9a 更新 2018-09-26 20:02:11 +08:00
Mr Chen 27b6ad0280 up 2018-09-26 14:59:06 +08:00
Mr Chen ae9e6921c3 up 2018-09-26 13:32:57 +08:00
Mr Chen b51eb99027 up 2018-09-25 18:43:26 +08:00
Mr Chen 2e584c540d up 2018-09-25 17:01:20 +08:00
Mr Chen bbf258e1a7 up 2018-09-25 16:37:56 +08:00
Mr Chen 53d57716aa o 2018-09-25 14:16:10 +08:00
Mr Chen e9a6e5477d up 2018-09-25 13:59:20 +08:00
Mr Chen 46ca8a6b44 up 2018-09-25 12:50:06 +08:00
Mr Chen d6f88caf3d up 2018-09-21 14:18:29 +08:00
midoks abc85902a2 up 2018-09-15 16:45:14 +08:00
Mr Chen 9edc8bbea2 up 2018-09-14 19:18:36 +08:00
Mr Chen 443d3efd37 up 2018-09-14 15:23:04 +08:00
midoks 948bcde358 模版 2018-09-13 22:30:37 +08:00
midoks 83aa3cfca6 up 2018-09-13 22:26:34 +08:00
Mr Chen 976c88ed44 Delete .DS_Store 2018-09-13 22:24:06 +08:00
midoks 0db16797b5 up 2018-09-13 22:23:05 +08:00
midoks b7802fc898 更新说明 2018-09-13 22:22:23 +08:00
midoks 741dffd5db up 2018-09-13 22:20:52 +08:00
Mr Chen 2c9f11bcfe static 2018-09-13 13:36:11 +08:00
midoks 16893e51e6 up 2018-09-11 23:02:52 +08:00
midoks 34a43a6af9 up 2018-09-11 22:42:43 +08:00
midoks 4dd5d946d9 up 2018-09-11 22:39:22 +08:00
Mr Chen 7e63f686b9 up 2018-09-11 16:27:09 +08:00
Mr Chen 7b57972179 up 2018-09-11 14:50:44 +08:00
Mr Chen a758a36bdc test 2018-09-11 14:48:31 +08:00
Mr Chen 84123c603d Create README.md 2018-09-11 14:44:23 +08:00
Mr Chen 33f9c09a07 Initial commit 2018-09-11 14:44:15 +08:00
1922 changed files with 20585 additions and 229847 deletions
-5
View File
@@ -1,5 +0,0 @@
# These are supported funding model platforms
github: midoks
custom: https://afdian.net/a/mdserver-web
@@ -1,29 +0,0 @@
---
name: BUG提交
about: 问题描述
---
## 什么系统 ?
Debian x, Centos x, Centos x stream, Ubuntu x?
## 错误信息 ?
Ex:
```bash
一个issue,一个问题。多余问题,视情况而答。
```
## 错误截图 ?
...
## 浏览器版本 ?
...
## 软件版本(插件/面板) ?
...
@@ -1,14 +0,0 @@
---
name: 需求提交
about: 需求描述
---
## 需求
想开发一个什么插件?
想有什么功能?
## 提供参考
给开发者提供参考!
-24
View File
@@ -1,24 +0,0 @@
name: Bug提交
description: 提交错误报告以帮助我们改进
labels: ["\U0001F48A bug"]
body:
- type: markdown
attributes:
value: |
感谢您花时间填写此错误报告!
- 如有疑问,请讨论[Discussions](https://github.com/midoks/mdserver-web/discussions).
- 检查以确保尚未有人打开类似的[issue](https://github.com/midoks/mdserver-web/issues).
- type: input
attributes:
label: 操作系统
description: |
请指定您报告的确切操作系统名称和版本,例如"CentOS 7"、"Ubuntu 20.04"。
validations:
required: true
- type: textarea
attributes:
label: 描述错误
description: 对bug的清晰而简洁的描述。
validations:
required: true
-8
View File
@@ -1,8 +0,0 @@
blank_issues_enabled: true
contact_links:
- name: 提出问题
url: https://github.com/midoks/mdserver-web/discussions
about: 请在讨论中提出问题。
- name: 提出建议
url: https://github.com/midoks/mdserver-web/discussions/categories/proposal
about: 请在讨论中提出建议。
-24
View File
@@ -1,24 +0,0 @@
name: Bug提交
description: 提交错误报告以帮助我们改进
labels: ["\U0001F48A bug"]
body:
- type: markdown
attributes:
value: |
感谢您花时间填写此错误报告!
- 如有疑问,请讨论[Discussions](https://github.com/midoks/mdserver-web/discussions).
- 检查以确保尚未有人打开类似的[issue](https://github.com/midoks/mdserver-web/issues).
- type: input
attributes:
label: 操作系统
description: |
请指定您报告的确切操作系统名称和版本,例如"CentOS 7"、"Ubuntu 20.04"。
validations:
required: true
- type: textarea
attributes:
label: 描述错误
description: 对bug的清晰而简洁的描述。
validations:
required: true
@@ -1,24 +0,0 @@
name: Bug提交
description: 提交错误报告以帮助我们改进
labels: ["\U0001F48A bug"]
body:
- type: markdown
attributes:
value: |
感谢您花时间填写此错误报告!
- 如有疑问,请讨论[Discussions](https://github.com/midoks/mdserver-web/discussions).
- 检查以确保尚未有人打开类似的[issue](https://github.com/midoks/mdserver-web/issues).
- type: input
attributes:
label: 操作系统
description: |
请指定您报告的确切操作系统名称和版本,例如"CentOS 7"、"Ubuntu 20.04"。
validations:
required: true
- type: textarea
attributes:
label: 描述错误
description: 对bug的清晰而简洁的描述。
validations:
required: true
-3
View File
@@ -1,3 +0,0 @@
### 合并描述
- 我添加了测试用例来覆盖新代码。
-38
View File
@@ -1,38 +0,0 @@
# This workflow will install Python dependencies, run tests and lint with a single version of Python
# For more information see: https://help.github.com/actions/language-and-framework-guides/using-python-with-github-actions
name: MW APP MySQL55
on:
push:
branches: [ "run_test" ]
pull_request:
branches: [ "run_test" ]
permissions:
contents: read
jobs:
build:
strategy:
matrix:
os: [ubuntu-20.04, ubuntu-18.04, ubuntu-22.04]
runs-on: ${{ matrix.os }}
#runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Set up Python 3.10
uses: actions/setup-python@v3
with:
python-version: "3.10"
- name: Install MW
run: |
sudo bash scripts/install_dev.sh
- name: Install MySQL55
run: |
cd /www/server/mdserver-web/plugins/mysql && sudo bash install.sh install 5.5
- name: Start DEBUG
run: |
cd web && gunicorn -c setting.py app:app
python3 panel_task.py &
-38
View File
@@ -1,38 +0,0 @@
# This workflow will install Python dependencies, run tests and lint with a single version of Python
# For more information see: https://help.github.com/actions/language-and-framework-guides/using-python-with-github-actions
name: MW APP MySQL56
on:
push:
branches: [ "run_test" ]
pull_request:
branches: [ "run_test" ]
permissions:
contents: read
jobs:
build:
strategy:
matrix:
os: [ubuntu-20.04, ubuntu-18.04, ubuntu-22.04]
runs-on: ${{ matrix.os }}
#runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Set up Python 3.10
uses: actions/setup-python@v3
with:
python-version: "3.10"
- name: Install MW
run: |
sudo bash scripts/install_dev.sh
- name: Install MySQL56
run: |
cd /www/server/mdserver-web/plugins/mysql && sudo bash install.sh install 5.6
- name: Start DEBUG
run: |
cd web && gunicorn -c setting.py app:app
python3 panel_task.py &
-38
View File
@@ -1,38 +0,0 @@
# This workflow will install Python dependencies, run tests and lint with a single version of Python
# For more information see: https://help.github.com/actions/language-and-framework-guides/using-python-with-github-actions
name: MW APP MySQL57
on:
push:
branches: [ "run_test" ]
pull_request:
branches: [ "run_test" ]
permissions:
contents: read
jobs:
build:
strategy:
matrix:
os: [ubuntu-20.04, ubuntu-18.04, ubuntu-22.04]
runs-on: ${{ matrix.os }}
#runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Set up Python 3.10
uses: actions/setup-python@v3
with:
python-version: "3.10"
- name: Install MW
run: |
sudo bash scripts/install_dev.sh
- name: Install MySQL57
run: |
cd /www/server/mdserver-web/plugins/mysql && sudo bash install.sh install 5.7
- name: Start DEBUG
run: |
cd web && gunicorn -c setting.py app:app
python3 panel_task.py &
-38
View File
@@ -1,38 +0,0 @@
# This workflow will install Python dependencies, run tests and lint with a single version of Python
# For more information see: https://help.github.com/actions/language-and-framework-guides/using-python-with-github-actions
name: MW APP MySQL80
on:
push:
branches: [ "run_test" ]
pull_request:
branches: [ "run_test" ]
permissions:
contents: read
jobs:
build:
strategy:
matrix:
os: [ubuntu-20.04, ubuntu-18.04, ubuntu-22.04]
runs-on: ${{ matrix.os }}
#runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Set up Python 3.10
uses: actions/setup-python@v3
with:
python-version: "3.10"
- name: Install MW
run: |
sudo bash scripts/install_dev.sh
- name: Install MySQL80
run: |
cd /www/server/mdserver-web/plugins/mysql && sudo bash install.sh install 8.0
- name: Start DEBUG
run: |
cd web && gunicorn -c setting.py app:app
python3 panel_task.py &
-38
View File
@@ -1,38 +0,0 @@
# This workflow will install Python dependencies, run tests and lint with a single version of Python
# For more information see: https://help.github.com/actions/language-and-framework-guides/using-python-with-github-actions
name: MW APP OpenResty
on:
push:
branches: [ "master", "dev" ]
pull_request:
branches: [ "master", "dev" ]
permissions:
contents: read
jobs:
build:
strategy:
matrix:
os: [ubuntu-latest]
runs-on: ${{ matrix.os }}
#runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Set up Python 3.10
uses: actions/setup-python@v3
with:
python-version: "3.10"
- name: Install MW
run: |
sudo bash scripts/install_dev.sh
- name: Install OpenResty
run: |
cd /www/server/mdserver-web/plugins/openresty && sudo bash install.sh install 1.27.1
- name: Start DEBUG
run: |
source /www/server/mdserver-web/bin/activate && cd web && gunicorn -c setting.py app:app
cd /www/server/mdserver-web && python3 panel_task.py &
-68
View File
@@ -1,68 +0,0 @@
# This workflow will install Python dependencies, run tests and lint with a single version of Python
# For more information see: https://help.github.com/actions/language-and-framework-guides/using-python-with-github-actions
name: MW APP PHP
on:
push:
branches: [ "run_test" ]
pull_request:
branches: [ "run_test" ]
permissions:
contents: read
jobs:
build:
strategy:
matrix:
os: [ubuntu-latest]
runs-on: ${{ matrix.os }}
#runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Set up Python 3.10
uses: actions/setup-python@v3
with:
python-version: "3.10"
- name: Install MW
run: |
sudo bash scripts/install_dev.sh
- name: Install PHP53
run: |
cd /www/server/mdserver-web/plugins/php && sudo bash install.sh install 53
- name: Install PHP54
run: |
cd /www/server/mdserver-web/plugins/php && sudo bash install.sh install 54
- name: Install PHP55
run: |
cd /www/server/mdserver-web/plugins/php && sudo bash install.sh install 55
- name: Install PHP56
run: |
cd /www/server/mdserver-web/plugins/php && sudo bash install.sh install 56
- name: Install PHP70
run: |
cd /www/server/mdserver-web/plugins/php && sudo bash install.sh install 70
- name: Install PHP71
run: |
cd /www/server/mdserver-web/plugins/php && sudo bash install.sh install 71
- name: Install PHP72
run: |
cd /www/server/mdserver-web/plugins/php && sudo bash install.sh install 72
- name: Install PHP73
run: |
cd /www/server/mdserver-web/plugins/php && sudo bash install.sh install 73
- name: Install PHP74
run: |
cd /www/server/mdserver-web/plugins/php && sudo bash install.sh install 74
- name: Install PHP80
run: |
cd /www/server/mdserver-web/plugins/php && sudo bash install.sh install 80
- name: Install PHP81
run: |
cd /www/server/mdserver-web/plugins/php && sudo bash install.sh install 81
- name: Start DEBUG
run: |
source /www/server/mdserver-web/bin/activate && cd web && gunicorn -c setting.py app:app
cd /www/server/mdserver-web && python3 panel_task.py &
-16
View File
@@ -1,16 +0,0 @@
name: sync2gitee
on: [push]
jobs:
repo-sync:
runs-on: ubuntu-latest
steps:
- name: Mirror the Github organization repos to Gitee.
uses: Yikun/hub-mirror-action@master
with:
src: 'github/midoks'
dst: 'gitee/midoks'
dst_key: ${{ secrets.GITEE_PRIVATE_KEY }}
dst_token: ${{ secrets.GITEE_TOKEN }}
static_list: "mdserver-web"
force_update: true
+5 -85
View File
@@ -8,6 +8,7 @@ __pycache__/
# Distribution / packaging
.Python
build/
develop-eggs/
dist/
downloads/
@@ -87,7 +88,6 @@ venv/
ENV/
env.bak/
venv.bak/
lib/python*
# Spyder project settings
.spyderproject
@@ -99,99 +99,19 @@ lib/python*
# mkdocs documentation
/site
bin
pyvenv.cfg
include
share
pip-selfcheck.json
# mypy
.mypy_cache/
.DS_Store
.idea/*.xml
.idea/*.iml
tmp/*
tmp/*.pl
tmp/*.log
*.swp
*.pem
*.zip
debug.out
mdioks.session-journal
*.session
temp
logs/panel.pid
logs/panel.log
logs/panel_task.log
logs/panel_exec.log
logs/panel_task.lock
ssl/local
ssl/nginx
ssl/choose.pl
scripts/init.d/mw
scripts/mdserver-web
data/api_login.txt
data/sessions
logs/*
data/*.db
data/control.conf
data/iplist.txt
data/json/index.json
data/json/config.json
data/site.pl
data/admin_path.pl
data/close.pl
data/datadir.pl
data/502Task.pl
data/default.pl
data/backup.pl
data/debug.pl
data/default_site.pl
data/ssl.pl
data/port.pl
data/ipv6.pl
data/restart.pl
data/edate.pl
data/osname.pl
data/only_netio_counters.pl
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
data/auth_secret.pl
data/panelSpeed.pl
plugins/vip_*
plugins/own_*
plugins/my_*
plugins/op_auth
plugins/l2tp
plugins/openlitespeed
plugins/tamper_proof
plugins/zimg
plugins/bk_demo
plugins/mail
plugins/fastdfs
plugins/v2ray
plugins/frp
plugins/file_search
plugins/proxysql
plugins/tidb
plugins/goedge-admin
plugins/goedge-node
plugins/goedge-happy
/logs
/data
/plugins/flexcdn
/plugins/tools
/plugins/choose-linux-python
*.md5
+1 -1
View File
@@ -186,7 +186,7 @@
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [midoks] [midoks of copyright owner]
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.
+5 -234
View File
@@ -1,236 +1,7 @@
<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 align="center">强烈推荐系统:debian</p>
</p>
### 简介
简单的Linux面板,感谢BT.CN写出如此好的web管理软件。我一看到,就知道这是我一直想要的页面化管理方式。
复制了后台管理界面,按照自己想要的方式写了一版。
![EulerOS](https://img.shields.io/badge/LINUX-EulerOS-blue?style=for-the-badge&logo=EulerOS)
![Debian](https://img.shields.io/badge/LINUX-Debian-blue?style=for-the-badge&logo=Debian)
![Ubuntu](https://img.shields.io/badge/LINUX-Ubuntu-blue?style=for-the-badge&logo=Ubuntu)
![Fedora](https://img.shields.io/badge/LINUX-Fedora-blue?style=for-the-badge&logo=Fedora)
![CentOS](https://img.shields.io/badge/LINUX-CentOS-blue?style=for-the-badge&logo=CentOS)
[![Wiki](https://img.shields.io/badge/MW-Wiki-red?style=for-the-badge&logo=wiki)](https://github.com/midoks/mdserver-web/wiki)
[![jsdelivr](https://data.jsdelivr.com/v1/package/gh/midoks/mdserver-web/badge)](https://www.jsdelivr.com/package/gh/midoks/mdserver-web)
* SSH终端工具
* 面板收藏功能
* 网站备份功能
* 插件方式管理
基本上可以使用,后续会继续优化!欢迎提供意见!
- 吹水组 - https://t.me/mdserver_web
- 交流论坛 - https://bbs.midoks.icu
# mdserver-web
简单的Linux面板
### 自动安装
```
如果出现问题,最好私给我面板信息。不要让我猜。如果不提供,不要提出问题,自行解决。 — 座右铭
Talk is cheap, show me the code. -- linus
```
- [兼容性测试报告](/compatibility.md)
- [常用命令说明](/cmd.md) [ mw default ] [ mw update_dev ]
### 特别赞助
- [虚位以待](https://bbs.midoks.icu)
### 主要插件介绍
* OpenResty - 轻量级,占有内存少,并发能力强。
* PHP[53-85] - PHP是世界上最好的编程语言。
* MySQL - 一种关系数据库管理系统。
* MariaDB - 是MySQL的一个重要分支。
* MySQL[community] - 一种关系数据库管理系统。
* MongoDB - 一种非关系NOSQL数据库管理系统。
* PostgreSQL - 功能强大的开源数据库。
* 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/插件开发
## 其他插件
- OP鉴权 - https://github.com/mw-plugin/op_auth
# Note
```
phpMyAdmin[4.4.15]支持MySQL[5.5-5.7]
phpMyAdmin[5.2.1]支持MySQL[8.0+]
PHP[53-72]支持phpMyAdmin[4.4.15]
PHP[72-84]支持phpMyAdmin[5.2.1]
```
# 郑重声明
不卖、不会监控(统计使用除外)、更不会注入病毒,大家使用方便(望大家不吝捐赠)。
- https://www.youtube.com/watch?v=2taa5K-Jmmw
# AD - VPS推荐 - 🙏
| 服务商 | 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
- 由[DDS-Derek](https://github.com/DDS-Derek)开发维护。
- https://github.com/DDS-Derek/mdserver-web-Docker
```
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.18.2
- 备份目录功能支持排除目录。
- 其他细节优化。
### JSDelivr安装地址
- 初始安装
```
bash <(curl --insecure -fsSL https://cdn.jsdelivr.net/gh/midoks/mdserver-web@latest/scripts/install.sh)
```
- 直接更新
```
bash <(curl --insecure -fsSL https://cdn.jsdelivr.net/gh/midoks/mdserver-web@latest/scripts/update.sh)
```
- 卸载脚本
```
wget --no-check-certificate -O uninstall.sh https://cdn.jsdelivr.net/gh/midoks/mdserver-web@latest/scripts/uninstall.sh && bash uninstall.sh
```
### 备用地址
- 初始安装
```
bash <(curl --insecure -fsSL https://raw.githubusercontent.com/midoks/mdserver-web/dev/scripts/install.sh)
bash <(curl --insecure -fsSL https://code.midoks.icu/midoks/mdserver-web/raw/branch/master/scripts/install.sh)
```
- 直接更新
```
bash <(curl --insecure -fsSL https://raw.githubusercontent.com/midoks/mdserver-web/master/scripts/update.sh)
```
- 卸载脚本
```
wget --no-check-certificate -O uninstall.sh https://raw.githubusercontent.com/midoks/mdserver-web/master/scripts/uninstall.sh && bash uninstall.sh
```
### 通用软件安装[命令行安装]
- 需已经安装mdserver-web
```
bash <(curl --insecure -fsSL https://raw.githubusercontent.com/midoks/mdserver-web/dev/scripts/quick/app.sh)
```
### DEV使用
```
bash <(curl --insecure -fsSL https://raw.githubusercontent.com/midoks/mdserver-web/dev/scripts/install.sh)
bash <(curl --insecure -fsSL https://raw.githubusercontent.com/midoks/mdserver-web/dev/scripts/update.sh)
bash <(curl --insecure -fsSL https://raw.githubusercontent.com/midoks/mdserver-web/dev/scripts/install_dev.sh)
bash <(curl --insecure -fsSL https://raw.githubusercontent.com/midoks/mdserver-web/dev/scripts/update_dev.sh)
wget --no-check-certificate -O install.sh https://raw.githubusercontent.com/midoks/mdserver-web/dev/scripts/install.sh && bash install.sh
wget --no-check-certificate -O uninstall.sh https://raw.githubusercontent.com/midoks/mdserver-web/dev/scripts/uninstall.sh && bash uninstall.sh
bash <(curl --insecure -fsSL https://raw.githubusercontent.com/midoks/mdserver-web/dev/scripts/quick/debug.sh)
bash <(curl --insecure -fsSL https://code.midoks.icu/midoks/mdserver-web/raw/branch/dev/scripts/install.sh)
bash <(curl --insecure -fsSL https://code.midoks.icu/midoks/mdserver-web/raw/branch/dev/scripts/update.sh)
bash <(curl --insecure -fsSL https://code.midoks.icu/midoks/mdserver-web/raw/branch/dev/scripts/install_dev.sh)
bash <(curl --insecure -fsSL https://code.midoks.icu/midoks/mdserver-web/raw/branch/dev/scripts/update_dev.sh)
```
### 旧版安装/更新
```
curl --insecure -fsSL https://raw.githubusercontent.com/midoks/mdserver-web/dev/scripts/old/install.sh | bash
curl --insecure -fsSL https://raw.githubusercontent.com/midoks/mdserver-web/dev/scripts/old/update.sh | bash
```
### 偶然问题
- 在安装/更新时,突然出现python插件pip,psutil,可尝试如下解决方案
```
cd /www/server/mdserver-web rm -rf lib
cd /www/server/mdserver-web && rm -rf lib64
cd /www/server/mdserver-web && rm -rf bin
cd /www/server/mdserver-web && rm -rf include
mw update/mw update_dev/mw dev
```
### 捐赠地址 USDT(TRC20)
TVbNgrpeGBGZVm5gTLa21ADP7RpnPFhjya
日行一善,以后必定大富大贵
### 支付宝赞助
[![截图](https://cdn.jsdelivr.net/gh/midoks/mdserver-web@latest/route/static/img/alipay_zz.png)](https://cdn.jsdelivr.net/gh/midoks/mdserver-web@latest/route/static/img/alipay_zz.png)
### 无图不真相
[![截图](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) 文件中。
curl -fsSL https://raw.githubusercontent.com/midoks/mdserver-web/master/scripts/install.sh | sh
```
+16
View File
@@ -0,0 +1,16 @@
# coding:utf-8
import sys
import io
import os
sys.path.append(os.getcwd() + "/class/core")
import config
app = config.config().makeApp(__name__)
try:
if __name__ == "__main__":
app.run()
except Exception as ex:
print ex
+114
View File
@@ -0,0 +1,114 @@
# coding:utf-8
import sys
import io
import os
import time
import shutil
reload(sys)
sys.setdefaultencoding('utf8')
from flask import Flask, session
from datetime import timedelta
sys.path.append(os.getcwd() + "/class/core")
import db
import public
class middleWare:
def __init__(self, wsgi_app):
self.wsgi_app = wsgi_app
def __call__(self, *args, **kwargs):
return self.wsgi_app(*args, **kwargs)
class config:
__version = '0.0.1'
__app = None
__modules = None
def __init__(self):
pass
def makeApp(self, name):
app = Flask(name)
self.__app = app
app.config.version = self.__version
app.config['SECRET_KEY'] = os.urandom(24)
app.config['PERMANENT_SESSION_LIFETIME'] = timedelta(days=7)
self.initDB()
self.initInitD()
self.initRoute()
# self.startDebug()
app.wsgi_app = middleWare(app.wsgi_app)
return app
def startDebug(self):
self.__app.debug = True
self.__app.config.version = self.__version + str(time.time())
def initDB(self):
try:
sql = db.Sql().dbfile('default')
csql = public.readFile('data/sql/default.sql')
csql_list = csql.split(';')
for index in range(len(csql_list)):
sql.execute(csql_list[index], ())
except Exception, ex:
print str(ex)
def initUser(self):
pass
def initInitD(self):
script = public.getRunDir() + '/scripts/init.d/mw.tpl'
script_bin = public.getRunDir() + '/scripts/init.d/mw'
if os.path.exists(script_bin):
return
content = public.readFile(script)
content = content.replace("{$SERVER_PATH}", public.getRunDir())
public.writeFile(script_bin, content)
public.execShell('chmod +x ' + script_bin)
if public.getOs() != 'darwin':
initd_bin = '/etc/init.d/mw'
if not os.path.exists(initd_bin):
shutil.copyfile(script_bin, initd_bin)
public.execShell('chmod +x ' + initd_bin)
def getVersion(self):
return self.__version
def getApp(self):
return self.__app
def initRoute(self):
import route
DEFAULT_MODULES = (
(route.dashboard, "/"),
(route.site, "/site"),
(route.files, "/files"),
(route.soft, "/soft"),
(route.config, "/config"),
(route.plugins, "/plugins"),
(route.task, "/task"),
(route.system, "/system"),
(route.database, "/database"),
(route.crontab, "/crontab"),
(route.firewall, "/firewall"),
(route.control, "/control")
)
self.modules = DEFAULT_MODULES
self.settingModules(self.__app, DEFAULT_MODULES)
def settingModules(self, app, modules):
for module, url_prefix in modules:
app.register_blueprint(module, url_prefix=url_prefix)
+276
View File
@@ -0,0 +1,276 @@
# coding: utf-8
import sqlite3
import os
class Sql():
#------------------------------
# 数据库操作类 For sqlite3
#------------------------------
__DB_FILE = None # 数据库文件
__DB_CONN = None # 数据库连接对象
__DB_TABLE = "" # 被操作的表名称
__OPT_WHERE = "" # where条件
__OPT_LIMIT = "" # limit条件
__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)
except Exception, ex:
return "error: " + str(ex)
def dbfile(self, name):
self.__DB_FILE = 'data/' + 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 order(self, order):
# ORDER条件
if len(order):
self.__OPT_ORDER = " ORDER BY " + order
return self
def limit(self, limit):
# LIMIT条件
if len(limit):
self.__OPT_LIMIT = " LIMIT " + 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_ORDER + self.__OPT_LIMIT
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, ex:
return "error: " + str(ex)
def getField(self, keyName):
# 取回指定字段
result = self.field(keyName).select()
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()
self.__DB_CONN.text_factory = str
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)
id = result.lastrowid
self.__close()
self.__DB_CONN.commit()
return id
except Exception, ex:
return "error: " + str(ex)
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()
self.__DB_CONN.text_factory = str
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, ex:
return "error: " + str(ex)
def commit(self):
self.__close()
self.__DB_CONN.commit()
def save(self, keys, param):
# 更新数据
self.__GetConn()
self.__DB_CONN.text_factory = str
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 public
public.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, 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, ex:
return "error: " + str(ex)
def execute(self, sql, param):
# 执行SQL语句返回受影响行
self.__GetConn()
try:
result = self.__DB_CONN.execute(sql, param)
self.__DB_CONN.commit()
return result.rowcount
except Exception, 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 data
except Exception, ex:
return "error: " + str(ex)
def create(self, name):
# 创建数据表
self.__GetConn()
import public
script = public.readFile('data/' + name + '.sql')
result = self.__DB_CONN.executescript(script)
self.__DB_CONN.commit()
return result.rowcount
def fofile(self, filename):
# 执行脚本
self.__GetConn()
import public
script = public.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
+250
View File
@@ -0,0 +1,250 @@
# coding: utf-8
import psutil
import time
import os
import sys
import public
import re
import json
import pwd
class file_api:
def __init__(self):
pass
def setFileAccept(self, filename):
auth = 'www:www'
if public.getOs() == 'darwin':
user = public.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 = public.getRootDir() + '/recycle_bin/'
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)
public.writeLog('TYPE_FILE', public.getInfo(
'移动文件[{1}]到回收站成功!', (path)))
return True
except:
public.writeLog('TYPE_FILE', public.getInfo(
'移动文件[{1}]到回收站失败!', (path)))
return False
def getBody(self, path):
if not os.path.exists(path):
return public.returnJson(False, '文件不存在', (path,))
if os.path.getsize(path) > 2097152:
return public.returnJson(False, u'不能在线编辑大于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 char['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'])
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'] = u'utf-8'
return public.returnJson(True, 'OK', data)
except Exception as ex:
return public.returnJson(False, u'文件编码不被兼容,无法正确读取文件!' + str(ex))
def saveBody(self, path, data, encoding='utf-8'):
if not os.path.exists(path):
return public.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()
public.writeLog('TYPE_FILE', '文件保存成功', (path,))
return public.returnJson(True, '文件保存成功')
except Exception as ex:
return public.returnJson(False, 'FILE_SAVE_ERR:' + str(ex))
def zip(self, sfile, dfile, stype, path):
if sfile.find(',') == -1:
if not os.path.exists(path + '/' + sfile):
return public.returnMsg(False, '指定文件不存在!')
try:
tmps = public.getRunDir() + '/tmp/panelExec.log'
if stype == 'zip':
os.system("cd '" + path + "' && zip '" + dfile +
"' -r '" + sfile + "' > " + tmps + " 2>&1")
else:
sfiles = ''
for sfile in sfile.split(','):
if not sfile:
continue
sfiles += " '" + sfile + "'"
os.system("cd '" + path + "' && tar -zcvf '" +
dfile + "' " + sfiles + " > " + tmps + " 2>&1")
self.setFileAccept(dfile)
public.writeLog("TYPE_FILE", '文件压缩成功!', (sfile, dfile))
return public.returnJson(True, '文件压缩成功!')
except:
return public.returnJson(False, '文件压缩失败!')
def delete(self, path):
if not os.path.exists(path):
return public.returnJson(False, '指定文件不存在!')
# 检查是否为.user.ini
if path.find('.user.ini') >= 0:
os.system("chattr -i '" + path + "'")
try:
if os.path.exists('data/recycle_bin.pl'):
if self.mvRecycleBin(path):
return public.returnJson(True, '已将文件移动到回收站!')
os.remove(path)
public.writeLog('TYPE_FILE', public.getInfo(
'删除文件[{1}]成功!', (path)))
return public.returnJson(True, '删除文件成功!')
except:
return public.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 = public.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).encode('utf8')
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:
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:
continue
data['DIR'] = sorted(dirnames)
data['FILES'] = sorted(filenames)
if path[0:2] == '//':
data['PATH'] = path[1:]
else:
data['PATH'] = path
return public.getJson(data)
+56
View File
@@ -0,0 +1,56 @@
# coding: utf-8
import psutil
import time
import os
import sys
import public
import re
import json
import pwd
class firewall_api:
def __init__(self):
pass
def getList(self, page, limit):
start = (page - 1) * limit
_list = public.M('firewall').field('id,port,ps,addtime').limit(
str(start) + ',' + str(limit)).order('id desc').select()
data = {}
data['data'] = _list
count = public.M('firewall').count()
_page = {}
_page['count'] = count
_page['tojs'] = 'showAccept'
_page['p'] = page
data['page'] = public.getPage(_page)
return public.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 = public.M('logs').where(find_search, ()).field(
'id,type,log,addtime').limit(str(start) + ',' + str(limit)).order('id desc').select()
data = {}
data['data'] = _list
count = public.M('logs').where(find_search, ()).count()
_page = {}
_page['count'] = count
_page['tojs'] = 'getLogs'
_page['p'] = page
data['page'] = public.getPage(_page)
return public.getJson(data)
+26 -49
View File
@@ -1,20 +1,8 @@
# 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
import public
class Page():
@@ -39,10 +27,10 @@ class Page():
__RTURN_JS = False # 是否返回JS回调
__START_NUM = None # 起始行
__END_NUM = None # 结束行
__ARGS_TPL = ''
def __init__(self):
if False:
tmp = public.getMsg('PAGE')
if tmp:
self.__PREV = tmp['PREV']
self.__NEXT = tmp['NEXT']
self.__START = tmp['START']
@@ -52,19 +40,12 @@ class Page():
self.__FO = tmp['FO']
self.__LINE = tmp['LINE']
def getPageNum(self, num):
return str(num) + self.__ARGS_TPL
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']
if 'args_tpl' in pageInfo:
self.__ARGS_TPL = pageInfo['args_tpl']
self.ROW = pageInfo['row']
self.__C_PAGE = self.__GetCpage(pageInfo['p'])
self.__START_NUM = self.__StartRow()
@@ -74,6 +55,7 @@ class Page():
self.SHIFT = self.__START_NUM - 1
keys = limit.split(',')
pages = {}
# 起始页
pages['1'] = self.__GetStart()
@@ -88,16 +70,14 @@ class Page():
# 当前显示页与总页数
pages['6'] = "<span class='Pnumber'>" + \
str(self.__C_PAGE) + "/" + \
str(self.__COUNT_PAGE) + "</span>"
bytes(self.__C_PAGE) + "/" + bytes(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['7'] = "<span class='Pline'>" + self.__FO + \
bytes(self.__START_NUM) + "-" + \
bytes(self.__END_NUM) + self.__LINE + "</span>"
# 行数
pages['8'] = "<span class='Pcount'>" + str(self.__COUNT_START) + \
str(self.__COUNT_ROW) + str(self.__COUNT_END) + "</span>"
pages['8'] = "<span class='Pcount'>" + self.__COUNT_START + \
bytes(self.__COUNT_ROW) + self.__COUNT_END + "</span>"
# 构造返回数据
retuls = '<div>'
@@ -116,11 +96,10 @@ class Page():
else:
if self.__RTURN_JS == "":
endStr = "<a class='Pend' href='" + self.__URI + "p=" + \
str(self.__COUNT_PAGE) + "'>" + str(self.__END) + "</a>"
bytes(self.__COUNT_PAGE) + "'>" + self.__END + "</a>"
else:
endStr = "<a class='Pend' onclick='" + self.__RTURN_JS + \
"(" + self.getPageNum(self.__COUNT_PAGE) + ")'>" + \
str(self.__END) + "</a>"
"(" + bytes(self.__COUNT_PAGE) + ")'>" + self.__END + "</a>"
return endStr
def __GetNext(self):
@@ -131,11 +110,10 @@ class Page():
else:
if self.__RTURN_JS == "":
nextStr = "<a class='Pnext' href='" + self.__URI + "p=" + \
str(self.__C_PAGE + 1) + "'>" + str(self.__NEXT) + "</a>"
bytes(self.__C_PAGE + 1) + "'>" + self.__NEXT + "</a>"
else:
nextStr = "<a class='Pnext' onclick='" + self.__RTURN_JS + \
"(" + self.getPageNum(self.__C_PAGE + 1) + ")'>" + \
str(self.__NEXT) + "</a>"
"(" + bytes(self.__C_PAGE + 1) + ")'>" + self.__NEXT + "</a>"
return nextStr
@@ -156,15 +134,15 @@ class Page():
if page > 0:
if self.__RTURN_JS == "":
pages += "<a class='Pnum' href='" + self.__URI + \
"p=" + str(page) + "'>" + str(page) + "</a>"
"p=" + bytes(page) + "'>" + bytes(page) + "</a>"
else:
pages += "<a class='Pnum' onclick='" + self.__RTURN_JS + \
"(" + self.getPageNum(page) + ")'>" + str(page) + "</a>"
"(" + bytes(page) + ")'>" + bytes(page) + "</a>"
# 当前页
if self.__C_PAGE > 0:
pages += "<span class='Pcurrent'>" + \
str(self.__C_PAGE) + "</span>"
bytes(self.__C_PAGE) + "</span>"
# 当前页之后
if self.__C_PAGE <= self.__LIST_NUM:
@@ -179,10 +157,10 @@ class Page():
break
if self.__RTURN_JS == "":
pages += "<a class='Pnum' href='" + self.__URI + \
"p=" + str(page) + "'>" + str(page) + "</a>"
"p=" + bytes(page) + "'>" + bytes(page) + "</a>"
else:
pages += "<a class='Pnum' onclick='" + self.__RTURN_JS + \
"(" + self.getPageNum(page) + ")'>" + str(page) + "</a>"
"(" + bytes(page) + ")'>" + bytes(page) + "</a>"
return pages
@@ -194,11 +172,10 @@ class Page():
else:
if self.__RTURN_JS == "":
startStr = "<a class='Ppren' href='" + self.__URI + "p=" + \
str(self.__C_PAGE - 1) + "'>" + str(self.__PREV) + "</a>"
bytes(self.__C_PAGE - 1) + "'>" + self.__PREV + "</a>"
else:
startStr = "<a class='Ppren' onclick='" + self.__RTURN_JS + \
"(" + self.getPageNum(self.__C_PAGE - 1) + ")'>" + \
str(self.__PREV) + "</a>"
"(" + bytes(self.__C_PAGE - 1) + ")'>" + self.__PREV + "</a>"
return startStr
def __GetStart(self):
@@ -209,10 +186,10 @@ class Page():
else:
if self.__RTURN_JS == "":
startStr = "<a class='Pstart' href='" + \
self.__URI + "p=1'>" + str(self.__START) + "</a>"
self.__URI + "p=1'>" + self.__START + "</a>"
else:
startStr = "<a class='Pstart' onclick='" + \
self.__RTURN_JS + "("+self.getPageNum(1)+")'>" + str(self.__START) + "</a>"
self.__RTURN_JS + "(1)'>" + self.__START + "</a>"
return startStr
def __GetCpage(self, p):
@@ -235,11 +212,11 @@ class Page():
# 取总页数
return int(math.ceil(self.__COUNT_ROW / float(self.ROW)))
def __SetUri(self, sinput):
def __SetUri(self, input):
# 构造URI
uri = '?'
for key in sinput:
for key in input:
if key == 'p':
continue
uri += key + '=' + sinput[key] + '&'
uri += key + '=' + input[key] + '&'
return str(uri)
+512
View File
@@ -0,0 +1,512 @@
# coding: utf-8
import psutil
import time
import os
import public
import re
import json
import threading
import multiprocessing
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 plugin_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 = public.getRunDir() + '/plugins'
# 进程是否存在
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):
if not self.__tasks:
self.__tasks = public.M('tasks').where(
"status!=?", ('1',)).field('status,name').select()
if sName.find('php-') != -1:
tmp = sName.split('-')
sName = tmp[0]
version = tmp[1]
isTask = '1'
for task in self.__tasks:
tmpt = public.getStrBetween('[', ']', task['name'])
if not tmpt:
continue
tmp1 = tmpt.split('-')
name1 = tmp1[0].lower()
if sName == 'php':
if name1 == sName and tmp1[1] == version:
isTask = task['status']
else:
if name1 == 'pure':
name1 = 'pure-ftpd'
if name1 == 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 checkStatusMProcess(self, plugins_info):
manager = multiprocessing.Manager()
return_dict = manager.dict()
jobs = []
ntmp_list = range(len(plugins_info))
for i in ntmp_list:
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):
public.writeFile(self.__index, '[]')
indexList = json.loads(public.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 public.readFile(version_f).strip()
return ''
# 构造本地插件信息
def getPluginInfo(self, info):
checks = ''
path = ''
coexist = False
if info["checks"][0:1] == '/':
checks = info["checks"]
else:
checks = public.getRootDir() + '/' + info['checks']
if info.has_key('path'):
path = info['path']
if path[0:1] != '/':
path = public.getRootDir() + '/' + path
if info.has_key('coexist') and info['coexist']:
coexist = True
pluginInfo = {
"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,
}
pluginInfo['task'] = self.checkSetupTask(pluginInfo['name'])
if checks.find('VERSION') > -1:
pluginInfo['install_checks'] = checks.replace(
'VERSION', info['versions'])
if path.find('VERSION') > -1:
pluginInfo['path'] = path.replace(
'VERSION', info['versions'])
pluginInfo['display'] = self.checkDisplayIndex(
info['name'], pluginInfo['versions'])
pluginInfo['setup'] = os.path.exists(pluginInfo['install_checks'])
if coexist and pluginInfo['setup']:
pluginInfo['setup_version'] = info['versions']
else:
pluginInfo['setup_version'] = self.getVersion(
pluginInfo['install_checks'])
# pluginInfo['status'] = self.checkStatus(pluginInfo)
return pluginInfo
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 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 getAllList(self, sType='0'):
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(public.readFile(json_file))
tmp_data = self.makeList(data, sType)
for index in range(len(tmp_data)):
plugins_info.append(tmp_data[index])
except Exception, 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(public.readFile(json_file))
tmp_data = self.makeList(data, sType)
for index in range(len(tmp_data)):
plugins_info.append(tmp_data[index])
except Exception, e:
print e
start = (page - 1) * pageSize
end = start + pageSize
_plugins_info = plugins_info[start:end]
_plugins_info = self.checkStatusMProcess(_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(public.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(public.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):
ret = {}
ret['type'] = json.loads(public.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'] = public.getPage(args)
return ret
def getIndexList(self):
if not os.path.exists(self.__index):
public.writeFile(self.__index, '[]')
indexList = json.loads(public.readFile(self.__index))
plist = []
app = []
for i in indexList:
info = i.split('-')
if not info[0] in app:
app.append(info[0])
path = self.__plugin_dir + '/' + info[0]
if os.path.isdir(path):
json_file = path + '/info.json'
if os.path.exists(json_file):
try:
data = json.loads(public.readFile(json_file))
tmp_data = self.makeList(data)
for index in range(len(tmp_data)):
if tmp_data[index]['versions'] == info[1] or info[1] in tmp_data[index]['versions']:
tmp_data[index]['display'] = True
plist.append(tmp_data[index])
continue
except Exception, e:
print e
plist = self.checkStatusMProcess(plist)
return plist
def setIndexSort(self, sort):
data = sort.split('|')
public.writeFile(self.__index, json.dumps(data))
return True
def addIndex(self, name, version):
if not os.path.exists(self.__index):
public.writeFile(self.__index, '[]')
indexList = json.loads(public.readFile(self.__index))
vname = name + '-' + version
if vname in indexList:
return public.returnJson(False, '请不要重复添加!')
if len(indexList) >= 12:
return public.returnJson(False, '首页最多只能显示12个软件!')
indexList.append(vname)
public.writeFile(self.__index, json.dumps(indexList))
return public.returnJson(True, '添加成功!')
def removeIndex(self, name, version):
if not os.path.exists(self.__index):
public.writeFile(self.__index, '[]')
indexList = json.loads(public.readFile(self.__index))
vname = name + '-' + version
if not vname in indexList:
return public.returnJson(True, '删除成功!')
indexList.remove(vname)
public.writeFile(self.__index, json.dumps(indexList))
return public.returnJson(True, '删除成功!')
def run(self, name, func, version, args='', script='index'):
path = public.getRunDir() + '/' + self.__plugin_dir + \
'/' + name + '/' + script + '.py'
py = 'python ' + path
if args == '':
py_cmd = py + ' ' + func + ' ' + version
else:
py_cmd = py + ' ' + func + ' ' + version + ' ' + args
if not os.path.exists(path):
return ('', '')
data = public.execShell(py_cmd)
print py_cmd
# print os.path.exists(py_cmd)
return (data[0].strip(), data[1].strip())
+723
View File
@@ -0,0 +1,723 @@
# coding: utf-8
import os
import sys
import time
import string
import json
import hashlib
import shlex
import datetime
import subprocess
import re
import hashlib
import db
from random import Random
def execShell(cmdstring, cwd=None, timeout=None, shell=True):
if shell:
cmdstring_list = cmdstring
else:
cmdstring_list = shlex.split(cmdstring)
if timeout:
end_time = datetime.datetime.now() + datetime.timedelta(seconds=timeout)
sub = subprocess.Popen(cmdstring_list, cwd=cwd, stdin=subprocess.PIPE,
shell=shell, bufsize=4096, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
while sub.poll() is None:
time.sleep(0.1)
if timeout:
if end_time <= datetime.datetime.now():
raise Exception("Timeout:%s" % cmdstring)
return sub.communicate()
def getRunDir():
return os.getcwd()
def getRootDir():
return os.path.dirname(os.path.dirname(getRunDir()))
def getPluginDir():
return getRunDir() + '/plugins'
def getServerDir():
return getRootDir() + '/server'
def getWwwDir():
return getRootDir() + '/wwwroot'
def getLogsDir():
return getRootDir() + '/wwwlogs'
def getOs():
os = execShell('uname')
return os[0].lower().strip()
def isAppleSystem():
if getOs() == 'darwin':
return True
return False
def isInstalledWeb():
path = getServerDir() + '/openresty/nginx/sbin/nginx'
if os.path.exists(path):
return True
return False
def restartWeb():
if isInstalledWeb():
initd = getServerDir() + '/openresty/init.d/openresty'
execShell(initd + ' ' + 'reload')
def M(table):
sql = db.Sql()
return sql.table(table)
def getPage(args, result='1,2,3,4,5,8'):
data = getPageObject(args, result)
return data[0]
def getPageObject(args, result='1,2,3,4,5,8'):
# 取分页
import page
# 实例化分页类
page = page.Page()
info = {}
info['count'] = 0
if args.has_key('count'):
info['count'] = int(args['count'])
info['row'] = 10
if args.has_key('row'):
info['row'] = int(args['row'])
info['p'] = 1
if args.has_key('p'):
info['p'] = int(args['p'])
info['uri'] = {}
info['return_js'] = ''
if args.has_key('tojs'):
info['return_js'] = args['tojs']
return (page.GetPage(info, result), page)
def md5(str):
# 生成MD5
try:
m = hashlib.md5()
m.update(str)
return m.hexdigest()
except:
return False
def getFileMd5(filename):
# 文件的MD5值
if not os.path.isfile(filename):
return False
myhash = hashlib.md5()
f = file(filename, 'rb')
while True:
b = f.read(8096)
if not b:
break
myhash.update(b)
f.close()
return myhash.hexdigest()
def getRandomString(length):
# 取随机字符串
str = ''
chars = 'AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz0123456789'
chrlen = len(chars) - 1
random = Random()
for i in range(length):
str += chars[random.randint(0, chrlen)]
return str
def getJson(data):
import json
return json.dumps(data)
def returnJson(status, msg, args=()):
# 取通用Json返回
return getJson(returnMsg(status, msg, args))
def returnMsg(status, msg, args=()):
# 取通用字曲返回
pjson = 'static/language/' + getLanguage() + '/public.json'
logMessage = json.loads(readFile(pjson))
keys = logMessage.keys()
if msg in keys:
msg = logMessage[msg]
for i in range(len(args)):
rep = '{' + str(i + 1) + '}'
msg = msg.replace(rep, args[i])
return {'status': status, 'msg': msg, 'data': args}
def getInfo(msg, args=()):
# 取提示消息
for i in range(len(args)):
rep = '{' + str(i + 1) + '}'
msg = msg.replace(rep, args[i])
return msg
def getMsg(key, args=()):
# 取提示消息
try:
logMessage = json.loads(
readFile('static/language/' + getLanguage() + '/public.json'))
keys = logMessage.keys()
msg = None
if key in keys:
msg = logMessage[key]
for i in range(len(args)):
rep = '{' + str(i + 1) + '}'
msg = msg.replace(rep, args[i])
return msg
except:
return key
def getLan(key):
# 取提示消息
logMessage = json.loads(
readFile('static/language/' + getLanguage() + '/template.json'))
keys = logMessage.keys()
msg = None
if key in keys:
msg = logMessage[key]
return msg
def readFile(filename):
# 读文件内容
try:
fp = open(filename, 'r')
fBody = fp.read()
fp.close()
return fBody
except:
return False
def getDate():
# 取格式时间
import time
return time.strftime('%Y-%m-%d %X', time.localtime())
def getLanguage():
path = 'data/language.pl'
if not os.path.exists(path):
return 'Simplified_Chinese'
return readFile(path).strip()
def writeLog(type, logMsg, args=()):
# 写日志
try:
import time
import db
import json
logMessage = json.loads(
readFile('static/language/' + getLanguage() + '/log.json'))
keys = logMessage.keys()
if logMsg in keys:
logMsg = logMessage[logMsg]
for i in range(len(args)):
rep = '{' + str(i + 1) + '}'
logMsg = logMsg.replace(rep, args[i])
if type in keys:
type = logMessage[type]
sql = db.Sql()
mDate = time.strftime('%Y-%m-%d %X', time.localtime())
data = (type, logMsg, mDate)
result = sql.table('logs').add('type,log,addtime', data)
except:
pass
def writeFile(filename, str):
# 写文件内容
try:
fp = open(filename, 'w+')
fp.write(str)
fp.close()
return True
except:
return False
def httpGet(url, timeout=30):
# 发送GET请求
try:
import urllib2
import ssl
try:
ssl._create_default_https_context = ssl._create_unverified_context
except:
pass
response = urllib2.urlopen(url, timeout=timeout)
return response.read()
except Exception, ex:
# WriteLog('网络诊断',str(ex) + '['+url+']');
return str(ex)
def httpPost(url, data, timeout=30):
# 发送POST请求
try:
import urllib
import urllib2
import ssl
try:
ssl._create_default_https_context = ssl._create_unverified_context
except:
pass
data = urllib.urlencode(data)
req = urllib2.Request(url, data)
response = urllib2.urlopen(req, timeout=timeout)
return response.read()
except Exception, ex:
# WriteLog('网络诊断',str(ex) + '['+url+']');
return str(ex)
def writeSpeed(title, used, total, speed=0):
# 写进度
if not title:
data = {'title': None, 'progress': 0,
'total': 0, 'used': 0, 'speed': 0}
else:
progress = int((100.0 * used / total))
data = {'title': title, 'progress': progress,
'total': total, 'used': used, 'speed': speed}
writeFile('/tmp/panelSpeed.pl', json.dumps(data))
return True
def getSpeed():
# 取进度
data = readFile('/tmp/panelSpeed.pl')
if not data:
data = json.dumps({'title': None, 'progress': 0,
'total': 0, 'used': 0, 'speed': 0})
writeFile('/tmp/panelSpeed.pl', data)
return json.loads(data)
def getLastLine(inputfile, lineNum):
# 读文件指定倒数行数
try:
fp = open(inputfile, 'r')
lastLine = ""
lines = fp.readlines()
count = len(lines)
if count > lineNum:
num = lineNum
else:
num = count
i = 1
lastre = []
for i in range(1, (num + 1)):
if lines:
n = -i
lastLine = lines[n].strip()
fp.close()
lastre.append(lastLine)
result = ''
num -= 1
while num >= 0:
result += lastre[num] + "\n"
num -= 1
return result
except:
return getMsg('TASK_SLEEP')
def downloadFile(url, filename):
import urllib
urllib.urlretrieve(url, filename=filename, reporthook=downloadHook)
def downloadHook(count, blockSize, totalSize):
speed = {'total': totalSize, 'block': blockSize, 'count': count}
print speed
print '%02d%%' % (100.0 * count * blockSize / totalSize)
def getLocalIp():
# 取本地外网IP
try:
import re
filename = 'data/iplist.txt'
ipaddress = readFile(filename)
if not ipaddress:
import urllib2
url = 'http://pv.sohu.com/cityjson?ie=utf-8'
opener = urllib2.urlopen(url)
str = opener.read()
ipaddress = re.search('\d+.\d+.\d+.\d+', str).group(0)
writeFile(filename, ipaddress)
ipaddress = re.search('\d+.\d+.\d+.\d+', ipaddress).group(0)
return ipaddress
except:
return '127.0.0.1'
def inArray(arrays, searchStr):
# 搜索数据中是否存在
for key in arrays:
if key == searchStr:
return True
return False
def checkIp(ip):
# 检查是否为IPv4地址
import re
p = re.compile(
'^((25[0-5]|2[0-4]\d|[01]?\d\d?)\.){3}(25[0-5]|2[0-4]\d|[01]?\d\d?)$')
if p.match(ip):
return True
else:
return False
def checkPort(port):
# 检查端口是否合法
ports = ['21', '25', '443', '8080', '888', '8888', '8443']
if port in ports:
return False
intport = int(port)
if intport < 1 or intport > 65535:
return False
return True
def getStrBetween(startStr, endStr, srcStr):
# 字符串取中间
start = srcStr.find(startStr)
if start == -1:
return None
end = srcStr.find(endStr)
if end == -1:
return None
return srcStr[start + 1:end]
def getCpuType():
# 取CPU类型
cpuinfo = open('/proc/cpuinfo', 'r').read()
rep = "model\s+name\s+:\s+(.+)"
tmp = re.search(rep, cpuinfo)
cpuType = None
if tmp:
cpuType = tmp.groups()[0]
return cpuType
def isRestart():
# 检查是否允许重启
num = M('tasks').where('status!=?', ('1',)).count()
if num > 0:
return False
return True
def isUpdateLocalSoft():
num = M('tasks').where('status!=?', ('1',)).count()
if os.path.exists('mdserver-web.zip'):
return True
if num > 0:
data = M('tasks').where('status!=?', ('1',)).field(
'id,type,execstr').limit('1').select()
argv = data[0]['execstr'].split('|dl|')
if data[0]['type'] == 'download' and argv[1] == 'mdserver-web.zip':
return True
return False
def hasPwd(password):
# 加密密码字符
import crypt
return crypt.crypt(password, password)
def getTimeout(url):
start = time.time()
result = httpGet(url)
if result != 'True':
return False
return int((time.time() - start) * 1000)
def auth_decode(data):
# 解密数据
token = GetToken()
# 是否有生成Token
if not token:
return returnMsg(False, 'REQUEST_ERR')
# 校验access_key是否正确
if token['access_key'] != data['btauth_key']:
return returnMsg(False, 'REQUEST_ERR')
# 解码数据
import binascii
import hashlib
import urllib
import hmac
import json
tdata = binascii.unhexlify(data['data'])
# 校验signature是否正确
signature = binascii.hexlify(
hmac.new(token['secret_key'], tdata, digestmod=hashlib.sha256).digest())
if signature != data['signature']:
return returnMsg(False, 'REQUEST_ERR')
# 返回
return json.loads(urllib.unquote(tdata))
# 数据加密
def auth_encode(data):
token = GetToken()
pdata = {}
# 是否有生成Token
if not token:
return returnMsg(False, 'REQUEST_ERR')
# 生成signature
import binascii
import hashlib
import urllib
import hmac
import json
tdata = urllib.quote(json.dumps(data))
# 公式 hex(hmac_sha256(data))
pdata['signature'] = binascii.hexlify(
hmac.new(token['secret_key'], tdata, digestmod=hashlib.sha256).digest())
# 加密数据
pdata['btauth_key'] = token['access_key']
pdata['data'] = binascii.hexlify(tdata)
pdata['timestamp'] = time.time()
# 返回
return pdata
def checkToken(get):
# 检查Token
tempFile = 'data/tempToken.json'
if not os.path.exists(tempFile):
return False
import json
import time
tempToken = json.loads(readFile(tempFile))
if time.time() > tempToken['timeout']:
return False
if get.token != tempToken['token']:
return False
return True
def checkInput(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 getNumLines(path, num, p=1):
# 取文件指定尾行数
try:
import cgi
if not os.path.exists(path):
return ""
start_line = (p - 1) * num
count = start_line + num
fp = open(path)
buf = ""
fp.seek(-1, 2)
if fp.read(1) == "\n":
fp.seek(-1, 2)
data = []
b = True
n = 0
for i in range(count):
while True:
newline_pos = string.rfind(buf, "\n")
pos = fp.tell()
if newline_pos != -1:
if n >= start_line:
line = buf[newline_pos + 1:]
try:
data.insert(0, cgi.escape(line))
except:
pass
buf = buf[:newline_pos]
n += 1
break
else:
if pos == 0:
b = False
break
to_read = min(4096, pos)
fp.seek(-to_read, 1)
buf = fp.read(to_read) + buf
fp.seek(-to_read, 1)
if pos - to_read == 0:
buf = "\n" + buf
if not b:
break
fp.close()
except:
data = []
return "\n".join(data)
def checkCert(certPath='ssl/certificate.pem'):
# 验证证书
openssl = '/usr/local/openssl/bin/openssl'
if not os.path.exists(openssl):
openssl = 'openssl'
certPem = readFile(certPath)
s = "\n-----BEGIN CERTIFICATE-----"
tmp = certPem.strip().split(s)
for tmp1 in tmp:
if tmp1.find('-----BEGIN CERTIFICATE-----') == -1:
tmp1 = s + tmp1
writeFile(certPath, tmp1)
result = execShell(openssl + " x509 -in " +
certPath + " -noout -subject")
if result[1].find('-bash:') != -1:
return True
if len(result[1]) > 2:
return False
if result[0].find('error:') != -1:
return False
return True
# 获取面板地址
# def getPanelAddr():
# import web
# protocol = 'https://' if os.path.exists("data/ssl.pl") else 'http://'
# h = web.ctx.host.split(':')
# try:
# result = protocol + h[0] + ':' + h[1]
# except:
# result = protocol + h[0] + ':' + readFile('data/port.pl').strip()
# return result
def toSize(size):
# 字节单位转换
d = ('b', 'KB', 'MB', 'GB', 'TB')
s = d[0]
for b in d:
if size < 1024:
return str(size) + ' ' + b
size = size / 1024
s = b
return str(size) + ' ' + b
def getMacAddress():
# 获取mac
import uuid
mac = uuid.UUID(int=uuid.getnode()).hex[-12:]
return ":".join([mac[e:e + 2] for e in range(0, 11, 2)])
def get_string(t):
if t != -1:
max = 126
m_types = [{'m': 122, 'n': 97}, {'m': 90, 'n': 65}, {'m': 57, 'n': 48}, {
'm': 47, 'n': 32}, {'m': 64, 'n': 58}, {'m': 96, 'n': 91}, {'m': 125, 'n': 123}]
else:
max = 256
t = 0
m_types = [{'m': 255, 'n': 0}]
arr = []
for i in range(max):
if i < m_types[t]['n'] or i > m_types[t]['m']:
continue
arr.append(chr(i))
return arr
def get_string_find(t):
if type(t) != list:
t = [t]
return_str = ''
for s1 in t:
return_str += get_string(int(s1[0]))[int(s1[1:])]
return return_str
def get_string_arr(t):
s_arr = {}
t_arr = []
for s1 in t:
for i in range(6):
if not i in s_arr:
s_arr[i] = get_string(i)
for j in range(len(s_arr[i])):
if s1 == s_arr[i][j]:
t_arr.append(str(i) + str(j))
return t_arr
+399
View File
@@ -0,0 +1,399 @@
# coding: utf-8
import psutil
import time
import os
import sys
import public
import re
import json
import pwd
class site_api:
siteName = None # 网站名称
sitePath = None # 根目录
sitePort = None # 端口
phpVersion = None # PHP版本
setupPath = None # 安装路径
isWriteLogs = None # 是否写日志
def __init__(self):
self.setupPath = public.getServerDir()
path = self.setupPath + '/openresty/nginx/conf/vhost'
if not os.path.exists(path):
public.execShell("mkdir -p " + path + " && chmod -R 755 " + path)
path = self.setupPath + '/openresty/nginx/conf/rewrite'
if not os.path.exists(path):
public.execShell("mkdir -p " + path + " && chmod -R 755 " + path)
# 域名编码转换
def toPunycode(self, domain):
import re
if sys.version_info[0] == 2:
domain = domain.encode('utf8')
tmp = domain.split('.')
newdomain = ''
for dkey in tmp:
# 匹配非ascii字符
match = re.search(u"[\x80-\xff]+", dkey)
if not match:
newdomain += dkey + '.'
else:
newdomain += 'xn--' + \
dkey.decode('utf-8').encode('punycode') + '.'
return newdomain[0:-1]
# 中文路径处理
def toPunycodePath(self, path):
if sys.version_info[0] == 2:
path = path.encode('utf-8')
if os.path.exists(path):
return path
import re
match = re.search(u"[\x80-\xff]+", path)
if not match:
return path
npath = ''
for ph in path.split('/'):
npath += '/' + self.toPunycode(ph)
return npath.replace('//', '/')
# 路径处理
def getPath(self, path):
if path[-1] == '/':
return path[0:-1]
return path
def getHostConf(self, siteName):
return public.getServerDir() + '/openresty/nginx/conf/vhost/' + siteName + '.conf'
def getRewriteConf(self, siteName):
return public.getServerDir() + '/openresty/nginx/conf/rewrite/' + siteName + '.conf'
def getIndexConf(self):
return public.getServerDir() + '/openresty/nginx/conf/nginx.conf'
def list(self):
_list = public.M('sites').where('', ()).field(
'id,name,path,status,ps,addtime,edate').limit('0,5').order('id desc').select()
_ret = {}
_ret['data'] = _list
count = public.M('sites').where('', ()).count()
_page = {}
_page['count'] = count
_page['tojs'] = 'getWeb'
_ret['page'] = public.getPage(_page)
return public.getJson(_ret)
def getDomain(self, pid):
_list = public.M('domain').where("pid=?", (pid,)).field(
'id,pid,name,port,addtime').select()
return public.getJson(_list)
def getLogs(self, siteName):
logPath = public.getLogsDir() + '/' + siteName + '.log'
if not os.path.exists(logPath):
return public.returnJson(False, '日志为空')
return public.returnJson(True, public.getNumLines(logPath, 1000))
def getSitePhpVersion(self, siteName):
conf = public.readFile(self.getHostConf(siteName))
rep = "enable-php-([0-9]{2,3})\.conf"
tmp = re.search(rep, conf).groups()
data = {}
data['phpversion'] = tmp[0]
return public.getJson(data)
def getIndex(self, sid):
print sid
siteName = public.M('sites').where("id=?", (sid,)).getField('name')
file = self.getHostConf(siteName)
conf = public.readFile(file)
rep = "\s+index\s+(.+);"
tmp = re.search(rep, conf).groups()
return tmp[0].replace(' ', ',')
def setIndex(self, sid, index):
if index.find('.') == -1:
return public.returnJson(False, '默认文档格式不正确,例:index.html')
index = index.replace(' ', '')
index = index.replace(',,', ',')
if len(index) < 3:
return public.returnJson(False, '默认文档不能为空!')
siteName = public.M('sites').where("id=?", (sid,)).getField('name')
index_l = index.replace(",", " ")
file = self.getHostConf(siteName)
conf = public.readFile(file)
if conf:
rep = "\s+index\s+.+;"
conf = re.sub(rep, "\n\tindex " + index_l + ";", conf)
public.writeFile(file, conf)
public.writeLog('TYPE_SITE', 'SITE_INDEX_SUCCESS', (siteName, index_l))
return public.returnJson(True, '设置成功!')
def getLimitNet(self, sid):
siteName = public.M('sites').where("id=?", (sid,)).getField('name')
filename = self.getHostConf(siteName)
# 站点总并发
data = {}
conf = public.readFile(filename)
try:
rep = "\s+limit_conn\s+perserver\s+([0-9]+);"
tmp = re.search(rep, conf).groups()
data['perserver'] = int(tmp[0])
# IP并发限制
rep = "\s+limit_conn\s+perip\s+([0-9]+);"
tmp = re.search(rep, conf).groups()
data['perip'] = int(tmp[0])
# 请求并发限制
rep = "\s+limit_rate\s+([0-9]+)\w+;"
tmp = re.search(rep, conf).groups()
data['limit_rate'] = int(tmp[0])
except:
data['perserver'] = 0
data['perip'] = 0
data['limit_rate'] = 0
return public.getJson(data)
def checkIndexConf(self):
limit = self.getIndexConf()
nginxConf = public.readFile(limit)
limitConf = "limit_conn_zone $binary_remote_addr zone=perip:10m;\n\t\tlimit_conn_zone $server_name zone=perserver:10m;"
nginxConf = nginxConf.replace(
"#limit_conn_zone $binary_remote_addr zone=perip:10m;", limitConf)
public.writeFile(limit, nginxConf)
def saveLimitNet(self, sid, perserver, perip, limit_rate):
str_perserver = 'limit_conn perserver ' + perserver + ';'
str_perip = 'limit_conn perip ' + perip + ';'
str_limit_rate = 'limit_rate ' + limit_rate + 'k;'
siteName = public.M('sites').where("id=?", (sid,)).getField('name')
filename = self.getHostConf(siteName)
conf = public.readFile(filename)
if(conf.find('limit_conn perserver') != -1):
# 替换总并发
rep = "limit_conn\s+perserver\s+([0-9]+);"
conf = re.sub(rep, str_perserver, conf)
# 替换IP并发限制
rep = "limit_conn\s+perip\s+([0-9]+);"
conf = re.sub(rep, str_perip, conf)
# 替换请求流量限制
rep = "limit_rate\s+([0-9]+)\w+;"
conf = re.sub(rep, str_limit_rate, conf)
else:
conf = conf.replace('#error_page 404/404.html;', "#error_page 404/404.html;\n " +
str_perserver + "\n " + str_perip + "\n " + str_limit_rate)
public.writeFile(filename, conf)
public.restartWeb()
public.writeLog('TYPE_SITE', 'SITE_NETLIMIT_OPEN_SUCCESS', (siteName,))
return public.returnJson(True, '设置成功!')
def closeLimitNet(self, sid):
siteName = public.M('sites').where("id=?", (sid,)).getField('name')
filename = self.getHostConf(siteName)
conf = public.readFile(filename)
# 清理总并发
rep = "\s+limit_conn\s+perserver\s+([0-9]+);"
conf = re.sub(rep, '', conf)
# 清理IP并发限制
rep = "\s+limit_conn\s+perip\s+([0-9]+);"
conf = re.sub(rep, '', conf)
# 清理请求流量限制
rep = "\s+limit_rate\s+([0-9]+)\w+;"
conf = re.sub(rep, '', conf)
public.writeFile(filename, conf)
public.restartWeb()
public.writeLog(
'TYPE_SITE', 'SITE_NETLIMIT_CLOSE_SUCCESS', (siteName,))
return public.returnJson(True, '已关闭流量限制!')
def getSecurity(self, sid, name):
filename = self.getHostConf(name)
conf = public.readFile(filename)
data = {}
if conf.find('SECURITY-START') != -1:
rep = "#SECURITY-START(\n|.){1,500}#SECURITY-END"
tmp = re.search(rep, conf).group()
data['fix'] = re.search(
"\(.+\)\$", tmp).group().replace('(', '').replace(')$', '').replace('|', ',')
data['domains'] = ','.join(re.search(
"valid_referers\s+none\s+blocked\s+(.+);\n", tmp).groups()[0].split())
data['status'] = True
else:
data['fix'] = 'jpg,jpeg,gif,png,js,css'
domains = public.M('domain').where(
'pid=?', (sid,)).field('name').select()
tmp = []
for domain in domains:
tmp.append(domain['name'])
data['domains'] = ','.join(tmp)
data['status'] = False
return public.getJson(data)
def setSecurity(self, sid, name, fix, domains, status):
if len(fix) < 2:
return public.returnJson(False, 'URL后缀不能为空!')
file = self.getHostConf(name)
if os.path.exists(file):
conf = public.readFile(file)
if conf.find('SECURITY-START') != -1:
rep = "\s{0,4}#SECURITY-START(\n|.){1,500}#SECURITY-END\n?"
conf = re.sub(rep, '', conf)
public.writeLog('网站管理', '站点[' + name + ']已关闭防盗链设置!')
else:
rconf = '''#SECURITY-START 防盗链配置
location ~ .*\.(%s)$
{
expires 30d;
access_log /dev/null;
valid_referers none blocked %s;
if ($invalid_referer){
return 404;
}
}
#SECURITY-END
include enable-php-''' % (fix.strip().replace(',', '|'), domains.strip().replace(',', ' '))
conf = re.sub("include\s+enable-php-", rconf, conf)
public.writeLog('网站管理', '站点[' + name + ']已开启防盗链!')
public.writeFile(file, conf)
public.restartWeb()
return public.returnJson(True, '设置成功!')
def getPhpVersion(self):
phpVersions = ('00', '52', '53', '54', '55',
'56', '70', '71', '72', '73', '74')
data = []
for val in phpVersions:
tmp = {}
if val == '00':
tmp['name'] = '纯静态'
data.append(tmp)
checkPath = public.getServerDir() + '/php/' + val + '/bin/php'
if os.path.exists(checkPath):
tmp['version'] = val
tmp['name'] = 'PHP-' + val
data.append(tmp)
return public.getJson(data)
def getRewriteList(self):
rewriteList = {}
rewriteList['rewrite'] = []
rewriteList['rewrite'].append('0.当前')
for ds in os.listdir('rewrite/nginx'):
rewriteList['rewrite'].append(ds[0:len(ds) - 5])
rewriteList['rewrite'] = sorted(rewriteList['rewrite'])
return rewriteList
def createRootDir(self, path):
if not os.path.exists(path):
os.makedirs(path)
if not public.isAppleSystem():
public.execShell('chown -R www:www ' + path)
public.execShell('chmod -R 755 ' + path)
def addDomain(self, domain, webname, pid):
pass
def nginxAddConf(self):
source_tpl = public.getRunDir() + '/data/tpl/nginx.conf'
vhost_file = self.setupPath + '/openresty/nginx/conf/vhost/' + self.siteName + '.conf'
content = public.readFile(source_tpl)
content = content.replace('{$PORT}', self.sitePort)
content = content.replace('{$SERVER_NAME}', self.siteName)
content = content.replace('{$ROOT_DIR}', self.sitePath)
content = content.replace('{$PHPVER}', self.phpVersion)
or_rewrite = self.setupPath + '/openresty/nginx/conf/rewrite'
content = content.replace('{$OR_REWRITE}', or_rewrite)
logsPath = public.getLogsDir()
content = content.replace('{$LOGPATH}', logsPath)
public.writeFile(vhost_file, content)
rewrite_file = self.setupPath + \
'/openresty/nginx/conf/rewrite/' + self.siteName + '.conf'
public.writeFile(rewrite_file, '')
def add(self, webname, port, ps, path, version):
siteMenu = json.loads(webname)
self.siteName = self.toPunycode(
siteMenu['domain'].strip().split(':')[0]).strip()
self.sitePath = self.toPunycodePath(
self.getPath(path.replace(' ', '')))
self.sitePort = port.strip().replace(' ', '')
self.phpVersion = version
siteM = public.M('sites')
if siteM.where("name=?", (self.siteName,)).count():
return public.returnJson(False, '您添加的站点已存在!')
# 写入数据库
pid = siteM.add('name,path,status,ps,edate,addtime',
(self.siteName, self.sitePath, '1', ps, '0000-00-00', public.getDate()))
opid = public.M('domain').where(
"name=?", (self.siteName,)).getField('pid')
if opid:
if public.M('sites').where('id=?', (opid,)).count():
return public.returnJson(False, '您添加的域名已存在!')
public.M('domain').where('pid=?', (opid,)).delete()
# 添加更多域名
for domain in siteMenu['domainlist']:
sdomain = domain
swebname = self.siteName
spid = str(get.pid)
# self.addDomain(domain, webname, pid)
public.M('domain').add('pid,name,port,addtime',
(pid, self.siteName, self.sitePort, public.getDate()))
self.createRootDir(self.sitePath)
self.nginxAddConf()
data = {}
data['siteStatus'] = False
public.restartWeb()
return public.getJson(data)
def delete(self, sid, webname):
confFile = self.setupPath + '/openresty/nginx/conf/vhost/' + webname + '.conf'
if os.path.exists(confFile):
os.remove(confFile)
rewriteFile = self.setupPath + '/openresty/nginx/conf/rewrite/' + webname + '.conf'
if os.path.exists(rewriteFile):
os.remove(rewriteFile)
public.M('sites').where("id=?", (sid,)).delete()
return public.returnJson(True, '站点删除成功!')
def setEndDate(self, sid, edate):
result = public.M('sites').where(
'id=?', (sid,)).setField('edate', edate)
siteName = public.M('sites').where('id=?', (sid,)).getField('name')
public.writeLog('TYPE_SITE', '设置成功,站点到期后将自动停止!', (siteName, edate))
return public.returnJson(True, '设置成功,站点到期后将自动停止!')
+643
View File
@@ -0,0 +1,643 @@
# coding: utf-8
import psutil
import time
import os
import re
import math
import json
from flask import Flask, session
import db
import public
import config
class system_api:
setupPath = None
pids = None
def __init__(self):
self.setupPath = public.getServerDir()
# 名取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 = public.GetLocalIp()
try:
try:
port = web.ctx.host.split(':')[1]
except:
port = public.readFile('data/port.pl')
except:
port = '8888'
domain = ''
if os.path.exists('data/domain.conf'):
domain = public.readFile('data/domain.conf')
autoUpdate = ''
if os.path.exists('data/autoUpdate.pl'):
autoUpdate = 'checked'
limitip = ''
if os.path.exists('data/limitip.conf'):
limitip = public.readFile('data/limitip.conf')
templates = []
for template in os.listdir('templates/'):
if os.path.isdir('templates/' + template):
templates.append(template)
template = public.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'] = public.M('users').where(
'username=?', ('admin',)).count()
data['version'] = '0.0.1'
return data
def getLoadAverage(self, get=None):
c = os.getloadavg()
data = {}
data['one'] = float(c[0])
data['five'] = float(c[1])
data['fifteen'] = float(c[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/panel/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 = public.readFile(titlePl).strip()
return title
def getSystemVersion(self):
# 取操作系统版本
os = public.getOs()
if os == 'darwin':
data = public.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
version = public.readFile('/etc/redhat-release')
if not version:
version = public.readFile(
'/etc/issue').strip().split("\n")[0].replace('\\n', '').replace('\l', '').strip()
else:
version = version.replace('release ', '').strip()
return version
def getBootTime(self):
# 取系统启动时间
start_time = psutil.boot_time()
run_time = time.time() - start_time
# conf = public.readFile('/proc/uptime').split()
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 public.getInfo('已不间断运行: {1}天{2}小时{3}分钟', (str(int(days)), str(int(hours)), str(int(min))))
def getCpuInfo(self, interval=1):
# 取CPU信息
cpuCount = psutil.cpu_count()
used = psutil.cpu_percent(interval=interval)
return used, cpuCount
def getMemInfo(self, get=None):
# 取内存信息
mem = psutil.virtual_memory()
if public.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 public.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, ex:
return 1
def getDiskInfo(self, get=None):
return self.getDiskInfo2()
# 取磁盘分区信息
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]
tmp['size'] = psutil.disk_usage(disk[1])
diskInfo.append(tmp)
return diskInfo
def getDiskInfo2(self):
# 取磁盘分区信息
temp = public.execShell(
"df -h -P|grep '/'|grep -v tmpfs | grep -v devfs")[0]
tempInodes = public.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', '/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]]
if disk[5] == '/':
bootLog = os.getcwd() + '/tmp/panelBoot.pl'
if disk[2].find('M') != -1:
if os.path.exists(bootLog):
os.system('rm -f ' + bootLog)
else:
if not os.path.exists(bootLog):
pass
if inodes[2] != '0':
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 '\t\033[1;32m[OK]\033[0m'
num += 1
total += size
count += num
return total, count
# 清理其它
def clearOther(self):
clearPath = [
{'path': '/www/server/panel', 'find': 'testDisk_'},
{'path': '/www/wwwlogs', 'find': 'log'},
{'path': '/tmp', 'find': 'panelBoot.pl'},
{'path': '/www/server/panel/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
public.serviceReload()
os.system('echo > /tmp/panelBoot.pl')
return total, count
def getNetWork(self, get=None):
# return self.GetNetWorkApi(get);
# 取网络流量信息
try:
# 取网络流量信息
networkIo = psutil.net_io_counters()[:4]
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(get)
networkInfo['mem'] = self.getMemInfo(get)
return networkInfo
except Exception, e:
print e
return None
def getNetWorkApi(self, get=None):
# 取网络流量信息
try:
tmpfile = 'data/network.temp'
networkIo = psutil.net_io_counters()[:4]
if not os.path.exists(tmpfile):
public.writeFile(tmpfile, str(
networkIo[0]) + '|' + str(networkIo[1]) + '|' + str(int(time.time())))
lastValue = public.readFile(tmpfile).split('|')
ntime = time.time()
networkInfo = {}
networkInfo['upTotal'] = networkIo[0]
networkInfo['downTotal'] = networkIo[1]
networkInfo['up'] = round(
float(networkIo[0] - int(lastValue[0])) / 1024 / (ntime - int(lastValue[2])), 2)
networkInfo['down'] = round(
float(networkIo[1] - int(lastValue[1])) / 1024 / (ntime - int(lastValue[2])), 2)
networkInfo['downPackets'] = networkIo[3]
networkInfo['upPackets'] = networkIo[2]
public.writeFile(tmpfile, str(
networkIo[0]) + '|' + str(networkIo[1]) + '|' + str(int(time.time())))
# networkInfo['cpu'] = self.GetCpuInfo(0.1)
return networkInfo
except:
return None
def restartServer(self, get):
if not public.isRestart():
return public.returnMsg(False, 'EXEC_ERR_TASK')
public.ExecShell("sync && /etc/init.d/bt stop && init 6 &")
return public.returnMsg(True, 'SYS_REBOOT')
# 释放内存
def reMemory(self, get):
os.system('sync')
scriptFile = 'script/rememory.sh'
if not os.path.exists(scriptFile):
public.downloadFile(web.ctx.session.home +
'/script/rememory.sh', scriptFile)
public.ExecShell("/bin/bash " + self.setupPath +
'/panel/' + scriptFile)
return self.GetMemInfo()
def getNetWorkIoData(self, start, end):
# 取指定时间段的网络Io
data = public.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 = public.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 = public.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 = public.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'
if stype == '0':
public.execShell("rm -f " + filename)
elif stype == '1':
_day = int(day)
if _day < 1:
return public.returnJson(False, "设置失败!")
public.writeFile(filename, day)
elif stype == 'del':
if not public.isRestart():
return public.returnJson(False, '请等待所有安装任务完成再执行')
os.remove("data/system.db")
sql = db.Sql().dbfile('system')
csql = public.readFile('data/sql/system.sql')
csql_list = csql.split(';')
for index in range(len(csql_list)):
sql.execute(csql_list[index], ())
return public.returnJson(True, "监控服务已关闭")
else:
data = {}
if os.path.exists(filename):
try:
data['day'] = int(public.readFile(filename))
except:
data['day'] = 30
data['status'] = True
else:
data['day'] = 30
data['status'] = False
return public.getJson(data)
return public.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'
for i in range(len(old_list)):
tm_new = int(new_list[i])
tm_old = int(new_list[i])
if tm_new > tm_old:
return 'new'
return ret
def getServerInfo(self):
upAddr = 'https://raw.githubusercontent.com/midoks/mdserver-web/master/version'
try:
version = public.httpGet(
upAddr + '/info.json')
version = json.loads(version)
return version[0]
except Exception as e:
print e
return {}
def updateServer(self, stype, version=''):
# 更新服务
try:
if public.isUpdateLocalSoft():
if stype == 'check' or stype == 'info' or stype == 'update':
return public.returnJson(True, '正在安装中...', 'download')
if stype == 'update_status':
data = public.readFile('tmp/panelExec.log')
if data == 'done':
return public.returnJson(True, '进度!', 100)
else:
_data = json.loads(data)
return public.returnJson(True, '进度!', _data['pre'])
if os.path.exists('mdserver-web.zip'):
return public.returnJson(True, '进度!', 100)
if not public.isRestart():
return public.returnJson(False, '请等待所有安装任务完成再执行!')
if stype == 'check':
version_now = config.config().getVersion()
version_new_info = self.getServerInfo()
if not 'version' in version_new_info:
return public.returnJson(False, '服务器数据有问题!')
diff = self.versionDiff(
version_now, version_new_info['version'])
if diff == 'new':
return public.returnJson(True, '有新版本!', version_new_info['version'])
elif diff == 'test':
return public.returnJson(True, '有测试版本!', version_new_info['version'])
else:
return public.returnJson(False, '已经是最新,无需更新!')
if stype == 'info':
version_new_info = self.getServerInfo()
version_now = config.config().getVersion()
if not 'version' in version_new_info:
return public.returnJson(False, '服务器数据有问题!')
diff = self.versionDiff(
version_now, version_new_info['version'])
return public.returnJson(True, '更新信息!', version_new_info)
if stype == 'update':
if version == '':
return public.returnJson(False, '缺少版本信息!')
v_new_info = self.getServerInfo()
if v_new_info['version'] != version:
return public.returnJson(False, '更新失败,请重试!')
if not 'path' in v_new_info or v_new_info['path'] == '':
return public.returnJson(False, '下载地址不存在!')
execstr = v_new_info['path'] + '|dl|mdserver-web.zip'
taskAdd = (None, '下载[mdserver-web-' + v_new_info['version'] + ']',
'download', '0', time.strftime('%Y-%m-%d %H:%M:%S'), execstr)
public.M('tasks').add(
'id,name,type,status,addtime, execstr', taskAdd)
return public.returnJson(True, '下载中...')
if stype == 'update_install':
public.execShell('unzip -o mdserver-web.zip -d ./')
public.execShell('rm -f mdserver-web.zip')
return public.returnJson(True, '安装更新成功!')
return public.returnJson(False, '已经是最新,无需更新!')
except Exception as ex:
print ex
return public.returnJson(False, "连接服务器失败!")
# 重启面板
def reWeb(self, get):
if not public.isRestart():
public.returnMsg(False, '请等待所有安装任务完成再执行!')
# public.ExecShell('/etc/init.d/bt restart &')
public.returnMsg(True, '执行成功!')
# 修复面板
def repPanel(self, get):
vp = ''
if public.readFile('/www/server/panel/class/common.py').find('checkSafe') != -1:
vp = '_pro'
public.ExecShell("wget -O update.sh " + public.get_url() +
"/install/update" + vp + ".sh && bash update.sh")
if hasattr(web.ctx.session, 'getCloudPlugin'):
del(web.ctx.session['getCloudPlugin'])
return True
@@ -1,23 +1,12 @@
#!/usr/bin/env python
# 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 # 画布宽度
@@ -30,7 +19,7 @@ class vieCode:
__inCurve = True # 是否画干扰线
__inNoise = True # 是否画干扰点
__type = 2 # 验证码类型 1、纯字母 2、数字字母混合
__fontPatn = 'static/fonts/2.ttf' # 字体
__fontPatn = 'class/fonts/2.ttf' # 字体
def GetCodeImage(self, size=80, length=4):
'''获取验证码图片
@@ -86,7 +75,7 @@ class vieCode:
if not self.__inNoise:
return
font = ImageFont.truetype(self.__fontPatn, int(self.__fontSize / 1.5))
for i in range(5):
for i in xrange(5):
# 杂点颜色
noiseColor = (random.randint(150, 200), random.randint(
150, 200), random.randint(150, 200))
@@ -115,9 +104,9 @@ class vieCode:
# 画曲线
color = (random.randint(30, 150), random.randint(
30, 150), random.randint(30, 150))
for x in range(xend):
for x in xrange(xend):
if w != 0:
for k in range(int(self.__heigth / 10)):
for k in xrange(int(self.__heigth / 10)):
y = a * math.sin(w * x + f) + b + self.__heigth / 2
i = int(self.__fontSize / 5)
while i > 0:
@@ -131,7 +120,7 @@ class vieCode:
font = ImageFont.truetype(self.__fontPatn, self.__fontSize)
x = 0
# 打印字符到画板
for i in range(self.__length):
for i in xrange(self.__length):
# 设置字体随机颜色
color = (random.randint(30, 150), random.randint(
30, 150), random.randint(30, 150))
-8
View File
@@ -1,8 +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 -print0 | xargs -0 git rm -f --ignore-unmatch
find . -name .DS_Store | xargs rm -rf
find . -type d -name "*.pyc" | xargs rm -rf
+9 -98
View File
@@ -1,107 +1,26 @@
#!/bin/bash
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin:/opt/homebrew/bin
DIR=$(cd "$(dirname "$0")"; pwd)
MDIR=$(dirname "$DIR")
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export LC_ALL="en_US.UTF-8"
# echo $DIR
PATH=$PATH:$DIR/bin
if [ -f ${DIR}/bin/activate ];then
source ${DIR}/bin/activate
if [ "$?" != "0" ];then
echo "load local python env fail!"
fi
fi
mw_start_task()
{
isStart=$(ps aux |grep 'panel_task.py'|grep -v grep|awk '{print $2}')
if [ "$isStart" == '' ];then
echo -e "starting mw-tasks... \c"
cd $DIR && python3 panel_task.py >> ${DIR}/logs/panel_task.log 2>&1 &
sleep 0.3
isStart=$(ps aux |grep 'panel_task.py'|grep -v grep|awk '{print $2}')
if [ "$isStart" == '' ];then
echo -e "\033[31mfailed\033[0m"
echo '------------------------------------------------------'
tail -n 20 $DIR/logs/panel_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(){
# 后台任务
mw_start_task
cd ${DIR}/web && gunicorn -c setting.py app:app
gunicorn -c setting.py app:app &
python task.py &
}
mw_start_debug(){
if [ ! -f $DIR/logs/panel_task.log ];then
echo '' > $DIR/logs/panel_task.log
fi
python3 panel_task.py >> $DIR/logs/panel_task.log 2>&1 &
port=7200
if [ -f /www/server/mdserver-web/data/port.pl ];then
port=$(cat /www/server/mdserver-web/data/port.pl)
fi
if [ -f ${DIR}/data/port.pl ];then
port=$(cat ${DIR}/data/port.pl)
fi
cd ${DIR}/web && gunicorn -b :${port} -k eventlet -w 1 app:app
# cd ${DIR}/web && gunicorn -b :${port} -k geventwebsocket.gunicorn.workers.GeventWebSocketWorker -w 1 app:app
}
mw_start_panel(){
port=7200
if [ -f ${DIR}/data/port.pl ];then
port=$(cat ${DIR}/data/port.pl)
fi
cd ${DIR}/web && gunicorn -b :${port} -k eventlet -w 1 app:app
# cd ${DIR}/web && gunicorn -b :${port} -k geventwebsocket.gunicorn.workers.GeventWebSocketWorker -w 1 app:app
# gunicorn -c setting.py app:app
python task.py &
gunicorn -b :7200 app:app
# gunicorn -c setting.py app:app
}
mw_start_bgtask(){
cd ${DIR}/web && gunicorn -c setting.py app:app
cd ${DIR} && python3 panel_task.py
}
mw_stop()
{
APP_LIST=`ps -ef|grep app:app |grep -v grep|awk '{print $2}'`
APP_LIST=($APP_LIST)
for p in ${APP_LIST[@]}
do
kill -9 $p > /dev/null 2>&1
done
TASK_LIST=`ps -ef|grep panel_task.py | grep -v grep |awk '{print $2}'`
TASK_LIST=($TASK_LIST)
for p in ${TASK_LIST[@]}
do
kill -9 $p > /dev/null 2>&1
done
zzpids=`ps -A -o stat,ppid,pid | grep -e '^[Zz]' | awk '{print $2}'`
zzpids=($zzpids)
for p in ${zzpids[@]}
do
kill -9 ${p} > /dev/null 2>&1
done
ps -ef|grep app:app |grep -v grep|awk '{print $2}'|xargs kill -9
ps -ef|grep task.py |grep -v grep|awk '{print $2}'|xargs kill -9
}
case "$1" in
@@ -115,12 +34,4 @@ case "$1" in
mw_stop
mw_start_debug
;;
'panel')
mw_stop
mw_start_panel
;;
'task')
# mw_stop
mw_start_bgtask
;;
esac
-86
View File
@@ -1,86 +0,0 @@
### 常用命令说明
- 面板相关命令
```
mw start | 启动面板服务器
mw stop | 停止面板服务器
mw restart | 重启面板服务器
mw default | 显示登录信息
mw db | 快捷连接MySQL
mw redis | 快捷连接Redis
mw valkey | 快捷连接valkey
mw mongodb | 快捷连接MongoDB
mw pgdb | 快捷连接PostgreSQL
----------------------------------------
mw open | 开启面板
mw close | 关闭面板
mw debug | 开发测试
mw venv | 进入虚拟环境
mw mirror | 切换镜像
mw install_app | 快捷安装常用软件
mw update | 更新到正式
mw dev/update_dev | 更新到开发
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
```
-71
View File
@@ -1,71 +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 |✅ |✅ |
| Debian 12.0 |✅ |✅ |
| 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 |PHP82 |PHP83 |PHP84 |
| ----------------- |-------|-------|-------|-------|-------|-------|-------|-------|-------|-------|-------|-------|-------|-------|
| CentOS 7.9 |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |
| CentOS 8.4 |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |
| CentOS 8 Stream |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |
| CentOS 9 Stream |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |
| Debian 10.3 |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |
| Debian 11.3 |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |
| Debian 12 |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |
| 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 |✅ |✅ |✅ |✅ |
| Debian 12 |✅ |✅ |✅ |✅ |
| Ubuntu 18.04 |✅ |✅ |✅ |✅ |
| Ubuntu 20.04 |✅ |✅ |✅ |✅ |
| Ubuntu 22.04 |✅ |✅ |✅ |✅ |
| Fedora 31 |✅ |✅ |✅ |✅ |
| Fedora 32 |✅ |✅ |✅ |✅ |
| AlmaLinux 9 |✅ |✅ |✅ |✅ |
| RockyLinux 8.6 |✅ |✅ |✅ |✅ |
| Arch Linux |✅ |✅ |✅ |✅ |
| openSUSE 15.4 |✅ |✅ |✅ |✅ |
-4
View File
@@ -1,4 +0,0 @@
# 自动生成
certificate.pem - 证书信息
privateKey.pem - 私钥
+32
View File
@@ -0,0 +1,32 @@
[
{
"title":"全部",
"type":0,
"ps":""
},
{
"title":"运行环境",
"type":1,
"ps":""
},
{
"title":"数据软件",
"type":2,
"ps":""
},
{
"title":"代码管理",
"type":3,
"ps":""
},
{
"title":"系统工具",
"type":4,
"ps":""
},
{
"title":"其他插件",
"type":5,
"ps":""
}
]
Executable
+1
View File
@@ -0,0 +1 @@
7200
+1
View File
@@ -0,0 +1 @@
True
+132
View File
@@ -0,0 +1,132 @@
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 `config` (
`id` INTEGER PRIMARY KEY AUTOINCREMENT,
`webserver` TEXT,
`backup_path` TEXT,
`sites_path` TEXT,
`status` INTEGER,
`mysql_root` TEXT
);
INSERT INTO `config` (`id`, `webserver`, `backup_path`, `sites_path`, `status`, `mysql_root`) VALUES
(1, 'nginx', '/www/backup', '/www/wwwroot', 0, 'admin');
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
);
CREATE TABLE IF NOT EXISTS `databases` (
`id` INTEGER PRIMARY KEY AUTOINCREMENT,
`pid` INTEGER,
`name` TEXT,
`username` TEXT,
`password` TEXT,
`accept` TEXT,
`ps` TEXT,
`addtime` 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
(2, '80', '网站默认端口', '0000-00-00 00:00:00'),
(3, '8888', 'WEB面板', '0000-00-00 00:00:00'),
(4, '21', 'FTP服务', '0000-00-00 00:00:00'),
(5, '22', 'SSH远程管理服务', '0000-00-00 00:00:00');
CREATE TABLE IF NOT EXISTS `ftps` (
`id` INTEGER PRIMARY KEY AUTOINCREMENT,
`pid` INTEGER,
`name` TEXT,
`password` TEXT,
`path` TEXT,
`status` TEXT,
`ps` TEXT,
`addtime` TEXT
);
CREATE TABLE IF NOT EXISTS `logs` (
`id` INTEGER PRIMARY KEY AUTOINCREMENT,
`type` TEXT,
`log` TEXT,
`addtime` TEXT
);
CREATE TABLE IF NOT EXISTS `sites` (
`id` INTEGER PRIMARY KEY AUTOINCREMENT,
`name` TEXT,
`path` TEXT,
`status` TEXT,
`index` TEXT,
`ps` TEXT,
`edate` TEXT,
`addtime` 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', '2016-12-10 15:12:56', 0, '287962566@qq.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
);
@@ -1,22 +1,20 @@
CREATE TABLE IF NOT EXISTS `network` (
`id` INTEGER PRIMARY KEY AUTOINCREMENT,
`up` TEXT,
`down` TEXT,
`up` INTEGER,
`down` INTEGER,
`total_up` INTEGER,
`total_down` INTEGER,
`down_packets` INTEGER,
`up_packets` INTEGER,
`addtime` INTEGER
);
CREATE INDEX network_addtime_idx ON network(`addtime`);
CREATE TABLE IF NOT EXISTS `cpuio` (
`id` INTEGER PRIMARY KEY AUTOINCREMENT,
`pro` TEXT,
`mem` TEXT,
`pro` INTEGER,
`mem` INTEGER,
`addtime` INTEGER
);
CREATE INDEX cpuio_addtime_idx ON cpuio(`addtime`);
CREATE TABLE IF NOT EXISTS `diskio` (
`id` INTEGER PRIMARY KEY AUTOINCREMENT,
@@ -28,7 +26,6 @@ CREATE TABLE IF NOT EXISTS `diskio` (
`write_time` INTEGER,
`addtime` INTEGER
);
CREATE INDEX diskio_addtime_idx ON diskio(`addtime`);
CREATE TABLE IF NOT EXISTS `load_average` (
`id` INTEGER PRIMARY KEY AUTOINCREMENT,
@@ -37,5 +34,4 @@ CREATE TABLE IF NOT EXISTS `load_average` (
`five` REAL,
`fifteen` REAL,
`addtime` INTEGER
);
CREATE INDEX load_average_addtime_idx ON load_average(`addtime`);
);
@@ -1,30 +1,24 @@
server
{
# reuseport 只能在一个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
#SSL-START %s
#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-START %s
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-START %s
include enable-php-{$PHPVER}.conf;
#PHP-INFO-END
#REWRITE-START
#REWRITE-START %s
include {$OR_REWRITE}/{$SERVER_NAME}.conf;
#REWRITE-END
@@ -38,16 +32,21 @@ server
location ~ \.well-known{
allow all;
}
location ~ .*\.(gif|jpg|jpeg|png|bmp|swf|js|css|ttf|woff2)$
location ~ .*\\.(gif|jpg|jpeg|png|bmp|swf)$
{
expires 30d;
expires 30d;
error_log off;
access_log /dev/null;
if ($invalid_referer){
return 404;
}
}
access_log {$LOGPATH}/{$SERVER_NAME}.log main;
location ~ .*\\.(js|css)?$
{
expires 12h;
error_log off;
access_log /dev/null;
}
access_log {$LOGPATH}/{$SERVER_NAME}.log;
error_log {$LOGPATH}/{$SERVER_NAME}.error.log;
}
-62
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 && \
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" ]
-9
View File
@@ -1,9 +0,0 @@
[Unit]
Description=Customize the script
After=network.target
[Service]
ExecStart=/start.sh
[Install]
WantedBy=multi-user.target
-1
View File
@@ -1 +0,0 @@
#!/bin/bash
-1
View File
@@ -1 +0,0 @@
logs
-361
View File
@@ -1,361 +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 sys
import os
import json
import time
import threading
import psutil
web_dir = os.getcwd() + "/web"
os.chdir(web_dir)
sys.path.append(web_dir)
import core.mw as mw
import thisdb
from admin import setup
setup.init()
g_log_file = mw.getPanelTaskExecLog()
if not os.path.exists(g_log_file):
os.system("touch " + g_log_file)
def execShell(cmdstring, cwd=None, timeout=None, shell=True):
cmd = cmdstring + ' > ' + g_log_file + ' 2>&1'
return mw.execShell(cmd)
def writeLogs(data):
# 写输出日志
try:
fp = open(g_log_file, 'w+')
fp.write(data)
fp.close()
except:
pass
def downloadFile(url, filename):
# 下载文件
try:
import urllib
import socket
socket.setdefaulttimeout(300)
headers = ('User-Agent', 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36')
opener = urllib.request.build_opener()
opener.addheaders = [headers]
urllib.request.install_opener(opener)
urllib.request.urlretrieve(url, filename=filename, reporthook=downloadHook)
if not mw.isAppleSystem():
os.system('chown www.www ' + filename)
writeLogs(filename + ' download success!')
except Exception as e:
writeLogs(str(e))
return True
def downloadHook(count, blockSize, totalSize):
# 下载文件进度回调
used = count * blockSize
pre = int((100.0 * used / totalSize))
speed = {'total': totalSize, 'used': used, 'pre': pre}
writeLogs(json.dumps(speed))
def runPanelTask():
# 站点过期检查
siteEdateCheck()
lock_file = mw.getTriggerTaskLockFile()
try:
if os.path.exists(lock_file):
bash_list = thisdb.getTaskList(status=-1)
for task in bash_list:
thisdb.setTaskStatus(task['id'], 0)
run_list = thisdb.getTaskList(status=0)
for run_task in run_list:
start = int(time.time())
thisdb.setTaskData(run_task['id'], start=start)
thisdb.setTaskStatus(run_task['id'], -1)
if run_task['type'] == 'download':
argv = run_task['cmd'].split('|mw|')
downloadFile(argv[0], argv[1])
elif run_task['type'] == 'execshell':
execShell(run_task['cmd'])
end = int(time.time())
thisdb.setTaskData(run_task['id'], end=end)
thisdb.setTaskStatus(run_task['id'], 1)
if thisdb.getTaskUnexecutedCount() < 1:
os.remove(lock_file)
except Exception as e:
print('runPanelTask:',mw.getTracebackInfo())
# 网站到期处理
def siteEdateCheck():
try:
from utils.site import sites as MwSites
website_edate = thisdb.getOption('website_edate', default='0000-00-00')
now_time_ymd = time.strftime('%Y-%m-%d', time.localtime())
if website_edate == now_time_ymd:
return False
site_list = thisdb.getSitesEdateList(now_time_ymd)
for site in site_list:
MwSites.instance().stop(site['id'])
thisdb.setOption('website_edate', now_time_ymd)
except Exception as e:
print('siteEdateCheck:',mw.getTracebackInfo())
# 任务队列
def startPanelTask():
try:
while True:
runPanelTask()
time.sleep(5)
except Exception as e:
print('startPanelTask:',mw.getTracebackInfo())
time.sleep(30)
startPanelTask()
def systemTask():
# 系统监控任务
from utils.system import monitor
try:
while True:
monitor_status = thisdb.getOption('monitor_status',type='monitor',default='open')
if monitor_status == 'open':
monitor.instance().run()
time.sleep(5)
except Exception as ex:
print('systemTask:',mw.getTracebackInfo())
time.sleep(30)
systemTask()
def panelPluginStatusCheck():
# 系统监控任务
from utils.plugin import plugin
try:
while True:
# start_t = time.time()
plugin.instance().autoCachePluginStatus()
# end_t = time.time()
time.sleep(60)
except Exception as ex:
print('panelPluginStatusCheck:',mw.getTracebackInfo())
time.sleep(120)
panelPluginStatusCheck()
# -------------------------------------- PHP监控 start --------------------------------------------- #
# 502错误检查线程
def check502Task():
try:
check_file = mw.getPanelDir() + '/data/502Task.pl'
while True:
if os.path.exists(check_file):
check502()
time.sleep(10)
except:
time.sleep(30)
check502Task()
def check502():
try:
verlist = [
'52', '53', '54', '55', '56', '70',
'71', '72', '73', '74', '80', '81',
'82', '83', '84'
]
for ver in verlist:
server_dir = mw.getServerDir()
php_path = server_dir + '/php/' + ver + '/sbin/php-fpm'
if not os.path.exists(php_path):
continue
if checkPHPVersion(ver):
continue
if startPHPVersion(ver):
print('检测到PHP-' + ver + '处理异常,已自动修复!')
mw.writeLog('PHP守护程序', '检测到PHP-' + ver + '处理异常,已自动修复!')
except Exception as e:
mw.writeLog('PHP守护程序', '自动修复异常:'+str(e))
# 处理指定PHP版本
def startPHPVersion(version):
server_dir = mw.getServerDir()
try:
# system
phpService = mw.systemdCfgDir() + '/php' + version + '.service'
if os.path.exists(phpService):
mw.execShell("systemctl restart php" + version)
if checkPHPVersion(version):
return True
# initd
fpm = server_dir + '/php/init.d/php' + version
php_path = server_dir + '/php/' + version + '/sbin/php-fpm'
if not os.path.exists(php_path):
if os.path.exists(fpm):
os.remove(fpm)
return False
if not os.path.exists(fpm):
return False
# 尝试重载服务
os.system(fpm + ' reload')
if checkPHPVersion(version):
return True
# 尝试重启服务
cgi = '/tmp/php-cgi-' + version + '.sock'
pid = server_dir + '/php/' + version + '/var/run/php-fpm.pid'
data = mw.execShell("ps -ef | grep php/" + version +" | grep -v grep|grep -v python |awk '{print $2}'")
if data[0] != '':
os.system("ps -ef | grep php/" + version + " | grep -v grep|grep -v python |awk '{print $2}' | xargs kill ")
time.sleep(0.5)
if not os.path.exists(cgi):
os.system('rm -f ' + cgi)
if not os.path.exists(pid):
os.system('rm -f ' + pid)
os.system(fpm + ' start')
if checkPHPVersion(version):
return True
# 检查是否正确启动
if os.path.exists(cgi):
return True
except Exception as e:
print('startPHPVersion:',mw.getTracebackInfo())
mw.writeLog('PHP守护程序', '自动修复异常:'+str(e))
return True
def checkPHPVersion(version):
# 检查指定PHP版本
try:
sock = mw.getFpmAddress(version)
data = mw.requestFcgiPHP(sock, '/phpfpm_status_' + version + '?json')
result = str(data, encoding='utf-8')
except Exception as e:
result = 'Bad Gateway'
# 检查openresty
if result.find('Bad Gateway') != -1:
return False
if result.find('HTTP Error 404: Not Found') != -1:
return False
# 检查Web服务是否启动
if result.find('Connection refused') != -1:
return False
return True
# -------------------------------------- PHP监控 end --------------------------------------------- #
# --------------------------------------OpenResty Auto Restart Start --------------------------------------------- #
# 解决acme.sh续签后,未起效。
def openrestyAutoRestart():
try:
while True:
# 检查是否安装
odir = mw.getServerDir() + '/openresty'
if not os.path.exists(odir):
time.sleep(86400)
continue
mw.opWeb('reload')
time.sleep(86400)
except Exception as e:
mw.writeLog('OpenResty检测', '自动修复异常:'+str(e))
time.sleep(86400)
# --------------------------------------OpenResty Auto Restart End --------------------------------------------- #
# ------------------------------------ OpenResty Restart At Once Start ------------------------------------------ #
def openrestyRestartAtOnce():
restart_nginx_tip = mw.getPanelDir()+'/data/restart_nginx.pl'
while True:
if os.path.exists(restart_nginx_tip):
os.remove(restart_nginx_tip)
mw.opWeb('reload')
time.sleep(1)
# ----------------------------------- OpenResty Restart At Once End ------------------------------------------ #
# --------------------------------------Panel Restart Start --------------------------------------------- #
def restartPanelService():
restart_tip = mw.getPanelDir()+'/data/restart.pl'
while True:
if os.path.exists(restart_tip):
print("restart panel")
os.remove(restart_tip)
mw.panelCmd('restart_panel')
time.sleep(1)
# --------------------------------------Panel Restart End --------------------------------------------- #
def setDaemon(t):
if sys.version_info.major == 3 and sys.version_info.minor >= 10:
t.daemon = True
else:
t.setDaemon(True)
return t
def run():
# 系统监控
sysTask = threading.Thread(target=systemTask)
sysTask = setDaemon(sysTask)
sysTask.start()
# PHP 502错误检查线程
php502 = threading.Thread(target=check502Task)
php502 = setDaemon(php502)
php502.start()
# OpenResty Restart At Once Start
oraos = threading.Thread(target=openrestyRestartAtOnce)
oraos = setDaemon(oraos)
oraos.start()
# OpenResty Auto Restart Start
oar = threading.Thread(target=openrestyAutoRestart)
oar = setDaemon(oar)
oar.start()
# Panel Plugin Status Check
pps = threading.Thread(target=panelPluginStatusCheck)
pps = setDaemon(pps)
pps.start()
# Panel Restart Start
rps = threading.Thread(target=restartPanelService)
rps = setDaemon(rps)
rps.start()
# 面板后台任务
startPanelTask()
if __name__ == "__main__":
run()
-391
View File
@@ -1,391 +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 sys
import os
import json
import time
import re
web_dir = os.getcwd() + "/web"
os.chdir(web_dir)
sys.path.append(web_dir)
from utils.firewall import Firewall as MwFirewall
import core.mw as mw
import thisdb
INIT_DIR = "/etc/rc.d/init.d"
if mw.isAppleSystem():
INIT_DIR = mw.getPanelDir() + "/scripts/init.d"
INIT_CMD = INIT_DIR + "/mw"
def mw_input_cmd(msg):
if sys.version_info[0] == 2:
in_val = raw_input(msg)
else:
in_val = input(msg)
return in_val
def getRemainLen(cmd, max_length=100):
cmd_len = len(cmd)
cmd_u8_len = len(cmd.encode('utf-8'))
return max_length-int((cmd_u8_len - cmd_len)/2+cmd_len)
def mwcli(mw_input=0):
panel_dir = mw.getPanelDir()
raw_tip = "========================================================================"
if not mw_input:
print("========================mdserver-web cli tools==========================")
cmd_list = [
'(1) 重启面板服务',
'(2) 停止面板服务',
'(3) 启动面板服务',
'(4) 重载面板服务',
'(5) 修改面板IP',
'(6) 修改面板端口',
'(7) 关闭安全入口',
'(10) 查看面板默认信息',
'(11) 修改面板密码',
'(12) 修改面板用户名',
'(13) 显示面板错误日志',
'(14) 关闭面板访问',
'(15) 开启面板访问',
'(20) 关闭BasicAuth认证',
'(21) 解除域名绑定',
'(22) 解除面板SSL绑定',
'(23) 开启IPV6支持',
'(24) 关闭IPV6支持',
'(25) 开启防火墙SSH端口',
'(26) 关闭二次验证',
'(27) 查看防火墙信息',
'(28) 自动识别防火墙端口到面板',
'(29) 自动识别配置站点信息',
'(100) 开启PHP52显示',
'(101) 关闭PHP52显示',
'(200) 切换Linux系统软件源',
'(201) 简单速度测试',
'(0) 取消'
]
cmd_list_num = len(cmd_list)
for index in range(cmd_list_num):
cmd = cmd_list[index]
if index % 2 == 0:
if index == (cmd_list_num-1):
print(cmd)
else:
print(cmd + " " * getRemainLen(cmd, 40), end="")
if index % 2 == 1:
print(cmd)
print(raw_tip)
try:
mw_input = input("请输入命令编号:")
if sys.version_info[0] == 3:
mw_input = int(mw_input)
except:
mw_input = 0
nums = [
1, 2, 3, 4, 5, 6, 7,
10, 11, 12, 13, 14, 15,
20, 21, 22, 23, 24, 25, 26, 27, 28, 29,
100, 101,
200, 201
]
if not mw_input in nums:
print(raw_tip)
print("已取消!")
exit()
if mw_input == 1:
os.system(INIT_CMD + " restart")
elif mw_input == 2:
os.system(INIT_CMD + " stop")
elif mw_input == 3:
os.system(INIT_CMD + " start")
elif mw_input == 4:
os.system(INIT_CMD + " reload")
elif mw_input == 5:
in_ip = mw_input_cmd("请输入设置的面板IP:")
in_ip = in_ip.strip()
ip_text = panel_dir + '/data/iplist.txt'
if not mw.isVaildIp(in_ip):
mw.echoInfo("【"+in_ip+"】: IP不合法")
return
mw.writeFile(ip_text, in_ip)
thisdb.setOption('server_ip', in_ip)
mw.echoInfo("设置面板IP: " + in_ip)
elif mw_input == 6:
in_port = mw_input_cmd("请输入新的面板端口:")
in_port_int = int(in_port.strip())
if in_port_int < 65536 and in_port_int > 0:
MwFirewall.instance().addAcceptPort(in_port, 'WEB面板[TOOLS修改]', 'port')
panel_port = panel_dir + '/data/port.pl'
mw.writeFile(panel_port, in_port)
os.system(INIT_CMD + " restart_panel")
os.system(INIT_CMD + " default")
else:
mw.echoInfo("端口范围在0-65536之间")
return
elif mw_input == 7:
thisdb.setOption('admin_path', '')
mw.echoInfo("关闭安全入口成功!")
elif mw_input == 10:
os.system(INIT_CMD + " default")
elif mw_input == 11:
input_pwd = mw_input_cmd("请输入新的面板密码:")
if len(input_pwd.strip()) < 5:
mw.echoInfo("错误,密码长度不能小于5位")
return
set_panel_pwd(input_pwd.strip(), True)
elif mw_input == 12:
input_user = mw_input_cmd("请输入新的面板用户名(>=5位):")
set_panel_username(input_user.strip())
elif mw_input == 13:
os.system('tail -100 ' + panel_dir + '/logs/panel_error.log')
elif mw_input == 14:
admin_close = thisdb.getOption('admin_close')
if admin_close == 'no':
thisdb.setOption('admin_close', 'yes')
mw.echoInfo("关闭面板访问成功!")
else:
mw.echoInfo("已关闭面板访问!")
elif mw_input == 15:
admin_close = thisdb.getOption('admin_close')
if admin_close == 'yes':
thisdb.setOption('admin_close', 'no')
mw.echoInfo("开启面板访问成功!")
else:
mw.echoInfo("已开启面板访问!")
elif mw_input == 20:
basic_auth = thisdb.getOptionByJson('basic_auth', default={'open':False})
if basic_auth['open']:
basic_auth['open'] = False
thisdb.setOption('basic_auth', json.dumps(basic_auth))
os.system(INIT_CMD + " restart")
mw.echoInfo("关闭basic_auth成功")
elif mw_input == 21:
panel_domain = thisdb.getOption('panel_domain', default='')
if panel_domain != '':
thisdb.setOption('panel_domain', '')
os.system(INIT_CMD + " unbind_domain")
mw.echoInfo("解除域名绑定成功")
else:
mw.echoInfo("面板未绑定域名!")
elif mw_input == 22:
panel_ssl = thisdb.getOptionByJson('panel_ssl', default={'open':False})
if panel_ssl['open']:
panel_ssl['open'] = False
thisdb.setOption('panel_ssl', json.dumps(panel_ssl))
os.system(INIT_CMD + " unbind_ssl")
mw.echoInfo("解除面板SSL绑定成功")
elif mw_input == 23:
listen_ipv6 = panel_dir + '/data/ipv6.pl'
if not os.path.exists(listen_ipv6):
mw.writeFile(listen_ipv6, 'True')
os.system(INIT_CMD + " restart")
mw.echoInfo("开启IPv6支持了")
else:
mw.echoInfo("已开启IPv6支持!")
elif mw_input == 24:
listen_ipv6 = panel_dir + '/data/ipv6.pl'
if not os.path.exists(listen_ipv6):
mw.echoInfo("已关闭IPv6支持!")
else:
os.remove(listen_ipv6)
os.system(INIT_CMD + " restart")
mw.echoInfo("关闭IPv6支持了")
elif mw_input == 25:
open_ssh_port()
mw.echoInfo("已开启!")
elif mw_input == 26:
two_step_verification = thisdb.getOptionByJson('two_step_verification', default={'open':False})
if two_step_verification['open']:
two_step_verification['open'] = False
thisdb.setOption('two_step_verification', json.dumps(two_step_verification))
mw.echoInfo("关闭二次验证成功!")
else:
mw.echoInfo("二次验证已关闭!")
elif mw_input == 27:
cmd = 'which ufw'
run_cmd = False
find_cmd = mw.execShell(cmd)
if find_cmd[0].strip() != '':
run_cmd = True
os.system('ufw status')
cmd = 'which firewall-cmd'
find_cmd = mw.execShell(cmd)
if find_cmd[0].strip() != '':
run_cmd = True
os.system('firewall-cmd --list-all')
if not run_cmd:
mw.echoInfo("未检测到防火墙!")
elif mw_input == 28:
MwFirewall.instance().aIF()
mw.echoInfo("执行自动识别防火墙端口到面板成功!")
elif mw_input == 29:
from utils.site_reflect import parse as MwParse
MwParse()
mw.echoInfo("自动识别配置站点信息成功!")
elif mw_input == 100:
php_conf = panel_dir + '/plugins/php/info.json'
if os.path.exists(php_conf):
cont = mw.readFile(php_conf)
cont = re.sub("\"53\"", "\"52\",\"53\"", cont)
cont = re.sub("\"5.3.29\"", "\"5.2.17\",\"5.3.29\"", cont)
mw.writeFile(php_conf, cont)
mw.echoInfo("执行PHP52显示成功!")
elif mw_input == 101:
php_conf = panel_dir + '/plugins/php/info.json'
if os.path.exists(php_conf):
cont = mw.readFile(php_conf)
cont = re.sub("\"52\",", "", cont)
cont = re.sub("\"5.2.17\",", cont)
mw.writeFile(php_conf, cont)
mw.echoInfo("执行PHP52隐藏成功!")
elif mw_input == 200:
os.system("bash <(curl -sSL https://linuxmirrors.cn/main.sh)")
# os.system(INIT_CMD + " mirror")
elif mw_input == 201:
os.system('curl -Lso- bench.sh | bash')
def open_ssh_port():
find_ssh_port_cmd = "cat /etc/ssh/sshd_config | grep '^Port \\d*' | tail -1"
cmd_data = mw.execShell(find_ssh_port_cmd)
ssh_port = cmd_data[0].replace("Port ", '').strip()
if ssh_port == '':
ssh_port = '22'
mw.echoInfo("SSH端口: "+ str(ssh_port))
MwFirewall.instance().addAcceptPort(ssh_port, 'SSH远程管理服务', 'port')
return True
def set_panel_pwd(password, ncli=False):
info = thisdb.getUserByRoot()
thisdb.setUserByRoot(password=password)
if ncli:
mw.echoInfo("username: " + info['name'])
mw.echoInfo("password: " + password)
else:
print(username)
def show_panel_pwd():
# 面板密码展示
info = thisdb.getUserByRoot()
defailt_pwd_file = mw.getPanelDir()+'/data/default.pl'
pwd = ''
if os.path.exists(defailt_pwd_file):
pwd = mw.readFile(defailt_pwd_file).strip()
if mw.md5(pwd) == info['password']:
mw.echoInfo('password: ' + pwd)
return
print("*-password has been changed!")
def show_panel_adminpath():
admin_path = thisdb.getOption('admin_path')
if admin_path == '':
print('/login')
else:
print('/'+admin_path)
def set_panel_username(username=None):
# 随机面板用户名
if username:
if len(username) < 5:
mw.echoInfo("错误,用户名长度不能少于5位")
return
if username in ['admin', 'root']:
mw.echoInfo("错误,不能使用过于简单的用户名")
return
thisdb.setUserByRoot(name=username)
mw.echoInfo("username: %s" % username)
return
info = thisdb.getUserByRoot()
if info['name'] == 'admin':
username = mw.getRandomString(8).lower()
thisdb.setUserByRoot(name=username)
mw.echoInfo('username: ' + info['name'])
def getServerIp():
version = sys.argv[2]
# ip = mw.execShell(
# "curl --insecure -{} -sS --connect-timeout 5 -m 60 https://v6r.ipip.net/?format=text".format(version))
ip = mw.execShell(
"curl --insecure -{} -sS --connect-timeout 5 -m 60 https://ip.cachecha.com/?format=text".format(version))
# print(ip[0])
return ip[0]
def getPanelSslType():
scheme = 'http'
panel_ssl = thisdb.getOptionByJson('panel_ssl', default={'open':False})
if panel_ssl['open']:
scheme = 'https'
return scheme
def getPanelBindDomain():
return thisdb.getOption('panel_domain', default='')
def main():
if len(sys.argv) == 1:
print('ERROR: Parameter error!')
exit(-2)
method = sys.argv[1]
if method == 'panel':
set_panel_pwd(sys.argv[2])
elif method == 'username':
if len(sys.argv) > 2:
set_panel_username(sys.argv[2])
else:
set_panel_username()
elif method == 'password':
show_panel_pwd()
elif method == 'test':
thisdb.getOption('admin_path')
elif method == 'admin_path':
show_panel_adminpath()
elif method == 'getServerIp':
print(getServerIp())
elif method == 'panel_ssl_type':
print(getPanelSslType())
elif method == 'panel_bind_domain':
print(getPanelBindDomain())
elif method == "cli":
clinum = 0
try:
if len(sys.argv) > 2:
clinum = int(sys.argv[2]) if sys.argv[2][:6] else sys.argv[2]
except:
clinum = sys.argv[2]
mwcli(clinum)
else:
print('ERROR: Parameter error')
if __name__ == "__main__":
main()
Binary file not shown.

After

Width:  |  Height:  |  Size: 181 B

+16
View File
@@ -0,0 +1,16 @@
<div class="bt-form">
<div class="bt-w-main">
<div class="bt-w-menu">
<p class="bgw" onclick="pluginService('abkill');">服务</p>
<p onclick="pluginInitD('abkill');">自启动</p>
<p onclick="pluginConfig('abkill');">黑名单</p>
<p onclick="pluginConfig('abkill');">异常日志</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=abkill&f=js/abkill.js");
</script>
+172
View File
@@ -0,0 +1,172 @@
# coding: utf-8
import time
import random
import os
import urllib
import binascii
import json
import re
import sys
import subprocess
sys.path.append(os.getcwd() + "/class/core")
import public
app_debug = False
if public.getOs() == 'darwin':
app_debug = True
def getPluginName():
return 'abkill'
def getPluginDir():
return public.getPluginDir() + '/' + getPluginName()
def getServerDir():
return public.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(':')
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 initDreplace():
initd_file = getInitDFile()
if not os.path.exists(initd_file):
return getServerDir()
return initd_file
def status():
data = public.execShell(
"ps -ef|grep " + getPluginName() + " |grep -v grep | grep -v python | awk '{print $2}'")
if data[0] == '':
return 'stop'
return 'start'
def csvnOp(method):
if app_debug:
os_name = public.getOs()
if os_name == 'darwin':
return "Apple Computer does not support"
_initd_csvn = '/etc/init.d/csvn'
_initd_csvn_httpd = '/etc/init.d/csvn-httpd'
#_csvn = getServerDir() + '/bin/csvn'
#_csvn_httpd = getServerDir() + '/bin/csvn-httpd'
ret_csvn_httpd = public.execShell(_initd_csvn_httpd + ' ' + method)
# ret_csvn = public.execShell(_initd_csvn + ' ' + method)
subprocess.Popen(_initd_csvn + ' ' + method,
stdout=subprocess.PIPE, shell=True)
if ret_csvn_httpd[1] == '':
return 'ok'
return 'fail'
def start():
return csvnOp('start')
def stop():
return csvnOp('stop')
def restart():
return csvnOp('restart')
def reload():
return csvnOp('reload')
def initdStatus():
if not app_debug:
if public.getOs() == 'darwin':
return "Apple Computer does not support"
_initd_csvn = '/etc/init.d/csvn'
_initd_csvn_httpd = '/etc/init.d/csvn-httpd'
if os.path.exists(_initd_csvn) and os.path.exists(_initd_csvn_httpd):
return 'ok'
return 'fail'
def initdInstall():
import shutil
if not app_debug:
if public.getOs() == 'darwin':
return "Apple Computer does not support"
_csvn = getServerDir() + '/bin/csvn'
_csvn_httpd = getServerDir() + '/bin/csvn-httpd'
ret_csvn = public.execShell(_csvn + ' install')
ret_csvn_httpd = public.execShell(_csvn_httpd + ' install')
if ret_csvn[1] == '' and ret_csvn_httpd[1] == '':
return 'ok'
return 'fail'
def initdUinstall():
if not app_debug:
if public.getOs() == 'darwin':
return "Apple Computer does not support"
_csvn = getServerDir() + '/bin/csvn'
_csvn_httpd = getServerDir() + '/bin/csvn-httpd'
ret_csvn = public.execShell(_csvn + ' remove')
ret_csvn_httpd = public.execShell(_csvn_httpd + ' remove')
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()
else:
print 'fail'
+16
View File
@@ -0,0 +1,16 @@
{
"sort": 7,
"ps": "异常程序监控和KILL",
"shell": "install.sh",
"name": "abkill",
"title": "异常程序监控",
"versions": "0.1",
"updates":"0.1",
"tip": "soft",
"checks": "server/abkill",
"author": "midoks",
"date": "201-04-01",
"home": "https://www.collab.net",
"type": "异常程序监控和KILL",
"pid": "4"
}
+76
View File
@@ -0,0 +1,76 @@
#!/bin/bash
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
ak_path={$SERVER_PATH}/abkill
ak_start(){
isStart=$(ps aux |grep 'abkill.py'|grep -v grep|awk '{print $2}')
if [ "$isStart" == '' ];then
echo -e "Starting abkill... \c"
cd $ak_path && nohup python abkill.py >> $ak_path/task.log 2>&1 &
sleep 0.3
isStart=$(ps aux |grep 'abkill.py'|grep -v grep|awk '{print $2}')
if [ "$isStart" == '' ];then
echo -e "\033[31mfailed\033[0m"
echo '------------------------------------------------------'
tail -n 20 $ak_path/task.log
echo '------------------------------------------------------'
echo -e "\033[31mError: abkill service startup failed.\033[0m"
return;
fi
echo -e "\033[32mdone\033[0m"
else
echo "Starting abkill(pid $isStart) already running"
fi
}
ak_stop()
{
echo -e "Stopping abkill... \c";
pids=$(ps aux | grep 'abkill.py' | grep -v grep|awk '{print $2}')
arr=($pids)
for p in ${arr[@]}
do
kill -9 $p
done
echo -e "\033[32mdone\033[0m"
}
ak_status()
{
isStart=$(ps aux|grep 'abkill.py'|grep -v grep|awk '{print $2}')
if [ "$isStart" != '' ];then
echo -e "\033[32mabkill (pid $(echo $isStart)) already running\033[0m"
else
echo -e "\033[31mabkill not running\033[0m"
fi
}
ak_reload()
{
isStart=$(ps aux|grep 'abkill.py'|grep -v grep|awk '{print $2}')
if [ "$isStart" != '' ];then
ak_stop
ak_start
else
echo -e "\033[31mmw not running\033[0m"
mw_start
fi
}
case "$1" in
'start') ak_start;;
'stop') ak_stop;;
'reload') ak_reload;;
'restart')
ak_stop
sleep 0.3
ak_start;;
'status') ak_status;;
esac
+32
View File
@@ -0,0 +1,32 @@
#!/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/bt_install.pl
Install_abkill()
{
echo '正在安装脚本文件...' > $install_tmp
mkdir -p $serverPath/abkill
echo '0.1' > $serverPath/abkill/version.pl
echo '安装完成' > $install_tmp
}
Uninstall_abkill()
{
rm -f $serverPath/abkill
echo "卸载完成" > $install_tmp
}
action=$1
if [ "${1}" == 'install' ];then
Install_abkill
else
Uninstall_abkill
fi
+1
View File
@@ -0,0 +1 @@
pluginService('abkill');
+85
View File
@@ -0,0 +1,85 @@
# coding: utf-8
# 使用示例:
# 1、将此文件重命名为btkill.py , 然后上传到服务器/root目录
# 2、执行 python /root/btkill.py
import time
import os
import sys
sys.path.append("/usr/local/lib/python2.7/site-packages")
import psutil
class btkill:
__limit = 10 # Cpu使用率触发上限
__vmsize = 1048576 / 4 # 虚拟内存触发上限(字节)
def checkMain(self):
pids = psutil.pids()
print pids
num = 0
for pid in pids:
try:
p = psutil.Process(pid)
if p.exe() == "":
continue
name = p.name()
if self.whiteList(name):
continue
cputimes = p.cpu_times()
if cputimes.user < 0.1:
continue
print p
percent = p.cpu_percent(interval=1)
vm = p.memory_info().vms
if percent > self.__limit or vm > self.__vmsize:
log = time.strftime('%Y-%m-%d %X', time.localtime()) + " (PID=" + str(
pid) + ", NAME=" + name + ", VMS=" + str(vm) + ", PERCENT=" + str(percent) + "%)"
# p.kill()
num += 1
print log + " >> killed\n"
except Exception as ex:
print str(ex)
return num
# 检查白名单
def whiteList(self, name):
wlist = ['yum', 'apt-get', 'apt', 'redis-cli', 'memcached', 'sshd', 'vm', 'vim', 'htop', 'top', 'sh', 'bash', 'zip', 'gzip', 'rsync',
'tar', 'unzip', 'php', 'composer', 'pkill', 'mongo', 'mongod', 'php-fpm', 'nginx', 'httpd', 'lsof', 'ps', 'redis-server',
'mysqld', 'mysqld_safe', 'mysql', 'pure-ftpd', 'sparse_dd', 'stunnel', 'squeezed', 'vncterm', 'awk', 'ruby', 'postgres',
'mpathalert', 'vncterm', 'multipathd', 'fe', 'elasticsyslog', 'syslogd', 'v6d', 'xapi', 'screen', 'runsvdir', 'svlogd',
'java', 'udevd', 'ntpd', 'irqbalance', 'qmgr', 'wpa_supplicant', 'mysqld_safe', 'sftp-server', 'lvmetad', 'gitlab-web',
'pure-ftpd', 'auditd', 'master', 'dbus-daemon', 'tapdisk', 'sshd', 'init', 'ksoftirqd', 'kworker', 'kmpathd',
'kmpath_handlerd', 'python', 'kdmflush', 'bioset', 'crond', 'kthreadd', 'migration', 'rcu_sched', 'kjournald',
'gcc', 'gcc++', 'nginx', 'mysqld', 'php-cgi', 'login', 'firewalld', 'iptables', 'systemd', 'network', 'dhclient',
'systemd-journald', 'NetworkManager', 'systemd-logind', 'systemd-udevd', 'polkitd', 'tuned', 'rsyslogd', 'AliYunDunUpdate', 'AliYunDun', 'sendmail']
wslist = ['vif', 'qemu', 'scsi_eh', 'xcp',
'xen', 'docker', 'yunsuo', 'aliyun', 'PM2']
for key in wlist:
if key == name:
return True
for key in wslist:
if name.find(key) != -1:
return True
return False
# 开始处理
def start(self):
num = 0
while True:
num += self.checkMain()
print "查杀完成, 共查杀[" + str(num) + "]个异常进程!"
time.sleep(3)
print '======================================='
print "查杀完成, 共查杀[" + str(num) + "]个异常进程!"
if __name__ == "__main__":
print "正在检测异常进程..."
print '======================================='
c = btkill()
c.start()
@@ -1,34 +0,0 @@
CREATE TABLE IF NOT EXISTS `dnsapi` (
`id` INTEGER PRIMARY KEY AUTOINCREMENT,
`name` TEXT,
`type` TEXT,
`val` TEXT,
`remark` TEXT,
`addtime` TEXT
);
CREATE TABLE IF NOT EXISTS `email` (
`id` INTEGER PRIMARY KEY AUTOINCREMENT,
`addr` TEXT,
`remark` TEXT,
`addtime` TEXT
);
CREATE TABLE IF NOT EXISTS `domain` (
`id` INTEGER PRIMARY KEY AUTOINCREMENT,
`domain` TEXT,
`dnsapi_id` TEXT,
`email` TEXT,
`remark` TEXT,
`effective_date` TEXT default '',
`expiration_date` TEXT default '',
`error` TEXT,
`status` INTEGER default '0',
`addtime` TEXT
);
-- ALTER TABLE `domain` ADD COLUMN `effective_date` TEXT DEFAULT '';
-- ALTER TABLE `domain` ADD COLUMN `expiration_date` TEXT DEFAULT '';
-- ALTER TABLE `domain` ADD COLUMN `error` TEXT DEFAULT '';
-- ALTER TABLE `domain` ADD COLUMN `status` INTEGER DEFAULT '0';
@@ -1,136 +0,0 @@
# coding:utf-8
import sys
import io
import os
import time
import re
import requests
import base64
# 8001 / 7788
goedge_addr = 'http://127.0.0.2:8009'
access_keyid = "xxx"
access_key = "xxx"
# 指定用户
userId = 1
if sys.platform != "darwin":
os.chdir("/www/server/mdserver-web")
web_dir = os.getcwd() + "/web"
if os.path.exists(web_dir):
sys.path.append(web_dir)
os.chdir(web_dir)
import core.mw as mw
domain = sys.argv[1]
ssl_path = sys.argv[2]
def getToken():
api_url = goedge_addr+'/APIAccessTokenService/getAPIAccessToken'
post_data = {
"type": "admin",
"accessKeyId": access_keyid,
"accessKey": access_key
}
data = requests.post(api_url,json=post_data)
data_obj = data.json()
return data_obj['data']['token']
token = getToken()
def commonReq(url, data):
headers = {
'X-Edge-Access-Token': token
}
api_url = goedge_addr+url
resp_data = requests.post(api_url,json=data, headers=headers)
return resp_data.json()
def listSSLCerts(domain):
request_data = {
"userId":userId,
"isCA":False,
"keyword": "ACME泛域名自动上传",
"domains":[domain,"*."+domain],
"size":1
}
response_data = commonReq('/SSLCertService/listSSLCerts', request_data)
data = response_data['data']['sslCertsJSON']
data = mw.base64StrDecode(data)
data = mw.getObjectByJson(data)
# print(data)
return data
# createSSLCert(domain)
def createSSLCert(domain, did=0):
ssl_cer_file = ssl_path + '/'+domain+'.cer'
if not os.path.exists(ssl_cer_file):
print("没有有效证书!")
return ''
# print(ssl_cer_file)
ssl_info = mw.getCertName(ssl_cer_file)
cer_data = mw.readFile(ssl_cer_file)
cer_data = mw.base64StrEncode(cer_data)
# print('cer',cer_data)
ssl_key_file = ssl_path + '/'+domain+'.key'
key_data = mw.readFile(ssl_key_file)
key_data = mw.base64StrEncode(key_data)
# print('ssl_info',ssl_info)
timeBeginAt = int(time.mktime(time.strptime(ssl_info['notBefore'], "%Y-%m-%d")))
timeEndAt = int(time.mktime(time.strptime(ssl_info['notAfter'], "%Y-%m-%d")))
request_data = {
"isOn":True,
"userId":userId,
"name": "ACME泛域名自动上传",
"isCA":False,
"description":domain,
"serverName":domain,
"certData":cer_data,
"keyData":key_data,
"timeBeginAt":timeBeginAt,
"timeEndAt": timeEndAt,
"dnsNames":[domain,"*."+domain],
"commonNames":[ssl_info['issuer']]
}
if did>0:
request_data['sslCertId'] = did
# print(request_data)
response_data = commonReq('/SSLCertService/updateSSLCert', request_data)
print('更新成功',domain,response_data)
return response_data
else:
# print(request_data)
response_data = commonReq('/SSLCertService/createSSLCert', request_data)
print('创建成功',domain,response_data)
return response_data
return response_data
def autoSyncDomain(domain):
data = listSSLCerts(domain)
if len(data) > 0 :
did = data[0]['id']
createSSLCert(domain,did)
else:
createSSLCert(domain)
print(data)
autoSyncDomain(domain)
print(domain,ssl_path)
@@ -1,12 +0,0 @@
# coding:utf-8
import sys
import io
import os
import time
import re
domain = sys.argv[1]
path = sys.argv[2]
print(domain,path)
Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.0 KiB

@@ -1,34 +0,0 @@
<style>
.overflow_hide {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
vertical-align: middle;
}
</style>
<div class="bt-form">
<div class='plugin_version'></div>
<div class="bt-w-main">
<div class="bt-w-menu">
<p class="bgw" onclick="pluginService('acme_pandominassl_apply');">服务</p>
<p onclick="dnsapiList();">DNSAPI *</p>
<p onclick="emailList();">邮件地址 </p>
<p onclick="domainList()">域名SSL *</p>
<p onclick="pluginConfigTpl('acme_pandominassl_apply',$('.plugin_version').attr('version'));">HOOK</p>
<p onclick="pluginLogs('acme_pandominassl_apply','','run_log');">日志</p>
<p onclick="apaReadme();">相关说明</p>
</div>
<div class="bt-w-con pd15">
<div class="soft-man-con"></div>
</div>
</div>
</div>
<script type="text/javascript">
resetPluginWinWidth(1000);
resetPluginWinHeight(550);
$.getScript( "/plugins/file?name=acme_pandominassl_apply&f=js/common.js", function(){
pluginService('acme_pandominassl_apply', $('.plugin_version').attr('version'));
});
</script>
-863
View File
@@ -1,863 +0,0 @@
# coding:utf-8
import sys
import io
import os
import time
import re
import requests
import json
web_dir = os.getcwd() + "/web"
if os.path.exists(web_dir):
sys.path.append(web_dir)
os.chdir(web_dir)
import core.mw as mw
app_debug = False
if mw.isAppleSystem():
app_debug = True
def getPluginName():
return 'acme_pandominassl_apply'
def getPluginDir():
return mw.getPluginDir() + '/' + getPluginName()
def getServerDir():
return mw.getServerDir() + '/' + getPluginName()
def getInitDFile():
current_os = mw.getOs()
if current_os == 'darwin':
return '/tmp/' + getPluginName()
if current_os.startswith('freebsd'):
return '/etc/rc.d/' + getPluginName()
return '/etc/init.d/' + getPluginName()
def getConf():
path = getServerDir() + "/hook.py"
return path
def getInitDTpl():
path = getPluginDir() + "/init.d/" + getPluginName() + ".tpl"
return path
def runLog():
return getServerDir() + '/hook.log'
def getArgs():
args = sys.argv[3:]
# print(args)
tmp = {}
args_len = len(args)
if args_len == 1:
t = args[0].strip('{').strip('}')
if t.strip() == '':
tmp = []
else:
t = t.split(':')
tmp[t[0]] = t[1]
tmp[t[0]] = t[1]
elif args_len > 1:
for i in range(len(args)):
t = args[i].split(':')
tmp[t[0]] = t[1]
return tmp
def checkArgs(data, ck=[]):
for i in range(len(ck)):
if not ck[i] in data:
return (False, mw.returnJson(False, '参数:(' + ck[i] + ')没有!'))
return (True, mw.returnJson(True, 'ok'))
def 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'
def configTpl():
path = getPluginDir() + '/hooks'
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 getPidFile():
file = getConf()
content = mw.readFile(file)
rep = r'pidfile\s*(.*)'
tmp = re.search(rep, content)
return tmp.groups()[0].strip()
def status():
return 'start'
def contentReplace(content):
service_path = mw.getServerDir()
content = content.replace('{$ROOT_PATH}', mw.getFatherDir())
content = content.replace('{$SERVER_PATH}', service_path)
content = content.replace('{$SERVER_APP}', service_path + '/redis')
content = content.replace('{$REDIS_PASS}', mw.getRandomString(10))
return content
def pSqliteDb(dbname='dnsapi'):
file = getServerDir() + '/acme.db'
name = 'acme'
import_sql = mw.readFile(getPluginDir() + '/conf/acme.sql')
md5_sql = mw.md5(import_sql)
import_sign = False
save_md5_file = getServerDir() + '/acme.md5'
if os.path.exists(save_md5_file):
save_md5_sql = mw.readFile(save_md5_file)
if save_md5_sql != md5_sql:
import_sign = True
mw.writeFile(save_md5_file, md5_sql)
else:
mw.writeFile(save_md5_file, md5_sql)
if not os.path.exists(file) or import_sql:
conn = mw.M(dbname).dbPos(getServerDir(), name)
csql_list = import_sql.split(';')
for index in range(len(csql_list)):
conn.execute(csql_list[index], ())
conn = mw.M(dbname).dbPos(getServerDir(), name)
return conn
def initDreplace():
file_tpl = getInitDTpl()
initD_path = getServerDir() + '/init.d'
if not os.path.exists(initD_path):
os.mkdir(initD_path)
file_bin = initD_path + '/' + getPluginName()
run_log_file = runLog()
if not os.path.exists(run_log_file):
mw.writeFile(run_log_file,'')
hook_file = getConf()
if not os.path.exists(hook_file):
mw.writeFile(hook_file,'')
return file_bin
def apaOp(method):
file = initDreplace()
return 'ok'
# current_os = mw.getOs()
# if current_os == "darwin":
# data = mw.execShell(file + ' ' + method)
# if data[1] == '':
# return 'ok'
# return data[1]
# if current_os.startswith("freebsd"):
# data = mw.execShell('service ' + getPluginName() + ' ' + method)
# if data[1] == '':
# return 'ok'
# return data[1]
# data = mw.execShell('systemctl ' + method + ' ' + getPluginName())
# if data[1] == '':
# return 'ok'
# return data[1]
def start():
import tool_cron
tool_cron.createBgTask()
return apaOp('start')
def stop():
import tool_cron
tool_cron.removeBgTask()
return apaOp('stop')
def restart():
status = apaOp('restart')
return status
def reload():
return apaOp('reload')
def dnsapiAdd():
conn = pSqliteDb('dnsapi')
args = getArgs()
data = checkArgs(args,['id','name', 'type', 'remark'])
if not data[0]:
return data[1]
name = args['name'].strip()
remark = args['remark'].strip()
stype = args['type'].strip()
val = args['val'].strip()
sid = args['id'].strip()
if name == '':
return mw.returnJson(False, '名称不能为空!')
if sid != '0' : #修改操作
conn.where("id=?", (sid,)).update({
'name':name,
'type':stype,
'val':val,
'remark':remark,
})
return mw.returnJson(True, '修改成功!')
if conn.where("name=?", (name,)).count():
return mw.returnJson(False, name+'已存在!')
addTime = time.strftime('%Y-%m-%d %X', time.localtime())
err = conn.add('name,type,val,remark,addtime', (name, stype, val,remark, addTime))
# print(err)
return mw.returnJson(True, '添加成功!')
def dnsapiDel():
args = getArgs()
data = checkArgs(args, ['id', 'name'])
if not data[0]:
return data[1]
conn = pSqliteDb('dnsapi')
try:
sid = args['id']
name = args['name']
conn.where("id=?", (sid,)).delete()
return mw.returnJson(True, '删除成功!')
except Exception as ex:
return mw.returnJson(False, '删除失败!' + str(ex))
def dnsapiList():
args = getArgs()
page = 1
page_size = 10
search = ''
data = {}
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']
conn = pSqliteDb('dnsapi')
limit = str((page - 1) * page_size) + ',' + str(page_size)
condition = ''
if not search == '':
condition = "name like '%" + search + "%'"
field = 'id,name,type,val,remark,addtime'
clist = conn.where(condition, ()).field(
field).limit(limit).order('id desc').select()
count = conn.where(condition, ()).count()
_page = {}
_page['count'] = count
_page['p'] = page
_page['row'] = page_size
_page['tojs'] = 'dbList'
data['page'] = mw.getPage(_page)
data['data'] = clist
return mw.getJson(data)
def dnsapiListAll():
conn = pSqliteDb('dnsapi')
field = 'id,name,type,val,remark,addtime'
data = conn.field(field).limit('1000').select()
return mw.getJson(data)
def emailList():
args = getArgs()
page = 1
page_size = 10
search = ''
data = {}
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']
conn = pSqliteDb('email')
limit = str((page - 1) * page_size) + ',' + str(page_size)
condition = ''
if not search == '':
condition = "addr like '%" + search + "%'"
field = 'id,addr,remark,addtime'
clist = conn.where(condition, ()).field(
field).limit(limit).order('id desc').select()
count = conn.where(condition, ()).count()
_page = {}
_page['count'] = count
_page['p'] = page
_page['row'] = page_size
_page['tojs'] = 'dbList'
data['page'] = mw.getPage(_page)
data['data'] = clist
return mw.getJson(data)
def emailAdd():
args = getArgs()
data = checkArgs(args,['addr', 'remark'])
if not data[0]:
return data[1]
addr = args['addr'].strip()
remark = args['remark'].strip()
if addr == '':
return mw.returnJson(False, '邮件地址不能为空!')
conn = pSqliteDb('email')
addTime = time.strftime('%Y-%m-%d %X', time.localtime())
conn.add('addr,remark,addtime', (addr, remark, addTime))
return mw.returnJson(True, '添加成功!')
def emailDel():
args = getArgs()
data = checkArgs(args, ['id', 'name'])
if not data[0]:
return data[1]
conn = pSqliteDb('email')
try:
sid = args['id']
name = args['name']
conn.where("id=?", (sid,)).delete()
return mw.returnJson(True, '删除成功!')
except Exception as ex:
return mw.returnJson(False, '删除失败!' + str(ex))
def domainAdd():
args = getArgs()
data = checkArgs(args,['id','domain', 'dnsapi_id','email','remark'])
if not data[0]:
return data[1]
sid = args['id'].strip()
domain = args['domain'].strip()
remark = args['remark'].strip()
email = args['email'].strip()
dnsapi_id = args['dnsapi_id'].strip()
if domain == '':
return mw.returnJson(False, '域名不能为空!')
if email == '':
return mw.returnJson(False, '邮件不能为空!')
conn = pSqliteDb('domain')
if sid != '0' : #修改操作
conn.where("id=?", (sid,)).update({
'domain':domain,
'dnsapi_id':dnsapi_id,
'email':email,
'remark':remark,
})
return mw.returnJson(True, '修改成功!')
if conn.where("domain=?", (domain,)).count():
return mw.returnJson(False, domain+'已存在!')
addTime = time.strftime('%Y-%m-%d %X', time.localtime())
conn.add('domain,dnsapi_id,email,remark,addtime', (domain, dnsapi_id,email,remark, addTime))
return mw.returnJson(True, '添加成功!')
def domainDel():
args = getArgs()
data = checkArgs(args, ['id', 'name'])
if not data[0]:
return data[1]
conn = pSqliteDb('domain')
try:
sid = args['id']
name = args['name']
conn.where("id=?", (sid,)).delete()
return mw.returnJson(True, '删除成功!')
except Exception as ex:
return mw.returnJson(False, '删除失败!' + str(ex))
def domainStatusToggle():
args = getArgs()
data = checkArgs(args, ['id'])
if not data[0]:
return data[1]
conn = pSqliteDb('domain')
field = 'id,status'
fdata = conn.field(field).where('id=?',(args['id'],)).find()
fstatus = fdata['status']
if fstatus == 0:
fstatus = 1
else:
fstatus = 0
# print(fdata['status'],fstatus)
conn.where("id=?", (args['id'],)).update({
'status':fstatus,
})
return mw.returnJson(True, '切换成功!')
def domainList():
args = getArgs()
page = 1
page_size = 10
search = ''
data = {}
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']
conn = pSqliteDb('domain')
conn_dnsapi = pSqliteDb('dnsapi')
limit = str((page - 1) * page_size) + ',' + str(page_size)
condition = ''
if not search == '':
condition = "domain like '%" + search + "%'"
field = 'id,domain,dnsapi_id,email,status,effective_date,expiration_date,remark,addtime'
clist = conn.where(condition, ()).field(
field).limit(limit).order('id desc').select()
for i in range(len(clist)):
tdata = conn_dnsapi.field('id,name').where('id=?',(clist[i]['dnsapi_id'],)).select()
if len(tdata) > 0:
# print(tdata[0]['name'])
clist[i]['dnsapi_id_alias'] = clist[i]['dnsapi_id']+'('+ tdata[0]['name'] +')'
else:
clist[i]['dnsapi_id_alias'] = clist[i]['dnsapi_id']+' (无)'
count = conn.where(condition, ()).count()
_page = {}
_page['count'] = count
_page['p'] = page
_page['row'] = page_size
_page['tojs'] = 'dbList'
data['page'] = mw.getPage(_page)
data['data'] = clist
return mw.getJson(data)
def getDnsapiData(dnsapi_id):
if dnsapi_id == '0':
return {}
conn_dnsapi = pSqliteDb('dnsapi')
tdata = conn_dnsapi.field('id,name,type,val').where('id=?',(dnsapi_id,)).select()
if len(tdata)>0:
return tdata[0]
return {}
def getDnsapiExportVar(val):
cmd_list = val.split('~')
def_var = ''
for x in range(len(cmd_list)):
v = cmd_list[x]
vlist = v.split('|')
def_var += 'export '+vlist[0]+'="'+vlist[1]+'"\n'
return def_var
def getDnsapiKv(val):
cmd_list = val.split('~')
def_var = {}
for x in range(len(cmd_list)):
v = cmd_list[x]
vlist = v.split('|')
kk = vlist[0]
vv = vlist[1]
def_var[kk] = vv
return def_var
def domainApplyPathJudge(domain):
acme_dir = "/root/.acme.sh"
if mw.isAppleSystem():
user = getRunUser()
acme_dir = "/Users/"+user+"/.acme.sh"
abs_domain_path = acme_dir+'/'+domain+'_ecc'
# print(abs_domain_path)
if os.path.exists(abs_domain_path):
return True, abs_domain_path
abs_domain_path = acme_dir+'/'+domain
# print(abs_domain_path)
if os.path.exists(abs_domain_path):
return True, abs_domain_path
return False,''
def hookWriteLog(line):
hook_file = runLog()
mdate = time.strftime('%Y-%m-%d %X', time.localtime())
log = "["+mdate+"]:"+line
print(log)
return mw.writeFile(hook_file,log+"\n",'a+')
def runHookPy(domain,path):
# print(domain,path)
run_log = runLog()
hook_file = getConf()
cmd = 'cd '+mw.getPanelDir()
cmd += ' && python3 '+hook_file + ' ' + domain + ' ' + path
cmd += ' >> '+ run_log
print(cmd)
return mw.execShell(cmd)
def autoUpdateSslData(domain, path):
ssl_cer_file = path + '/'+domain+'.cer'
ssl_info = mw.getCertName(ssl_cer_file)
time_begin = int(time.mktime(time.strptime(ssl_info['notBefore'], "%Y-%m-%d")))
time_end = int(time.mktime(time.strptime(ssl_info['notAfter'], "%Y-%m-%d")))
# print(ssl_info)
# print(time_begin,time_end)
conn = pSqliteDb('domain')
conn.where('domain=?', (domain,)).update({
'effective_date':str(time_begin),
'expiration_date':str(time_end),
})
return True
def runHookDstDomain(row):
domain = row['domain']
email = str(row['email'])
if row['effective_date'] != '':
effective_date = int(row['effective_date'])
now_int = int(time.time())
day = (now_int - effective_date)/86400
if int(day) < 8:
common_log = '【'+domain+'】未过期'
hookWriteLog(common_log)
return
hookWriteLog('开始申请【'+domain+'】SSL证书')
cmd = "#!/bin/bash\n"
cmd += "PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin:/opt/homebrew/bin:/root/.acme.sh\n"
cmd += "export PATH\n"
if mw.isAppleSystem():
user = getRunUser()
cmd += "source /Users/"+user+"/.zshrc\n"
cmd_data = getDnsapiData(row['dnsapi_id'])
# print(cmd_data)
export_val = getDnsapiExportVar(cmd_data['val'])
cmd += export_val
# acme.sh --register-account -m my@example.com
cmd_register = 'acme.sh --register-account -m '+ email + '\n'
cmd += cmd_register
# acme.sh --issue -d "example.com" -d "*.example.com" --dns dns_cf
cmd_apply = 'acme.sh --issue --dns '+str(cmd_data['type'])+' -d '+domain+' -d "*.'+domain+'"'
if row['effective_date'] != '':
effective_date = int(row['effective_date'])
now_int = int(time.time())
day = (now_int - effective_date)/86400
if int(day) > 7:
cmd_apply = 'acme.sh --issue --dns '+str(cmd_data['type'])+' -d '+domain+' -d "*.'+domain+'" --force'
cmd += cmd_apply
run_log = runLog()
cmd += ' >> '+ run_log
print(cmd)
os.system(cmd)
hookWriteLog('结束申请【'+domain+'】SSL证书')
isok, path = domainApplyPathJudge(domain)
print(isok,path)
if isok:
autoUpdateSslData(domain, path)
hookWriteLog('开始执行【'+domain+'】Hook脚本')
runHookPy(domain,path)
hookWriteLog('结束执行【'+domain+'】Hook脚本')
def getHookIdCmd():
args = getArgs()
data = checkArgs(args, ['id'])
if not data[0]:
return data[1]
cmd = "cd "+mw.getPanelDir()+" "
cmd += '&& python3 plugins/acme_pandominassl_apply/index.py run_hook_id 1.0 {"id":"'+args['id']+'"}'
return mw.returnJson(True, 'ok',cmd)
def runHookId():
args = getArgs()
data = checkArgs(args, ['id'])
if not data[0]:
return data[1]
conn = pSqliteDb('domain')
field = 'id,domain,dnsapi_id,email,effective_date,expiration_date,remark'
row = conn.field(field).where('id=?',(args['id'],)).find()
runHookDstDomain(row)
return 'run hook '+args['id']+' end'
def runHookCmd():
cmd = "cd "+mw.getPanelDir()+" "
cmd += '&& python3 plugins/acme_pandominassl_apply/index.py run_hook'
return mw.returnJson(True, 'ok',cmd)
def runHook():
conn = pSqliteDb('domain')
field = 'id,domain,dnsapi_id,email,effective_date,expiration_date,remark'
clist = conn.field(field).where('status=?',(1,)).limit('1000').order('id desc').select()
for idx in range(len(clist)):
row = clist[idx]
# print(row)
runHookDstDomain(row)
time.sleep(1)
return 'run hook end'
def autoSyncDomain(domain, dnsapi_id, email):
conn = pSqliteDb('domain')
field = 'id,domain,dnsapi_id,email,effective_date,expiration_date,remark'
fdata = conn.field(field).where('domain=?',(domain,)).select()
# print(fdata)
mdate = time.strftime('%Y-%m-%d %X', time.localtime())
if len(fdata) == 0:
conn.add('domain,dnsapi_id,email,remark,addtime', (domain, dnsapi_id, email,"备注", mdate))
print(domain+" 同步成功!")
return True
def runSyncCfDataRow(row, page = 1):
# print(row, page)
cf_key = row['kv']['CF_Key']
cf_mail = row['kv']['CF_Email']
header = {
'X-Auth-Email': cf_mail,
'X-Auth-Key': cf_key,
'Content-Type': 'application/json'
}
url = "https://api.cloudflare.com/client/v4/zones?status=active&page="+str(page)+"&per_page=20&order=status&direction=desc&match=all"
try:
r = requests.get(url, timeout=30, headers=header)
r.raise_for_status()
r.encoding = r.apparent_encoding
# print(r.text)
jdata = json.loads(r.text)
result = jdata['result']
result_info = jdata['result_info']
# print(len(result))
for i in result:
autoSyncDomain(i['name'], row['id'], cf_mail)
if result_info['total_pages'] > page:
page += 1
runSyncCfDataRow(row, page)
# print(result)
except Exception as e:
print(e)
# 同步CloudFlare数据
def runSyncCfData():
print("同步CloudFlare数据开始")
conn_dnsapi = pSqliteDb('dnsapi')
field = "id,name,type,val"
fdata = conn_dnsapi.field(field).where('type=?', ('dns_cf',)).limit('10').select()
for x in range(len(fdata)):
row = fdata[x]
dnsapi_kv = getDnsapiKv(row['val'])
row['kv'] = dnsapi_kv
runSyncCfDataRow(row)
print("同步CloudFlare数据结束")
return ''
def runSyncCfCmd():
cmd = "cd "+mw.getPanelDir()+" "
cmd += '&& python3 plugins/acme_pandominassl_apply/index.py run_sync_cf_data'
return mw.returnJson(True, 'ok',cmd)
def runSyncDnspodDataRow(row, page = 1):
# print(row, page)
DPI_Id = row['kv']['DPI_Id']
DPI_Key = row['kv']['DPI_Key']
data = {
'login_token': DPI_Id+','+DPI_Key,
'length':5,
'format':'json',
}
url = "https://api.dnspod.com/Domain.List"
try:
r = requests.post(url, timeout=30, data=data)
r.raise_for_status()
r.encoding = r.apparent_encoding
# print(r.text)
jdata = json.loads(r.text)
info = jdata['info']
domains = jdata['domains']
# print(jdata)
for i in domains:
autoSyncDomain(i['name'], row['id'], i['owner'])
page_total = int(info['domain_total']/data['length'])
if page_total > page:
page += 1
runSyncDnspodDataRow(row, page)
except Exception as e:
print(e)
# 同步DnsPod数据
def runSyncDnsPodData():
print("同步DnsPod数据开始")
conn_dnsapi = pSqliteDb('dnsapi')
field = "id,name,type,val"
fdata = conn_dnsapi.field(field).where('type=?', ('dns_dpi',)).limit('10').select()
for x in range(len(fdata)):
row = fdata[x]
dnsapi_kv = getDnsapiKv(row['val'])
row['kv'] = dnsapi_kv
runSyncDnspodDataRow(row)
print(fdata)
print("同步DnsPod数据结束")
return ''
def runSyncDnsPodCmd():
cmd = "cd "+mw.getPanelDir()+" "
cmd += '&& python3 plugins/acme_pandominassl_apply/index.py run_sync_dnspod_data'
return mw.returnJson(True, 'ok',cmd)
if __name__ == "__main__":
func = sys.argv[1]
# print(func)
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 == 'run_info':
print(runInfo())
elif func == 'conf':
print(getConf())
elif func == 'run_log':
print(runLog())
elif func == 'config_tpl':
print(configTpl())
elif func == 'read_config_tpl':
print(readConfigTpl())
elif func == 'dnsapi_list':
print(dnsapiList())
elif func == 'dnsapi_list_all':
print(dnsapiListAll())
elif func == 'dnsapi_add':
print(dnsapiAdd())
elif func == 'dnsapi_del':
print(dnsapiDel())
elif func == 'email_list':
print(emailList())
elif func == 'email_add':
print(emailAdd())
elif func == 'email_del':
print(emailDel())
elif func == 'domain_list':
print(domainList())
elif func == 'domain_add':
print(domainAdd())
elif func == 'domain_del':
print(domainDel())
elif func == 'domain_status_toggle':
print(domainStatusToggle())
elif func == 'run_hook':
print(runHook())
elif func == 'run_hook_cmd':
print(runHookCmd())
elif func == 'get_run_hook_id_cmd':
print(getHookIdCmd())
elif func == 'run_hook_id':
print(runHookId())
elif func == 'run_sync_cf_data':
print(runSyncCfData())
elif func == 'run_sync_cf_cmd':
print(runSyncCfCmd())
elif func == 'run_sync_dnspod_data':
print(runSyncDnsPodData())
elif func == 'run_sync_dnspod_cmd':
print(runSyncDnsPodCmd())
else:
print('error')
-17
View File
@@ -1,17 +0,0 @@
{
"sort": 7,
"ps": "ACME泛域名SSL申请/管理",
"name": "acme_pandominassl_apply",
"title": "ACME泛域名SSL",
"shell": "install.sh",
"versions":["1.0"],
"tip": "soft",
"checks": "server/acme_pandominassl_apply",
"path": "server/acme_pandominassl_apply",
"display": 1,
"author": "ACME",
"date": "2024-09-12",
"home": "https://github.com/acmesh-official/acme.sh",
"type": 0,
"pid": "5"
}
@@ -1,54 +0,0 @@
#!/bin/bash
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin:/opt/homebrew/bin
export PATH
curPath=`pwd`
rootPath=$(dirname "$curPath")
rootPath=$(dirname "$rootPath")
serverPath=$(dirname "$rootPath")
VERSION=$2
# curl https://get.acme.sh | sh
# acme.sh --uninstall
# source /Users/xxx/.zshrc
# https://github.com/acmesh-official/acme.sh/wiki/dnsapi
# https://docs.dnspod.com/api-legacy/domains.html#get-the-domain-list
# cd /www/server/mdserver-web && python3 plugins/acme_pandominassl_apply/index.py run_hook
# cd /www/server/mdserver-web && python3 plugins/acme_pandominassl_apply/index.py run_sync_cf_data
# cd /www/server/mdserver-web && python3 plugins/acme_pandominassl_apply/index.py run_sync_dnspod_data
if [ -f ${rootPath}/bin/activate ];then
source ${rootPath}/bin/activate
fi
# pip install cloudflare
Install_App()
{
echo '正在安装脚本文件...'
mkdir -p $serverPath/source
mkdir -p $serverPath/acme_pandominassl_apply
echo "${VERSION}" > $serverPath/acme_pandominassl_apply/version.pl
cd ${rootPath} && python3 ${rootPath}/plugins/acme_pandominassl_apply/index.py start
echo '安装[ACME泛域名SSL]完成'
}
Uninstall_App()
{
cd ${rootPath} && python3 ${rootPath}/plugins/acme_pandominassl_apply/index.py stop
rm -rf $serverPath/acme_pandominassl_apply
echo "卸载[ACME泛域名SSL]成功"
}
action=$1
if [ "${1}" == 'install' ];then
Install_App
else
Uninstall_App
fi
@@ -1,801 +0,0 @@
function apaPost(method, args,callback){
var loadT = layer.msg('正在获取...', { icon: 16, time: 0, shade: 0.3 });
var req_data = {};
req_data['name'] = 'acme_pandominassl_apply';
req_data['func'] = method;
req_data['version'] = $('.plugin_version').attr('version');
if (typeof(args) == 'string'){
req_data['args'] = JSON.stringify(toArrayObject(args));
} else {
req_data['args'] = JSON.stringify(args);
}
$.post('/plugins/run', req_data, function(data) {
layer.close(loadT);
if (!data.status){
//错误展示10S
layer.msg(data.msg,{icon:0,time:2000,shade: [10, '#000']});
return;
}
if(typeof(callback) == 'function'){
callback(data);
}
},'json');
}
function apaPostN(method, args,callback){
var req_data = {};
req_data['name'] = 'acme_pandominassl_apply';
req_data['func'] = method;
req_data['version'] = $('.plugin_version').attr('version');
if (typeof(args) == 'string'){
req_data['args'] = JSON.stringify(toArrayObject(args));
} else {
req_data['args'] = JSON.stringify(args);
}
$.post('/plugins/run', req_data, function(data) {
if (!data.status){
//错误展示10S
layer.msg(data.msg,{icon:0,time:2000,shade: [10, '#000']});
return;
}
if(typeof(callback) == 'function'){
callback(data);
}
},'json');
}
function apaPostCallbak(method, version, args,callback){
var loadT = layer.msg('正在获取...', { icon: 16, time: 0, shade: 0.3 });
var req_data = {};
req_data['name'] = 'acme_pandominassl_apply';
req_data['func'] = method;
args['version'] = version;
if (typeof(args) == 'string'){
req_data['args'] = JSON.stringify(toArrayObject(args));
} else {
req_data['args'] = JSON.stringify(args);
}
$.post('/plugins/callback', req_data, function(data) {
layer.close(loadT);
if (!data.status){
layer.msg(data.msg,{icon:0,time:2000,shade: [0.3, '#000']});
return;
}
if(typeof(callback) == 'function'){
callback(data);
}
},'json');
}
function apaReadme(){
var readme = '<ul class="help-info-text c7">';
readme += '<li>ACME泛域名SSL申请/管理/HOOK</li>';
readme += '<li>通过DNS验证获取SSL证书!</li>';
readme += '<li>HOOK: ssl发生变动时调用!</li>';
readme += '<li>暂时仅支持1000个域名管理!</li>';
readme += '<li>DNSAPI文档: https://github.com/acmesh-official/acme.sh/wiki/dnsapi</li>';
readme += '<li>默认7天强制更新!</li>';
readme += '</ul>';
$('.soft-man-con').html(readme);
}
function emailDel(id, name){
safeMessage('删除['+name+']','您真的要删除['+name+']吗?',function(){
var data='id='+id+'&name='+name;
apaPost('email_del', data, function(data){
var rdata = $.parseJSON(data.data);
showMsg(rdata.msg,function(){
emailList();
},{icon: rdata.status ? 1 : 2}, 600);
});
});
}
function emailAdd(type){
layer.open({
type: 1,
area: '500px',
title: '添加邮件地址',
closeBtn: 1,
shift: 5,
shadeClose: true,
btn:["提交","关闭"],
content: "<form class='bt-form pd20' id='email_add'>\
<div class='line'>\
<span class='tname'>邮件地址</span>\
<div class='info-r'><input name='addr' class='bt-input-text mr5' style='width:100%;' placeholder='邮件地址' type='text'></div>\
</div>\
<div class='line'>\
<span class='tname'>备注</span>\
<div class='info-r'><input name='remark' class='bt-input-text mr5' style='width:100%;' placeholder='备注' type='text'></div>\
</div>\
</form>",
success:function(){
$("input[name='addr']").keyup(function(){
var v = $(this).val();
$("input[name='remark']").val(v);
});
},
yes:function(index) {
var data = $("#email_add").serialize();
data = decodeURIComponent(data);
// data = toArrayObject(data);
apaPost('email_add', data, function(data){
var rdata = $.parseJSON(data.data);
showMsg(rdata.msg,function(){
if (rdata.status){
layer.close(index);
emailList();
}
},{icon: rdata.status ? 1 : 2}, 2000);
});
}
});
}
function emailList(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;
}
apaPost('email_list', _data, function(data){
var rdata = $.parseJSON(data.data);
var list = '';
for(i in rdata.data){
list += '<tr>';
list +='<td><input value="'+rdata.data[i]['id']+'" class="check" onclick="checkSelect();" type="checkbox"></td>';
list += '<td>' + rdata.data[i]['addr'] +'</td>';
list += '<td>' + rdata.data[i]['remark'] +'</td>';
list += '<td style="text-align:right">';
list += '<a href="javascript:;" class="btlink" onclick="emailDel(\''+rdata.data[i]['id']+'\',\''+rdata.data[i]['addr']+'\')" title="删除">删除</a>' +
'</td>';
list += '</tr>';
}
var con = '<div class="safe bgw">\
<button onclick="emailAdd()" title="添加邮件地址" class="btn btn-success btn-sm" type="button" style="margin-right: 5px;">添加邮件地址</button>\
<span style="float:right"> \
<button batch="true" style="float: right;display: none;margin-left:10px;" onclick="delDbBatch();" title="删除选中项" class="btn btn-default btn-sm">删除选中</button>\
</span>\
<div class="divtable mtb10">\
<div class="tablescroll">\
<table id="DataBody" class="table table-hover" width="100%" cellspacing="0" cellpadding="0" border="0" style="border: 0 none;">\
<thead><tr><th width="30"><input class="check" onclick="checkSelect();" type="checkbox"></th>\
<th>邮件地址</th>\
<th>备注</th>\
<th style="text-align:right;">操作</th></tr></thead>\
<tbody>'+ list +'</tbody>\
</table>\
</div>\
<div class="dataTables_paginate paging_bootstrap page"></div>\
</div>\
</div>';
$(".soft-man-con").html(con);
$('.dataTables_paginate').html(rdata.page);
readerTableChecked();
});
}
function dnsapiDel(id, name){
safeMessage('删除['+name+']','您真的要删除['+name+']吗?',function(){
var data='id='+id+'&name='+name;
apaPost('dnsapi_del', data, function(data){
var rdata = $.parseJSON(data.data);
showMsg(rdata.msg,function(){
dnsapiList();
},{icon: rdata.status ? 1 : 2}, 600);
});
});
}
var dnsapi_option = [
{"name":"dns_cf", "title":'cloudflare', 'key':'CF_Key:CF_Email'},
{"name":"dns_dp", "title":'dnspod/国内', 'key':'DP_Id:DP_Key'},
{"name":"dns_dpi", "title":'dnspod/国际', 'key':'DPI_Id:DPI_Key'},
{"name":"dns_gd", "title":'GoDaddy', 'key':'GD_Key:GD_Secret'},
{"name":"dns_pdns", "title":'PowerDNS', 'key':'PDNS_Url:PDNS_ServerId:PDNS_Token:PDNS_Ttl'},
{"name":"dns_lua", "title":'LuaDNS', 'key':'LUA_Key:LUA_Email'},
{"name":"dns_me", "title":'DNSMadeEasy', 'key':'ME_Key:ME_Secret'},
{"name":"dns_aws", "title":'Amazon Route53', 'key':'AWS_ACCESS_KEY_ID:AWS_SECRET_ACCESS_KEY'},
{"name":"dns_ali", "title":'Aliyun', 'key':'Ali_Key:Ali_Secret'},
{"name":"dns_ispconfig", "title":'ISPConfig', 'key':'ISPC_User:ISPC_Password:ISPC_Api:ISPC_Api_Insecure'},
{"name":"dns_ad", "title":'Alwaysdata', 'key':'AD_API_KEY'},
{"name":"dns_linode_v4", "title":'Linode', 'key':'LINODE_V4_API_KEY'},
{"name":"dns_freedns", "title":'FreeDNS', 'key':'FREEDNS_User:FREEDNS_Password'},
{"name":"dns_cyon", "title":'cyon.ch', 'key':'CY_Username:CY_Password:CY_OTP_Secret'},
{"name":"dns_gandi_livedns", "title":'LiveDNS', 'key':'GANDI_LIVEDNS_TOKEN'},
{"name":"dns_knot", "title":'Knot', 'key':'KNOT_SERVER:KNOT_KEY'},
{"name":"dns_dgon", "title":'DigitalOcean', 'key':'DO_API_KEY'},
{"name":"dns_cloudns", "title":'ClouDNS.net', 'key':'CLOUDNS_SUB_AUTH_ID:CLOUDNS_AUTH_PASSWORD'},
{"name":"dns_namesilo", "title":'Namesilo', 'key':'Namesilo_Key'},
{"name":"dns_azure", "title":'Azure', 'key':'AZUREDNS_SUBSCRIPTIONID:AZUREDNS_TENANTID:AZUREDNS_APPID:AZUREDNS_CLIENTSECRET'},
{"name":"dns_selectel", "title":'selectel.com', 'key':'SL_Key'},
{"name":"dns_zonomi", "title":'zonomi.com', 'key':'ZM_Key'},
{"name":"dns_kinghost", "title":'KingHost', 'key':'KINGHOST_Username:KINGHOST_Password'},
{"name":"dns_zilore", "title":'Zilore', 'key':'Zilore_Key'},
{"name":"dns_gcloud", "title":'Google Cloud DNS', 'key':'CLOUDSDK_ACTIVE_CONFIG_NAME'},
{"name":"dns_mydnsjp", "title":'MyDNS.JP', 'key':'MYDNSJP_MasterID:MYDNSJP_Password'},
{"name":"dns_doapi", "title":'do.de', 'key':'DO_LETOKEN'},
{"name":"dns_online", "title":'Online', 'key':'ONLINE_API_KEY'},
{"name":"dns_cn", "title":'Core-Networks', 'key':'CN_User:CN_Password'},
{"name":"dns_ultra", "title":'UltraDNS', 'key':'ULTRA_USR:ULTRA_PWD'},
{"name":"dns_hetzner", "title":'Hetzner', 'key':'HETZNER_Token'},
{"name":"dns_ddnss", "title":'DDNSS.de', 'key':'DDNSS_Token'},
];
function getDnsapiKey(name){
for (var i = 0; i < dnsapi_option.length; i++) {
if (dnsapi_option[i]['name'] == name){
return dnsapi_option[i]['key'];
}
}
return '';
}
function getDnsapiTitle(name){
for (var i = 0; i < dnsapi_option.length; i++) {
if (dnsapi_option[i]['name'] == name){
return dnsapi_option[i]['title'];
}
}
return '其他';
}
function dnsapiAdd(row){
// console.log(row);
var option_name = '';
var option_remark = '';
var option_type = 'dns_cf';
var option_val = '';
var option_id = 0;
if (typeof(row) != 'undefined'){
option_name = row['name'];
option_remark = row['remark'];
option_type = row['type'];
option_val = row['val'];
option_id = row['id'];
}
function renderDnsapiOption(name, val){
var vlist = {};
if (val != ''){
var t = val.split('~');
for (var i = 0; i < t.length; i++) {
var kv = t[i].split('|');
if (kv.length == 2){
vlist[kv[0]] = kv[1];
} else {
vlist[kv[0]] = '';
}
}
// console.log(vlist);
}
var key = getDnsapiKey(name);
var klist = key.split(':');
var option_html = '';
for (var i = 0; i < klist.length; i++) {
var klist_val = '';
if (klist[i] in vlist){
klist_val = vlist[klist[i]];
}
option_html += "<span class='tname'>"+klist[i]+"</span>\
<div class='info-r'>\
<input name='"+klist[i]+"' class='bt-input-text mr5' style='width:100%;' value='"+klist_val+"' placeholder='请输入对应值' type='text'>\
</div>";
}
$('#dnsapi_option').html(option_html);
}
layer.open({
type: 1,
area: '500px',
title: '添加DNSAPI',
closeBtn: 1,
shift: 5,
shadeClose: true,
btn:["提交","关闭"],
content: "<form class='bt-form pd20' id='dnsapi_add'>\
<div class='line'>\
<span class='tname'>名称</span>\
<div class='info-r'><input name='name' class='bt-input-text mr5' style='width:100%;' placeholder='名称' value='"+option_name+"' type='text'></div>\
</div>\
<div class='line'>\
<span class='tname'>DNSAPI类型</span>\
<div class='info-r'>\
<select class='bt-input-text mr5' name='type'>\
<option name='cf'>cloudflare</option>\
</select>\
</div>\
</div>\
<div class='line' id='dnsapi_option'>\
<span class='tname'>CF_Key</span>\
<div class='info-r'>\
<input name='v1' class='bt-input-text mr5' style='width:100%;' placeholder='请输入对应值' type='text'>\
</div>\
<span class='tname'>CF_Email</span>\
<div class='info-r'>\
<input name='v2' class='bt-input-text mr5' style='width:100%;' placeholder='请输入对应值' type='text'>\
</div>\
</div>\
<div class='line'>\
<span class='tname'>备注</span>\
<div class='info-r'><input name='remark' class='bt-input-text mr5' style='width:100%;' placeholder='备注' value='"+option_remark+"' type='text'></div>\
</div>\
<input name='id' value='"+option_id+"' type='hidden'>\
</form>",
success:function(){
$("input[name='name']").keyup(function(){
var v = $(this).val();
$("input[name='remark']").val(v);
});
var option = '';
for (var i = 0; i<dnsapi_option.length; i++) {
if (dnsapi_option[i]['name'] == option_type){
option += "<option value='"+dnsapi_option[i]['name']+"' key='"+dnsapi_option[i]['key']+"' selected>"+dnsapi_option[i]['title']+"</option>";
} else {
option += "<option value='"+dnsapi_option[i]['name']+"' key='"+dnsapi_option[i]['key']+"'>"+dnsapi_option[i]['title']+"</option>";
}
}
renderDnsapiOption(option_type, option_val);
$('select[name="type"]').html(option);
$('select[name="type"]').change(function(){
var name = $(this).val();
if (option_type == name){
renderDnsapiOption(name, option_val);
} else {
renderDnsapiOption(name,'');
}
});
},
yes:function(index) {
var data = $("#dnsapi_add").serialize();
data = decodeURIComponent(data);
data = toArrayObject(data);
var key = getDnsapiKey(data['type']);
var klist = key.split(':');
var val = '';
for (var i = 0; i < klist.length; i++) {
var k = klist[i];
if (k in data){
if (klist.length - 1 == i){
val += k + '|' + data[k];
} else {
val += k + '|' + data[k]+'~';
}
}
}
data['val'] = val;
apaPost('dnsapi_add', data, function(data){
var rdata = $.parseJSON(data.data);
showMsg(rdata.msg,function(){
if (rdata.status){
layer.close(index);
dnsapiList();
}
},{icon: rdata.status ? 1 : 2}, 2000);
});
}
});
}
function dnsapiList(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;
}
apaPost('dnsapi_list', _data, function(data){
var rdata = $.parseJSON(data.data);
var list = '';
for(i in rdata.data){
list += '<tr>';
list +='<td><input value="'+rdata.data[i]['id']+'" class="check" onclick="checkSelect();" type="checkbox"></td>';
list += '<td>' + rdata.data[i]['name'] +'</td>';
list += '<td>' + getDnsapiTitle(rdata.data[i]['type']) +'</td>';
list += '<td>' + rdata.data[i]['val'].split('~').join("<br/>") +'</td>';
list += '<td>' + rdata.data[i]['remark'] +'</td>';
list += '<td style="text-align:right">';
list += '<a href="javascript:;" index="'+i+'" class="btlink edit" title="编辑">编辑</a> | ';
list += '<a href="javascript:;" class="btlink" onclick="dnsapiDel(\''+rdata.data[i]['id']+'\',\''+rdata.data[i]['name']+'\')" title="删除">删除</a>';
list += '</td></tr>';
}
var con = '<div class="safe bgw">\
<button onclick="dnsapiAdd()" title="DNSAPI" class="btn btn-success btn-sm" type="button" style="margin-right: 5px;">添加DNSAPI</button>\
<span style="float:right"> \
<button batch="true" style="float: right;display: none;margin-left:10px;" onclick="delDbBatch();" title="删除选中项" class="btn btn-default btn-sm">删除选中</button>\
</span>\
<div class="divtable mtb10">\
<div class="tablescroll">\
<table id="DataBody" class="table table-hover" width="100%" cellspacing="0" cellpadding="0" border="0" style="border: 0 none;">\
<thead><tr><th width="30"><input class="check" onclick="checkSelect();" type="checkbox"></th>\
<th>名称</th>\
<th>类型</th>\
<th>值</th>\
<th>备注</th>\
<th style="text-align:right;">操作</th></tr></thead>\
<tbody>'+ list +'</tbody>\
</table>\
</div>\
<div class="dataTables_paginate paging_bootstrap page"></div>\
</div>\
</div>';
$(".soft-man-con").html(con);
$('.dataTables_paginate').html(rdata.page);
$('.edit').click(function(){
var idx = $(this).attr('index');
var row = rdata.data[idx];
// console.log(row);
dnsapiAdd(row);
})
readerTableChecked();
});
}
function domainDel(id, name){
safeMessage('删除['+name+']','您真的要删除['+name+']吗?',function(){
var data='id='+id+'&name='+name;
apaPost('domain_del', data, function(data){
var rdata = $.parseJSON(data.data);
showMsg(rdata.msg,function(){
domainList();
},{icon: rdata.status ? 1 : 2}, 600);
});
});
}
function domainStatusToggle(id){
var data='id='+id;
apaPost('domain_status_toggle', data, function(data){
var rdata = $.parseJSON(data.data);
showMsg(rdata.msg,function(){
domainList();
},{icon: rdata.status ? 1 : 2}, 600);
});
}
function domainIdCmd(id){
var data='id='+id;
apaPost('get_run_hook_id_cmd', data, function(data){
var rdata = $.parseJSON(data.data);
// console.log(rdata);
layer.open({
title: "手动同步命令",
area: ['600px', '180px'],
type:1,
closeBtn: 1,
shadeClose: false,
btn:["复制","取消"],
content: '<div class="pd15">\
<div class="divtable">\
<pre class="layui-code">'+rdata.data+'</pre>\
</div>\
</div>',
success:function(){
copyText(rdata.data);
},
yes:function(){
copyText(rdata.data);
}
});
});
}
function domainHookCmd(){
apaPost('run_hook_cmd', {}, function(data){
var rdata = $.parseJSON(data.data);
layer.open({
title: "手动同步全部命令",
area: ['600px', '180px'],
type:1,
closeBtn: 1,
shadeClose: false,
btn:["复制","取消"],
content: '<div class="pd15">\
<div class="divtable">\
<pre class="layui-code">'+rdata.data+'</pre>\
</div>\
</div>',
success:function(){
copyText(rdata.data);
},
yes:function(){
copyText(rdata.data);
}
});
});
}
function syncCfCmd(){
apaPost('run_sync_cf_cmd', {}, function(data){
var rdata = $.parseJSON(data.data);
layer.open({
title: "手动同步CloudFlare全部域名命令",
area: ['600px', '180px'],
type:1,
closeBtn: 1,
shadeClose: false,
btn:["复制","取消"],
content: '<div class="pd15">\
<div class="divtable">\
<pre class="layui-code">'+rdata.data+'</pre>\
</div>\
</div>',
success:function(){
copyText(rdata.data);
},
yes:function(){
copyText(rdata.data);
}
});
});
}
function syncDnsPodCmd(){
apaPost('run_sync_dnspod_cmd', {}, function(data){
var rdata = $.parseJSON(data.data);
layer.open({
title: "手动同步DnsPod全部域名命令",
area: ['600px', '180px'],
type:1,
closeBtn: 1,
shadeClose: false,
btn:["复制","取消"],
content: '<div class="pd15">\
<div class="divtable">\
<pre class="layui-code">'+rdata.data+'</pre>\
</div>\
</div>',
success:function(){
copyText(rdata.data);
},
yes:function(){
copyText(rdata.data);
}
});
});
}
function domainAdd(row){
var option_domian = '';
var option_remark = '备注';
var option_email = '';
var option_id = 0;
var option_dnsapi_id = 0;
if (typeof(row) != 'undefined'){
option_domian = row['domain'];
option_remark = row['remark'];
option_email = row['email'];
option_id = row['id'];
option_dnsapi_id = row['dnsapi_id'];
}
layer.open({
type: 1,
area: '500px',
title: '添加顶级域名',
closeBtn: 1,
shift: 5,
shadeClose: true,
btn:["提交","关闭"],
content: "<form class='bt-form pd20' id='domain_add'>\
<div class='line'>\
<span class='tname'>域名</span>\
<div class='info-r'><input name='domain' class='bt-input-text mr5' style='width:100%;' value='"+option_domian+"' placeholder='域名' type='text'></div>\
</div>\
<div class='line'>\
<span class='tname'>DNSAPI</span>\
<div class='info-r'>\
<select class='bt-input-text mr5' name='dnsapi_id'>\
<option name='0'>无设置</option>\
</select>\
</div>\
</div>\
<div class='line'>\
<span class='tname'>邮件</span>\
<div class='info-r'><input name='email' class='bt-input-text mr5' style='width:100%;' value='"+option_email+"' placeholder='邮件' type='text'></div>\
</div>\
<div class='line'>\
<span class='tname'>备注</span>\
<div class='info-r'><input name='remark' class='bt-input-text mr5' style='width:100%;' value='"+option_remark+"' placeholder='备注' type='text'></div>\
</div>\
<input name='id' value='"+option_id+"' type='hidden'>\
</form>",
success:function(){
// $("input[name='domain']").keyup(function(){
// var v = $(this).val();
// $("input[name='remark']").val(v);
// });
var dnsapi_id_html = "<option value='0'>无设置</option>";
apaPostN('dnsapi_list_all', {}, function(data){
var rdata = $.parseJSON(data.data);
for (var i = 0; i < rdata.length; i++) {
if (option_dnsapi_id == rdata[i]['id']){
dnsapi_id_html += "<option value='"+rdata[i]['id']+"' val='"+rdata[i]['val']+"' selected>"+rdata[i]['name']+"</option>";
} else {
dnsapi_id_html += "<option value='"+rdata[i]['id']+"' val='"+rdata[i]['val']+"'>"+rdata[i]['name']+"</option>";
}
}
$('select[name="dnsapi_id"]').change(function(){
var val = $('select[name="dnsapi_id"]').find("option:selected").attr('val');
var val_arr = val.split("|");
for (var i = 0; i < val_arr.length; i++) {
var param = val_arr[i];
var strictEmailRegex = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
if(strictEmailRegex.test(param)){
$('input[name="email"]').val(param);
}
}
});
$('select[name="dnsapi_id"]').html(dnsapi_id_html);
});
},
yes:function(index) {
var data = $("#domain_add").serialize();
data = decodeURIComponent(data);
apaPost('domain_add', data, function(data){
var rdata = $.parseJSON(data.data);
showMsg(rdata.msg,function(){
if (rdata.status){
layer.close(index);
domainList();
}
},{icon: rdata.status ? 1 : 2}, 2000);
});
}
});
}
function domainList(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;
}
apaPost('domain_list', _data, function(data){
var rdata = $.parseJSON(data.data);
// console.log(rdata);
var list = '';
for(i in rdata.data){
list += '<tr>';
list +='<td><input value="'+rdata.data[i]['id']+'" class="check" onclick="checkSelect();" type="checkbox"></td>';
list += '<td>' + rdata.data[i]['domain'] +'</td>';
list += '<td>' + rdata.data[i]['dnsapi_id_alias'] +'</td>';
list += '<td>' + rdata.data[i]['email'] +'</td>';
list += '<td>' + rdata.data[i]['remark'] +'</td>';
if (rdata.data[i]['effective_date'] == ''){
list += '<td>空/未申请</td>';
} else {
list += '<td>'+getFormatTime(rdata.data[i]['effective_date'],'yyyy/MM/dd')+'</td>';
}
if (rdata.data[i]['expiration_date'] == ''){
list += '<td>空/未申请</td>';
} else {
list += '<td>'+getFormatTime(rdata.data[i]['expiration_date'],'yyyy/MM/dd')+'</td>';
}
if (rdata.data[i]['status'] == '0'){
list += '<td><a href="javascript:;" index="'+i+'" class="btlink status">否</a></td>';
} else {
list += '<td><a href="javascript:;" index="'+i+'" class="btlink status">是</a></td>';
}
list += '<td style="text-align:right">';
list += '<a href="javascript:;" index="'+i+'" class="btlink cmd" title="命令">命令</a> | ';
list += '<a href="javascript:;" index="'+i+'" class="btlink edit" title="编辑">编辑</a> | ';
list += '<a href="javascript:;" class="btlink" onclick="domainDel(\''+rdata.data[i]['id']+'\',\''+rdata.data[i]['domain']+'\')" title="删除">删除</a>';
list += '</td></tr>';
}
var con = '<div class="safe bgw">\
<button onclick="domainAdd()" title="添加顶级域名" class="btn btn-success btn-sm" type="button" style="margin-right: 5px;">添加域名</button>\
<button onclick="domainHookCmd()" title="全部同步命令" class="btn btn-success btn-sm" type="button" style="margin-right: 5px;">全部同步命令</button>\
<button onclick="syncCfCmd()" title="cloudflare同步命令" class="btn btn-success btn-sm" type="button" style="margin-right: 5px;">cloudflare同步命令</button>\
<button onclick="syncDnsPodCmd()" title="DnsPod国际同步命令" class="btn btn-success btn-sm" type="button" style="margin-right: 5px;">DnsPod国际同步命令</button>\
<div class="divtable mtb10">\
<div class="tablescroll">\
<table id="DataBody" class="table table-hover" width="100%" cellspacing="0" cellpadding="0" border="0" style="border: 0 none;">\
<thead><tr><th width="30"><input class="check" onclick="checkSelect();" type="checkbox"></th>\
<th>域名</th>\
<th>DNSAPI</th>\
<th>邮件</th>\
<th>备注</th>\
<th>开始</th>\
<th>结束</th>\
<th>同步</th>\
<th style="text-align:right;">操作</th></tr></thead>\
<tbody>'+ list +'</tbody>\
</table>\
</div>\
<div class="dataTables_paginate paging_bootstrap page"></div>\
</div>\
</div>';
$(".soft-man-con").html(con);
$('.dataTables_paginate').html(rdata.page);
$('.edit').click(function(){
var idx = $(this).attr('index');
var row = rdata.data[idx];
domainAdd(row);
});
$('.status').click(function(){
var idx = $(this).attr('index');
var row = rdata.data[idx];
domainStatusToggle(row['id']);
});
$('.cmd').click(function(){
var idx = $(this).attr('index');
var row = rdata.data[idx];
domainIdCmd(row['id']);
});
readerTableChecked();
});
}
@@ -1,142 +0,0 @@
# coding:utf-8
import sys
import io
import os
import time
import json
# print(sys.platform)
if sys.platform != "darwin":
os.chdir("/www/server/mdserver-web")
web_dir = os.getcwd() + "/web"
if os.path.exists(web_dir):
sys.path.append(web_dir)
os.chdir(web_dir)
import core.mw as mw
from utils.crontab import crontab as MwCrontab
app_debug = False
if mw.isAppleSystem():
app_debug = True
def getPluginName():
return 'acme_pandominassl_apply'
def getPluginDir():
return mw.getPluginDir() + '/' + getPluginName()
def getServerDir():
return mw.getServerDir() + '/' + getPluginName()
def getTaskConf():
conf = getServerDir() + "/cron_config.json"
return conf
def getTaskDeltaConf():
conf = getServerDir() + "/cron_delta_config.json"
return conf
def getConfigData():
try:
return json.loads(mw.readFile(getTaskConf()))
except:
pass
return {
"task_id": -1,
"period": "day-n",
"where1": "1",
"hour": "0",
"minute": "15",
}
def createBgTask():
removeBgTask()
createBgTaskByName(getPluginName())
return True
def createBgTaskByName(name):
args = getConfigData()
_name = "[勿删]ACME泛域名SSL[APA]"
res = mw.M("crontab").field("id, name").where("name=?", (_name,)).find()
if res:
return True
if "task_id" in args and args["task_id"] > 0:
res = mw.M("crontab").field("id, name").where(
"id=?", (args["task_id"],)).find()
if res and res["id"] == args["task_id"]:
print("计划任务已经存在!")
return True
mw_dir = mw.getPanelDir()
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 run_hook"' + "\n"
cmd += 'cd $mw_dir && python3 $script_path/index.py run_hook' + "\n"
cmd += 'echo "【`date +"%Y-%m-%d %H:%M:%S"`】 END★" >> $logs_file' + "\n"
cmd += 'echo "<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<" >> $logs_file' + "\n"
params = {
'name': _name,
'type': args['period'],
'week': "",
'where1': args['where1'],
'hour': args['hour'],
'minute': args['minute'],
'save': "",
'backup_to': "",
'stype': "toShell",
'sname': '',
'sbody': cmd,
'url_address': '',
}
task_id = MwCrontab.instance().add(params)
if task_id > 0:
args["task_id"] = task_id
args["name"] = name
mw.writeFile(getTaskConf(), json.dumps(args))
def removeBgTask():
cfg = getConfigData()
if "task_id" in cfg 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
mw.writeFile(getTaskConf(), json.dumps(cfg))
return True
return False
if __name__ == "__main__":
if len(sys.argv) > 1:
action = sys.argv[1]
if action == "remove":
removeBgTask()
elif action == "add":
createBgTask()
Binary file not shown.

Before

Width:  |  Height:  |  Size: 42 KiB

-31
View File
@@ -1,31 +0,0 @@
<style>
.overflow_hide {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
vertical-align: middle;
}
</style>
<div class="bt-form">
<div class='plugin_version'></div>
<div class="bt-w-main">
<div class="bt-w-menu">
<p class="bgw" onclick="pluginService('alist');">服务</p>
<p onclick="pluginInitD('alist');">自启动</p>
<p onclick="alistCommonFunc();">常用功能</p>
<p onclick="pluginConfigTpl('alist',$('.plugin_version').attr('version'));">配置修改</p>
<p onclick="pluginLogs('alist','','run_log');">运行日志</p>
<p onclick="alistReadme();">相关说明</p>
</div>
<div class="bt-w-con pd15">
<div class="soft-man-con" style="height: 520px; overflow: auto;"></div>
</div>
</div>
</div>
<script type="text/javascript">
$.getScript( "/plugins/file?name=alist&f=js/alist.js", function(){
pluginService('alist', $('.plugin_version').attr('version'));
});
</script>
-319
View File
@@ -1,319 +0,0 @@
# coding:utf-8
import sys
import io
import os
import time
import re
import json
web_dir = os.getcwd() + "/web"
if os.path.exists(web_dir):
sys.path.append(web_dir)
os.chdir(web_dir)
import core.mw as mw
app_debug = False
if mw.isAppleSystem():
app_debug = True
def getPluginName():
return 'alist'
def getPluginDir():
return mw.getPluginDir() + '/' + getPluginName()
def getServerDir():
return mw.getServerDir() + '/' + getPluginName()
def getInitDFile():
current_os = mw.getOs()
if current_os == 'darwin':
return '/tmp/' + getPluginName()
if current_os.startswith('freebsd'):
return '/etc/rc.d/' + getPluginName()
return '/etc/init.d/' + getPluginName()
def getInitDTpl():
path = getPluginDir() + "/init.d/" + getPluginName() + ".tpl"
return path
def getArgs():
args = sys.argv[3:]
tmp = {}
args_len = len(args)
if args_len == 1:
t = args[0].strip('{').strip('}')
if t.strip() == '':
tmp = []
else:
t = t.split(':')
tmp[t[0]] = t[1]
tmp[t[0]] = t[1]
elif args_len > 1:
for i in range(len(args)):
t = args[i].split(':')
tmp[t[0]] = t[1]
return tmp
def checkArgs(data, ck=[]):
for i in range(len(ck)):
if not ck[i] in data:
return (False, mw.returnJson(False, '参数:(' + ck[i] + ')没有!'))
return (True, mw.returnJson(True, 'ok'))
def getConf():
path = getServerDir() + "/data/config.json"
return path
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 __release_port(port, ps = '开启端口'):
try:
from utils.firewall import Firewall as MwFirewall
MwFirewall.instance().addAcceptPort(port, ps, 'port')
return port
except Exception as e:
return "Release failed {}".format(e)
def __delete_port(port):
try:
from utils.firewall import Firewall as MwFirewall
MwFirewall.instance().delAcceptPortCmd(port, 'tcp')
return port
except Exception as e:
return "Delete failed {}".format(e)
def openPort():
content = mw.readFile(getConf())
data = json.loads(content)
http_port = data['scheme']['http_port']
__release_port(http_port, 'alist')
return True
def status():
cmd = "ps aux|grep alist |grep -v grep | grep -v python | grep -v mdserver-web | awk '{print $2}'"
data = mw.execShell(cmd)
if data[0] == '':
return 'stop'
return 'start'
def contentReplace(content):
service_path = mw.getServerDir()
content = content.replace('{$ROOT_PATH}', mw.getFatherDir())
content = content.replace('{$SERVER_PATH}', service_path)
content = content.replace('{$SERVER_APP}', service_path + '/'+getPluginName())
return content
def initDreplace():
file_tpl = getInitDTpl()
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()
# initd replace
if not os.path.exists(file_bin):
content = mw.readFile(file_tpl)
content = content.replace('{$SERVER_PATH}', service_path)
mw.writeFile(file_bin, content)
mw.execShell('chmod +x ' + file_bin)
# systemd
systemDir = mw.systemdCfgDir()
systemService = systemDir + '/' + getPluginName() + '.service'
if os.path.exists(systemDir) and not os.path.exists(systemService):
systemServiceTpl = getPluginDir() + '/init.d/' + getPluginName() + '.service.tpl'
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')
time.sleep(1)
return file_bin
def alistOp(method):
file = initDreplace()
current_os = mw.getOs()
if current_os == "darwin":
data = mw.execShell(file + ' ' + method)
if data[1] == '':
return 'ok'
return data[1]
if current_os.startswith("freebsd"):
data = mw.execShell('service ' + getPluginName() + ' ' + method)
if data[1] == '':
return 'ok'
return data[1]
data = mw.execShell('systemctl ' + method + ' ' + getPluginName())
if data[1] == '':
return 'ok'
return data[1]
def start():
return alistOp('start')
def stop():
return alistOp('stop')
def restart():
status = alistOp('restart')
return status
def reload():
return alistOp('reload')
def initdStatus():
current_os = mw.getOs()
if current_os == 'darwin':
return "Apple Computer does not support"
if current_os.startswith('freebsd'):
initd_bin = getInitDFile()
if os.path.exists(initd_bin):
return 'ok'
shell_cmd = 'systemctl status ' + \
getPluginName() + ' | grep loaded | grep "enabled;"'
data = mw.execShell(shell_cmd)
if data[0] == '':
return 'fail'
return 'ok'
def initdInstall():
current_os = mw.getOs()
if current_os == 'darwin':
return "Apple Computer does not support"
# freebsd initd install
if current_os.startswith('freebsd'):
import shutil
source_bin = initDreplace()
initd_bin = getInitDFile()
shutil.copyfile(source_bin, initd_bin)
mw.execShell('chmod +x ' + initd_bin)
mw.execShell('sysrc ' + getPluginName() + '_enable="YES"')
return 'ok'
mw.execShell('systemctl enable ' + getPluginName())
return 'ok'
def initdUinstall():
current_os = mw.getOs()
if current_os == 'darwin':
return "Apple Computer does not support"
if current_os.startswith('freebsd'):
initd_bin = getInitDFile()
os.remove(initd_bin)
mw.execShell('sysrc ' + getPluginName() + '_enable="NO"')
return 'ok'
mw.execShell('systemctl disable ' + getPluginName())
return 'ok'
def runLog():
return getServerDir() + '/data/log/log.log'
def pSqliteDb(dbname='databases'):
pos_file = getServerDir() + '/data/'
file = pos_file + '/data.db'
name = 'data'
conn = mw.M(dbname).dbPos(pos_file, name)
return conn
def clearCopyTask():
conn = pSqliteDb('x_task_items')
conn.where('key=?', ('copy',)).setField('persist_data','[]')
restart()
return mw.returnJson(True, '清空成功并重启服务!')
def homePage():
content = mw.readFile(getConf())
data = json.loads(content)
http_port = data['scheme']['http_port']
ip = mw.getLocalIp()
if mw.isAppleSystem():
ip = '127.0.0.1'
url = 'http://'+ip+":"+str(http_port)
# print(url)
return mw.returnJson(True, 'ok!', url)
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 == 'run_log':
print(runLog())
elif func == 'config_tpl':
print(configTpl())
elif func == 'read_config_tpl':
print(readConfigTpl())
elif func == 'clear_copy_task':
print(clearCopyTask())
elif func == 'home_page':
print(homePage())
else:
print('error')
-17
View File
@@ -1,17 +0,0 @@
{
"sort": 7,
"ps": "一个支持多种存储的文件列表程序",
"name": "alist",
"title": "Alist",
"shell": "install.sh",
"versions":["3.37.4","3.38.0","3.45.0"],
"tip": "soft",
"checks": "server/alist",
"path": "server/alist",
"display": 1,
"author": "alist",
"date": "2022-10-09",
"home": "https://alist.nn.ci",
"type": 0,
"pid": "5"
}
-13
View File
@@ -1,13 +0,0 @@
[Unit]
Description=A file list program that supports multiple storage
After=network.target
[Service]
Type=simple
WorkingDirectory={$SERVER_PATH}/alist
ExecStart={$SERVER_PATH}/alist/alist server
ExecReload=/bin/kill -USR2 $MAINPID
Restart=on-failure
[Install]
WantedBy=multi-user.target
-46
View File
@@ -1,46 +0,0 @@
#!/bin/sh
# chkconfig: 2345 55 25
# description: alist Service
### BEGIN INIT INFO
# Provides: alist
# Required-Start: $all
# Required-Stop: $all
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
# Short-Description: starts alist
# Description: starts the MDW-Web
### END INIT INFO
# Simple alist init.d script conceived to work on Linux systems
# as it does use of the /proc filesystem.
app_start(){
echo "Starting alist server..."
cd {$SERVER_PATH}/alist
./alist server >> {$SERVER_PATH}/alist/logs.pl 2>&1 &
}
app_stop(){
echo "dztasks stopped"
ps -ef| grep alist | grep -v grep | grep -v python | grep -v sh | awk '{print $2}'| xargs kill -9
}
case "$1" in
start)
app_start
;;
stop)
app_stop
;;
restart|reload)
app_stop
sleep 0.3
app_start
;;
*)
echo "Please use start or stop as first argument"
;;
esac
-93
View File
@@ -1,93 +0,0 @@
#!/bin/bash
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin:/opt/homebrew/bin
export PATH
curPath=`pwd`
rootPath=$(dirname "$curPath")
rootPath=$(dirname "$rootPath")
serverPath=$(dirname "$rootPath")
# cd /Users/midoks/Desktop/mwdev/server/mdserver-web/plugins/alist && bash install.sh install 3.37.4
# cd /www/server/mdserver-web/plugins/alist && bash install.sh install 3.45.0
VERSION=$2
sysArch=`arch`
sysName=`uname`
ALIST_ARCH_NAME=amd64
if [ "$sysArch" == "arm64" ];then
ALIST_ARCH_NAME=arm64
elif [ "$sysArch" == "x86_64" ]; then
ALIST_ARCH_NAME=amd64
elif [ "$sysArch" == "aarch64" ]; then
ALIST_ARCH_NAME=arm64
fi
ALIST_NAME=linux
if [ "$sysName" == "Darwin" ];then
ALIST_NAME=darwin
fi
Install_App()
{
echo '正在安装脚本文件...'
mkdir -p $serverPath/source
mkdir -p $serverPath/source/alist
FILE_TGZ=alist-${ALIST_NAME}-${ALIST_ARCH_NAME}.tar.gz
ALIST_DIR=$serverPath/source/alist
if [ ! -f $ALIST_DIR/${FILE_TGZ} ];then
wget --no-check-certificate -O $ALIST_DIR/${FILE_TGZ} https://github.com/alist-org/alist/releases/download/v${VERSION}/${FILE_TGZ}
fi
mkdir -p $serverPath/alist
cd $ALIST_DIR && tar -zxvf ${FILE_TGZ} -C $serverPath/alist
echo "${VERSION}" > $serverPath/alist/version.pl
cd ${rootPath} && python3 ${rootPath}/plugins/alist/index.py start
cd ${rootPath} && python3 ${rootPath}/plugins/alist/index.py initd_install
cd $serverPath/alist && ./alist admin set admin
# cd /www/server/alist && ./alist admin set admin
echo '安装完成'
}
Uninstall_App()
{
if [ -f /usr/lib/systemd/system/alist.service ];then
systemctl stop alist
systemctl disable alist
rm -rf /usr/lib/systemd/system/alist.service
systemctl daemon-reload
fi
if [ -f /lib/systemd/system/alist.service ];then
systemctl stop alist
systemctl disable alist
rm -rf /lib/systemd/system/alist.service
systemctl daemon-reload
fi
if [ -f $serverPath/alist/initd/alist ];then
$serverPath/alist/initd/alist stop
fi
if [ -d $serverPath/alist ];then
rm -rf $serverPath/alist
fi
echo "卸载alist成功"
}
action=$1
if [ "${1}" == 'install' ];then
Install_App
else
Uninstall_App
fi
-93
View File
@@ -1,93 +0,0 @@
function alistPost(method, version, args,callback){
var loadT = layer.msg('正在获取...', { icon: 16, time: 0, shade: 0.3 });
var req_data = {};
req_data['name'] = 'alist';
req_data['func'] = method;
req_data['version'] = version;
if (typeof(args) == 'string'){
req_data['args'] = JSON.stringify(toArrayObject(args));
} else {
req_data['args'] = JSON.stringify(args);
}
$.post('/plugins/run', req_data, function(data) {
layer.close(loadT);
if (!data.status){
//错误展示10S
layer.msg(data.msg,{icon:0,time:2000,shade: [10, '#000']});
return;
}
if(typeof(callback) == 'function'){
callback(data);
}
},'json');
}
function alistPostCallbak(method, version, args,callback){
var loadT = layer.msg('正在获取...', { icon: 16, time: 0, shade: 0.3 });
var req_data = {};
req_data['name'] = 'alist';
req_data['func'] = method;
args['version'] = version;
if (typeof(args) == 'string'){
req_data['args'] = JSON.stringify(toArrayObject(args));
} else {
req_data['args'] = JSON.stringify(args);
}
$.post('/plugins/callback', req_data, function(data) {
layer.close(loadT);
if (!data.status){
layer.msg(data.msg,{icon:0,time:2000,shade: [0.3, '#000']});
return;
}
if(typeof(callback) == 'function'){
callback(data);
}
},'json');
}
function clearTaskCopy(){
layer.confirm('您真的要清空复制任务吗?', { icon: 3, closeBtn: 2 }, function() {
alistPost('clear_copy_task', '', {}, function(data){
var rdata = $.parseJSON(data.data);
showMsg(rdata.msg, function(){},{ icon: rdata.status ? 1 : 2 });
});
});
}
function commonHomePage(){
alistPost('home_page', '', {}, function(data){
var rdata = $.parseJSON(data.data);
window.open(rdata.data);
});
}
function alistCommonFunc(){
var con = '';
con += '<hr/><p class="conf_p" style="text-align:center;">\
<button class="btn btn-default btn-sm" onclick="commonHomePage()">主页</button>\
<button class="btn btn-default btn-sm" onclick="clearTaskCopy()">清空复制任务</button>\
</p>';
$(".soft-man-con").html(con);
}
function alistReadme(){
var readme = '<ul class="help-info-text c7">';
readme += '<li>手动开启默认端口:5244</li>';
readme += '<li>默认admin:admin</li>';
readme += '<li>手动改密码: cd /www/server/alist && ./alist admin set NEW_PASSWORD</li>';
readme += '</ul>';
$('.soft-man-con').html(readme);
}
-424
View File
@@ -1,424 +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
web_dir = os.getcwd() + "/web"
if os.path.exists(web_dir):
sys.path.append(web_dir)
os.chdir(web_dir)
import core.mw as 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=600):
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):
self.__host = host = data["ftp_host"].strip()
if host.find(':') == -1:
self.__port = self.default_port
else:
self.__host = host.split(':')[0]
self.__port = host.split(':')[1]
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) + r"_(.+)_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

-101
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>
-311
View File
@@ -1,311 +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")
web_dir = os.getcwd() + "/web"
if os.path.exists(web_dir):
sys.path.append(web_dir)
os.chdir(web_dir)
import core.mw as mw
import core.db as db
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(':', 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 status():
return 'start'
def getConf():
cfg = getServerDir() + "/cfg.json"
if not os.path.exists(cfg):
rdata = {
'use_sftp':False,
}
return mw.returnJson(False, "未配置", rdata)
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 findPathName(path, filename):
f = os.scandir(path)
l = []
for ff in f:
t = {}
if ff.name.find(filename) > -1:
t['filename'] = path + '/' + ff.name
l.append(t)
return l
def backupAllFunc(stype):
name = sys.argv[2]
num = sys.argv[3]
prefix_dict = {
"site": "web",
"database": "db",
"path": "path",
}
backups = []
# print("stype:", stype)
# 提前获取-清理多余备份
if stype == 'site':
pid = mw.M('sites').where('name=?', (name,)).getField('id')
backups = mw.M('backup').where('type=? and pid=?', ('0', pid)).field('id,filename').select()
if stype == 'database':
db_path = mw.getServerDir() + '/mysql'
pid = mw.M('databases').dbPos(db_path, 'mysql').where('name=?', (name,)).getField('id')
backups = mw.M('backup').where('type=? and pid=?', ('1', pid)).field('id,filename').select()
if stype == 'path':
backup_dir = mw.getBackupDir()
backup_path = backup_dir + '/path'
_name = 'path_{}'.format(os.path.basename(name))
backups = findPathName(backup_path, _name)
# 其他类型关系性数据库(mysql类的)
if stype.find('database_') > -1:
plugin_name = stype.replace('database_', '')
db_path = mw.getServerDir() + '/' + plugin_name
pid = mw.M('databases').dbPos(db_path, 'mysql').where('name=?', (name,)).getField('id')
backups = mw.M('backup').where('type=? and pid=?', ('1', pid)).field('id,filename').select()
args = stype + " " + name + " " + num
cmd = 'python3 ' + mw.getPanelDir() + '/scripts/backup.py ' + args
if stype.find('database_') > -1:
plugin_name = stype.replace('database_', '')
args = "database " + name + " " + num
cmd = 'python3 ' + mw.getPanelDir() + '/plugins/' + plugin_name + '/scripts/backup.py ' + args
os.system(cmd)
# 开始执行上传信息.
if stype.find('database_') > -1:
bk_name = 'database'
plugin_name = stype.replace('database_', '')
bk_prefix = plugin_name + '/db'
stype = 'database'
else:
bk_prefix = prefix_dict[stype]
bk_name = stype
find_path = mw.getBackupDir() + '/' + bk_name + '/' + bk_prefix + '_' + name
if stype == 'path':
_name = 'path_{}'.format(os.path.basename(name))
find_path = mw.getBackupDir() + '/path/'+_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 == "":
mw.echoInfo("not find upload file!")
return ''
mw.echoInfo("准备上传文件 {}".format(filename))
ftp = FtpPSClient()
ftp.uploadFile(filename, stype)
# print(backups)
backups = sorted(backups, key=lambda x: x['filename'], reverse=False)
mw.echoStart('开始删除远程备份')
num = int(num)
sep = len(backups) - num
if sep > -1:
for backup in backups:
fn = os.path.basename(backup['filename'])
object_name = ftp.buildDirName(stype, fn)
ftp.deleteFile(object_name)
mw.echoInfo("已清理远程过期备份文件:" + object_name)
sep -= 1
if sep < 0:
break
mw.echoEnd('结束删除远程备份')
return ''
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']) or func.find('database_') > -1:
print(backupAllFunc(func))
else:
print('error')
-18
View File
@@ -1,18 +0,0 @@
{
"sort":6,
"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":"https://github.com/midoks/mdserver-web",
"date":"2022-10-23",
"pid": "5"
}
-31
View File
@@ -1,31 +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")
sys_os=`uname`
Install_App()
{
mkdir -p ${serverPath}/backup_ftp
echo "${1}" > ${serverPath}/backup_ftp/version.pl
echo '安装完成'
}
Uninstall_App()
{
rm -rf ${serverPath}/backup_ftp
}
action=$1
if [ "${1}" == 'install' ];then
Install_App $2
else
Uninstall_App $2
fi
-258
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

-74
View File
@@ -1,74 +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="commonFunc();">常用功能</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 cleanPost(method, version, args, callback){
var loadT = layer.msg('正在获取...', { icon: 16, time: 0, shade: 0.3 });
var req_data = {};
req_data['name'] = 'clean';
req_data['func'] = method;
req_data['version'] = version;
if (typeof(args) == 'string'){
req_data['args'] = JSON.stringify(toArrayObject(args));
} else {
req_data['args'] = JSON.stringify(args);
}
$.post('/plugins/run', req_data, function(data) {
layer.close(loadT);
if (!data.status){
layer.msg(data.msg,{icon:0,time:2000,shade: [10, '#000']});
return;
}
if(typeof(callback) == 'function'){
callback(data);
}
},'json');
}
function cleanRun(){
cleanPost('clean_run', '', {}, function(rdata){
// var rdata = $.parseJSON(rdata.data);
// layer.msg(rdata.msg, { icon: rdata.status ? 1 : 2 });
});
layer.msg("执行成功!", { icon: 1});
}
function commonFunc(){
con = '<hr/><p class="conf_p" style="text-align:center;">\
<button class="btn btn-default btn-sm" onclick="cleanRun()">手动执行</button> \
</p>';
$(".soft-man-con").html(con);
}
function pRead(){
var readme = '<ul class="help-info-text c7">';
readme += '<li>启动后,加入到计划任务每隔7天[0:15]执行。</li>';
readme += '<li>【*】代表要删除。</li>';
readme += '<li>添加/删除清除日志,可在`配置修改`中添加/删除。</li>';
readme += '<li>如果是目录,会寻找【.log】后缀的文件清空。</li>';
readme += '</ul>';
$('.soft-man-con').html(readme);
}
</script>
-298
View File
@@ -1,298 +0,0 @@
# coding:utf-8
import sys
import io
import os
import time
web_dir = os.getcwd() + "/web"
if os.path.exists(web_dir):
sys.path.append(web_dir)
os.chdir(web_dir)
import core.mw as 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/log/cron-*",
"/var/log/maillog-*",
"/var/log/secure-*",
"/var/log/spooler-*",
"/var/log/yum.log-*",
"/var/log/messages-*",
"/var/log/btmp-*",
"/var/log/auth.*",
"/var/log/messages.*",
"/var/log/debug.*",
"/var/log/syslog.*",
"/var/log/syslog",
"/var/log/mail.*",
"/var/log/btmp.*",
"/var/log/sa/sa*",
"/var/log/sysstat/sa*",
"/var/log/atop/atop*",
"/var/log/anaconda/*.log",
"/var/log/dpkg.log.*",
"/var/log/alternatives.log.*",
"/var/log/user.log.*",
"/var/log/kern.log.*",
"/var/log/daemon.log.*",
"/var/log/*.gz",
"/var/log/*.xz",
"/var/log/*.log.*",
"/var/log/audit/audit.log.*",
"/var/log/hawkey.log-*",
"/var/log/apt/*.gz",
"/var/log/apt/*.xz",
"/var/log/rhsm/rhsm.log-*",
"/var/log/rhsm/rhsmcertd.log-*",
"/var/log/exim4/*.gz",
"/var/log/journal/*",
"/var/spool/clientmqueue/*",
"/tmp/yum_save_*",
"/tmp/tmp.*",
"/www/server/dztasks/logs/dztasks.*.log",
"/www/server/dztasks/logs/dztasks_*",
]
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/syslog",
"/var/log/cron",
"/www/wwwlogs",
"/www/server/rsyncd",
"/www/server/acme_pandominassl_apply",
"/www/server/sphinx/index",
"/www/server/mongodb/logs",
"/www/server/php/53/var/log",
"/www/server/php/54/var/log",
"/www/server/php/55/var/log",
"/www/server/php/56/var/log",
"/www/server/php/70/var/log",
"/www/server/php/71/var/log",
"/www/server/php/72/var/log",
"/www/server/php/73/var/log",
"/www/server/php/74/var/log",
"/www/server/php/80/var/log",
"/www/server/php/81/var/log",
"/www/server/php/82/var/log",
"/www/server/php/83/var/log",
"/www/server/php/84/var/log",
"/www/server/openresty/nginx/logs",
"/www/server/phpmyadmin",
"/www/server/redis/data",
"/www/server/alist/data/log",
"/www/server/dztasks/logs",
"/www/server/rsyncd/lsyncd.log"
"/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 cleanRun():
plugin_dir = getPluginDir()
log_file = getServerDir()+'/clean.log'
cmd = 'cd '+mw.getPanelDir()+' && python3 '+plugin_dir+'/index.py clean > '+log_file
os.system(cmd)
return mw.returnJson(True, '执行成功!')
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()
elif func == 'clean_run':
print(cleanRun())
else:
print('error')
-17
View File
@@ -1,17 +0,0 @@
{
"sort":2,
"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"
}
-42
View File
@@ -1,42 +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")
VERSION=$2
if [ -f ${rootPath}/bin/activate ];then
source ${rootPath}/bin/activate
fi
Install_app()
{
echo '正在安装脚本文件...'
mkdir -p $serverPath/clean
cd ${rootPath} && python3 ${rootPath}/plugins/clean/index.py start
echo "${VERSION}" > $serverPath/clean/version.pl
echo '安装完成'
}
Uninstall_app()
{
cd ${rootPath} && python3 ${rootPath}/plugins/clean/index.py stop
rm -rf $serverPath/clean
echo "Uninstall_clean"
}
action=$1
if [ "${1}" == 'install' ];then
Install_app
else
Uninstall_app
fi
-129
View File
@@ -1,129 +0,0 @@
# coding:utf-8
import sys
import io
import os
import time
import json
web_dir = os.getcwd() + "/web"
if os.path.exists(web_dir):
sys.path.append(web_dir)
os.chdir(web_dir)
import core.mw as mw
from utils.crontab import crontab as MwCrontab
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():
conf = getTaskConf()
if os.path.exists(conf):
return json.loads(mw.readFile(getTaskConf()))
return {
"task_id": -1,
"period": "day-n",
"where1": "7",
"hour": "0",
"minute": "15",
}
def createBgTask():
removeBgTask()
createBgTaskByName(getPluginName())
def createBgTaskByName(name):
args = getConfigData()
_name = "[勿删]日志清理[" + name + "]"
res = mw.M("crontab").field("id, name").where("name=?", (_name,)).find()
if res:
return True
if "task_id" in args and args["task_id"] > 0:
res = mw.M("crontab").field("id, name").where(
"id=?", (args["task_id"],)).find()
if res and res["id"] == args["task_id"]:
print("计划任务已经存在!")
return True
mw_dir = mw.getPanelDir()
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': args['period'],
'week': "",
'where1': args['where1'],
'hour': args['hour'],
'minute': args['minute'],
'save': "",
'backup_to': "",
'stype': "toShell",
'sname': '',
'sbody': cmd,
'url_address': '',
}
task_id = MwCrontab.instance().add(params)
if task_id > 0:
args["task_id"] = task_id
args["name"] = name
mw.writeFile(getTaskConf(), json.dumps(args))
def removeBgTask():
cfg = getConfigData()
if "task_id" in cfg 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"]:
data = MwCrontab.instance().delete(cfg["task_id"])
if data[0]:
cfg["task_id"] = -1
mw.writeFile(getTaskConf(), json.dumps(cfg))
return True
return False
if __name__ == "__main__":
if len(sys.argv) > 1:
action = sys.argv[1]
if action == "remove":
removeBgTask()
elif action == "add":
createBgTask()
Binary file not shown.

Before

Width:  |  Height:  |  Size: 31 KiB

-31
View File
@@ -1,31 +0,0 @@
<style>
.overflow_hide {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
vertical-align: middle;
}
</style>
<div class="bt-form">
<div class='plugin_version'></div>
<div class="bt-w-main">
<div class="bt-w-menu">
<p class="bgw" onclick="pluginService('cloudreve');">服务</p>
<p onclick="pluginInitD('cloudreve');">自启动</p>
<p onclick="alistCommonFunc();">常用功能</p>
<p onclick="pluginConfigTpl('cloudreve',$('.plugin_version').attr('version'));">配置修改</p>
<p onclick="pluginLogs('cloudreve','','run_log');">运行日志</p>
<p onclick="alistReadme();">相关说明</p>
</div>
<div class="bt-w-con pd15">
<div class="soft-man-con" style="height: 520px; overflow: auto;"></div>
</div>
</div>
</div>
<script type="text/javascript">
$.getScript( "/plugins/file?name=cloudreve&f=js/cloudreve.js", function(){
pluginService('cloudreve', $('.plugin_version').attr('version'));
});
</script>
-292
View File
@@ -1,292 +0,0 @@
# coding:utf-8
import sys
import io
import os
import time
import re
web_dir = os.getcwd() + "/web"
if os.path.exists(web_dir):
sys.path.append(web_dir)
os.chdir(web_dir)
import core.mw as mw
app_debug = False
if mw.isAppleSystem():
app_debug = True
def getPluginName():
return 'cloudreve'
def getPluginDir():
return mw.getPluginDir() + '/' + getPluginName()
def getServerDir():
return mw.getServerDir() + '/' + getPluginName()
def getInitDFile():
current_os = mw.getOs()
if current_os == 'darwin':
return '/tmp/' + getPluginName()
if current_os.startswith('freebsd'):
return '/etc/rc.d/' + getPluginName()
return '/etc/init.d/' + getPluginName()
def getInitDTpl():
path = getPluginDir() + "/init.d/" + getPluginName() + ".tpl"
return path
def getArgs():
args = sys.argv[3:]
tmp = {}
args_len = len(args)
if args_len == 1:
t = args[0].strip('{').strip('}')
if t.strip() == '':
tmp = []
else:
t = t.split(':')
tmp[t[0]] = t[1]
tmp[t[0]] = t[1]
elif args_len > 1:
for i in range(len(args)):
t = args[i].split(':')
tmp[t[0]] = t[1]
return tmp
def checkArgs(data, ck=[]):
for i in range(len(ck)):
if not ck[i] in data:
return (False, mw.returnJson(False, '参数:(' + ck[i] + ')没有!'))
return (True, mw.returnJson(True, 'ok'))
def getConf():
path = getServerDir() + "/conf.ini"
return path
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 status():
data = mw.execShell(
"ps aux|grep cloudreve |grep -v grep | grep -v python | grep -v mdserver-web | awk '{print $2}'")
if data[0] == '':
return 'stop'
return 'start'
def contentReplace(content):
service_path = mw.getServerDir()
content = content.replace('{$ROOT_PATH}', mw.getFatherDir())
content = content.replace('{$SERVER_PATH}', service_path)
content = content.replace('{$SERVER_APP}', service_path + '/'+getPluginName())
return content
def initDreplace():
file_tpl = getInitDTpl()
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()
# initd replace
if not os.path.exists(file_bin):
content = mw.readFile(file_tpl)
content = content.replace('{$SERVER_PATH}', service_path)
mw.writeFile(file_bin, content)
mw.execShell('chmod +x ' + file_bin)
# systemd
systemDir = mw.systemdCfgDir()
systemService = systemDir + '/' + getPluginName() + '.service'
if os.path.exists(systemDir) and not os.path.exists(systemService):
systemServiceTpl = getPluginDir() + '/init.d/' + getPluginName() + '.service.tpl'
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 alistOp(method):
file = initDreplace()
current_os = mw.getOs()
if current_os == "darwin":
data = mw.execShell(file + ' ' + method)
if data[1] == '':
return 'ok'
return data[1]
if current_os.startswith("freebsd"):
data = mw.execShell('service ' + getPluginName() + ' ' + method)
if data[1] == '':
return 'ok'
return data[1]
data = mw.execShell('systemctl ' + method + ' ' + getPluginName())
if data[1] == '':
return 'ok'
return data[1]
def start():
return alistOp('start')
def stop():
return alistOp('stop')
def restart():
status = alistOp('restart')
return status
def reload():
return alistOp('reload')
def initdStatus():
current_os = mw.getOs()
if current_os == 'darwin':
return "Apple Computer does not support"
if current_os.startswith('freebsd'):
initd_bin = getInitDFile()
if os.path.exists(initd_bin):
return 'ok'
shell_cmd = 'systemctl status ' + \
getPluginName() + ' | grep loaded | grep "enabled;"'
data = mw.execShell(shell_cmd)
if data[0] == '':
return 'fail'
return 'ok'
def initdInstall():
current_os = mw.getOs()
if current_os == 'darwin':
return "Apple Computer does not support"
# freebsd initd install
if current_os.startswith('freebsd'):
import shutil
source_bin = initDreplace()
initd_bin = getInitDFile()
shutil.copyfile(source_bin, initd_bin)
mw.execShell('chmod +x ' + initd_bin)
mw.execShell('sysrc ' + getPluginName() + '_enable="YES"')
return 'ok'
mw.execShell('systemctl enable ' + getPluginName())
return 'ok'
def initdUinstall():
current_os = mw.getOs()
if current_os == 'darwin':
return "Apple Computer does not support"
if current_os.startswith('freebsd'):
initd_bin = getInitDFile()
os.remove(initd_bin)
mw.execShell('sysrc ' + getPluginName() + '_enable="NO"')
return 'ok'
mw.execShell('systemctl disable ' + getPluginName())
return 'ok'
def runLog():
return getServerDir() + '/logs.pl'
def pSqliteDb(dbname='databases'):
pos_file = getServerDir() + '/data/'
file = pos_file + '/data.db'
name = 'data'
conn = mw.M(dbname).dbPos(pos_file, name)
return conn
def getCloudrevePort():
file = getConf()
content = mw.readFile(file)
rep = r'Listen\s*=\s*(.*)'
tmp = re.search(rep, content)
return tmp.groups()[0].strip()
def homePage():
http_port = getCloudrevePort()
ip = mw.getLocalIp()
if mw.isAppleSystem():
ip = '127.0.0.1'
url = 'http://'+ip+""+str(http_port)
# print(url)
return mw.returnJson(True, 'ok!', url)
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 == 'run_log':
print(runLog())
elif func == 'config_tpl':
print(configTpl())
elif func == 'read_config_tpl':
print(readConfigTpl())
elif func == 'home_page':
print(homePage())
else:
print('error')
-17
View File
@@ -1,17 +0,0 @@
{
"sort": 7,
"ps": "一个支持多种存储的文件列表程序",
"name": "cloudreve",
"title": "cloudreve",
"shell": "install.sh",
"versions":["3.8.3"],
"tip": "soft",
"checks": "server/cloudreve",
"path": "server/cloudreve",
"display": 1,
"author": "cloudreve",
"date": "2024-10-14",
"home": "https://cloudreve.org/",
"type": 0,
"pid": "5"
}
@@ -1,13 +0,0 @@
[Unit]
Description=A file list program that supports multiple storage
After=network.target
[Service]
Type=simple
WorkingDirectory={$SERVER_PATH}/cloudreve
ExecStart={$SERVER_PATH}/cloudreve/cloudreve
ExecReload=/bin/kill -USR2 $MAINPID
Restart=on-failure
[Install]
WantedBy=multi-user.target
-46
View File
@@ -1,46 +0,0 @@
#!/bin/sh
# chkconfig: 2345 55 25
# description: cloudreve Service
### BEGIN INIT INFO
# Provides: cloudreve
# Required-Start: $all
# Required-Stop: $all
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
# Short-Description: starts cloudreve
# Description: starts the MDW-Web
### END INIT INFO
# Simple cloudreve init.d script conceived to work on Linux systems
# as it does use of the /proc filesystem.
app_start(){
echo "Starting cloudreve server..."
cd {$SERVER_PATH}/cloudreve
./cloudreve >> {$SERVER_PATH}/cloudreve/logs.pl 2>&1 &
}
app_stop(){
echo "dztasks stopped"
ps -ef| grep cloudreve | grep -v grep | grep -v python | grep -v sh | awk '{print $2}'| xargs kill
}
case "$1" in
start)
app_start
;;
stop)
app_stop
;;
restart|reload)
app_stop
sleep 0.3
app_start
;;
*)
echo "Please use start or stop as first argument"
;;
esac
-90
View File
@@ -1,90 +0,0 @@
#!/bin/bash
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin:/opt/homebrew/bin
export PATH
curPath=`pwd`
rootPath=$(dirname "$curPath")
rootPath=$(dirname "$rootPath")
serverPath=$(dirname "$rootPath")
# cd /Users/midoks/Desktop/mwdev/server/mdserver-web/plugins/cloudreve && bash install.sh install 3.8.3
# cd /www/server/mdserver-web/plugins/cloudreve && bash install.sh install 3.8.3
VERSION=$2
sysArch=`arch`
sysName=`uname`
ALIST_ARCH_NAME=amd64
if [ "$sysArch" == "arm64" ];then
ALIST_ARCH_NAME=arm64
elif [ "$sysArch" == "x86_64" ]; then
ALIST_ARCH_NAME=amd64
elif [ "$sysArch" == "aarch64" ]; then
ALIST_ARCH_NAME=aarch64
fi
ALIST_NAME=linux
if [ "$sysName" == "Darwin" ];then
ALIST_NAME=darwin
fi
Install_App()
{
echo '正在安装脚本文件...'
mkdir -p $serverPath/source
mkdir -p $serverPath/source/cloudreve
FILE_TGZ=cloudreve_${VERSION}_${ALIST_NAME}_${ALIST_ARCH_NAME}.tar.gz
CLOUDREVE_DIR=$serverPath/source/cloudreve
if [ ! -f $CLOUDREVE_DIR/${FILE_TGZ} ];then
wget -O $CLOUDREVE_DIR/${FILE_TGZ} https://github.com/cloudreve/Cloudreve/releases/download/${VERSION}/${FILE_TGZ}
fi
mkdir -p $serverPath/cloudreve
cd $CLOUDREVE_DIR && tar -zxvf ${FILE_TGZ} -C $serverPath/cloudreve
echo "${VERSION}" > $serverPath/cloudreve/version.pl
cd ${rootPath} && python3 ${rootPath}/plugins/cloudreve/index.py start
cd ${rootPath} && python3 ${rootPath}/plugins/cloudreve/index.py initd_install
# cd $serverPath/cloudreve && ./alist admin set admin
echo '安装cloudreve完成'
}
Uninstall_App()
{
if [ -f /usr/lib/systemd/system/cloudreve.service ];then
systemctl stop cloudreve
systemctl disable cloudreve
rm -rf /usr/lib/systemd/system/cloudreve.service
systemctl daemon-reload
fi
if [ -f /lib/systemd/system/cloudreve.service ];then
systemctl stop cloudreve
systemctl disable cloudreve
rm -rf /lib/systemd/system/cloudreve.service
systemctl daemon-reload
fi
if [ -f $serverPath/cloudreve/initd/cloudreve ];then
$serverPath/cloudreve/initd/cloudreve stop
fi
if [ -d $serverPath/cloudreve ];then
rm -rf $serverPath/cloudreve
fi
echo "卸载cloudreve成功"
}
action=$1
if [ "${1}" == 'install' ];then
Install_App
else
Uninstall_App
fi
-82
View File
@@ -1,82 +0,0 @@
function alistPost(method, version, args,callback){
var loadT = layer.msg('正在获取...', { icon: 16, time: 0, shade: 0.3 });
var req_data = {};
req_data['name'] = 'cloudreve';
req_data['func'] = method;
req_data['version'] = version;
if (typeof(args) == 'string'){
req_data['args'] = JSON.stringify(toArrayObject(args));
} else {
req_data['args'] = JSON.stringify(args);
}
$.post('/plugins/run', req_data, function(data) {
layer.close(loadT);
if (!data.status){
//错误展示10S
layer.msg(data.msg,{icon:0,time:2000,shade: [10, '#000']});
return;
}
if(typeof(callback) == 'function'){
callback(data);
}
},'json');
}
function alistPostCallbak(method, version, args,callback){
var loadT = layer.msg('正在获取...', { icon: 16, time: 0, shade: 0.3 });
var req_data = {};
req_data['name'] = 'cloudreve';
req_data['func'] = method;
args['version'] = version;
if (typeof(args) == 'string'){
req_data['args'] = JSON.stringify(toArrayObject(args));
} else {
req_data['args'] = JSON.stringify(args);
}
$.post('/plugins/callback', req_data, function(data) {
layer.close(loadT);
if (!data.status){
layer.msg(data.msg,{icon:0,time:2000,shade: [0.3, '#000']});
return;
}
if(typeof(callback) == 'function'){
callback(data);
}
},'json');
}
function commonHomePage(){
alistPost('home_page', '', {}, function(data){
var rdata = $.parseJSON(data.data);
window.open(rdata.data);
});
}
function alistCommonFunc(){
var con = '';
con += '<hr/><p class="conf_p" style="text-align:center;">\
<button class="btn btn-default btn-sm" onclick="commonHomePage()">主页</button>\
</p>';
$(".soft-man-con").html(con);
}
function alistReadme(){
var readme = '<ul class="help-info-text c7">';
readme += '<li>记住安装时生成的用户+密码</li>';
readme += '</ul>';
$('.soft-man-con').html(readme);
}
-78
View File
@@ -1,78 +0,0 @@
# plugins_cryptocurrency_trade
- https://docs.ccxt.com/en/latest/install.html#python-proxies
数字货币量化交易插件
```
cd /www/server/mdserver-web && python3 plugins/cryptocurrency_trade/ccxt/okex/strategy/st_demo.py
python3 plugins/cryptocurrency_trade/ccxt/okex/strategy/st_demo.py
miniconda3
```
# 免费TV策略
https://cn.tradingview.com/scripts/?script_type=strategies
### tradingview pine 指标
```
// This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © TenCloud
//@version=5
indicator("第一个指标",shorttitle = "开心吧")
ema10 = ta.ema(close, 25)
ema100 = ta.ema(close, 100)
l1=plot(ema10, color=color.red,title = "ema10")
l2=plot(ema100,color = color.green, title = "ema100")
buy = ta.crossover(ema10,ema100)
sell = ta.crossunder(ema10,ema100)
plotchar(buy, text = "buy", color=color.green)
plotchar(sell, text="sell", color=color.red)
l_color = ema10>ema100 ? color.green:color.red
fill(l1,l2,color=color.new(l_color,70))
```
### tradingview pine 策略
```
// This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © TenCloud
//@version=5
strategy("第一个指标",shorttitle = "开心吧", overlay = true, initial_capital = 1000)
ema10 = ta.ema(close, 25)
ema100 = ta.ema(close, 100)
l1=plot(ema10, color=color.red,title = "ema10")
l2=plot(ema100,color = color.green, title = "ema100")
buy = ta.crossover(ema10,ema100)
sell = ta.crossunder(ema10,ema100)
if buy
strategy.entry("long1", strategy.long,1)
if sell
strategy.close("long1", qty_percent = 100,comment = "平仓多单")
// plotchar(buy, text = "buy", color=color.green)
// plotchar(sell, text="sell", color=color.red)
// l_color = ema10>ema100 ? color.green:color.red
// fill(l1,l2,color=color.new(l_color,70))
```
@@ -1,555 +0,0 @@
import ccxt
from datetime import datetime
import time
import sys
import json
import os
import glob
import threading
from pprint import pprint
# print(os.getcwd())
sys.path.append(os.getcwd() + "/class/core")
import mw
# cd /www/server/mdserver-web && source bin/activate
# python3 plugins/cryptocurrency_trade/ccxt/public_data/data.py run
# python3 plugins/cryptocurrency_trade/ccxt/public_data/data.py long
# 查看支持的交易所
# print(ccxt.exchanges)
# 代理设置
# exchange = ccxt.poloniex({
# 'proxies': {
# 'http': 'http://127.0.0.1:1088'
# },
# })
# exchange = ccxt.poloniex()
exchange = ccxt.okex()
def getPluginName():
return 'cryptocurrency_trade'
def getPluginDir():
return mw.getPluginDir() + '/' + getPluginName()
def getServerDir():
return mw.getServerDir() + '/' + getPluginName()
def getConfigData():
cfg_path = getServerDir() + "/data.cfg"
if not os.path.exists(cfg_path):
mw.writeFile(cfg_path, '{}')
t = mw.readFile(cfg_path)
return json.loads(t)
def writeLog(log_str):
if __name__ == "__main__":
print(log_str)
log_file = getServerDir() + '/logs/datasource.log'
mw.writeFileLog(log_str, log_file, 1 * 1024 * 1024)
return True
def isSetDbConf():
data = getConfigData()
if 'db' in data:
return True
return False
def beforeDate(day=360):
dd = time.time() - day * 86400
d = datetime.fromtimestamp(dd)
day = d.strftime("%Y-%m-%d")
return day
def getTextTimeShow(time):
d = datetime.fromtimestamp(time)
day = d.strftime("%Y-%m-%d %H:%M:%S")
return day
def isSqlError(mysqlMsg):
# 检测数据库执行错误
mysqlMsg = str(mysqlMsg)
if "MySQLdb" in mysqlMsg:
return mw.returnJson(False, 'MySQLdb组件缺失! <br>进入SSH命令行输入: pip install mysql-python | pip install mysqlclient==2.0.3')
if "2002," in mysqlMsg:
return mw.returnJson(False, '数据库连接失败,请检查数据库服务是否启动!')
if "2003," in mysqlMsg:
return mw.returnJson(False, "Can't connect to MySQL server on '127.0.0.1' (61)")
if "using password:" in mysqlMsg:
return mw.returnJson(False, '数据库密码错误')
if "1045" in mysqlMsg:
return mw.returnJson(False, '连接错误!')
if "SQL syntax" in mysqlMsg:
return mw.returnJson(False, 'SQL语法错误!')
if "Connection refused" in mysqlMsg:
return mw.returnJson(False, '数据库连接失败,请检查数据库服务是否启动!')
if "1133" in mysqlMsg:
return mw.returnJson(False, '数据库用户不存在!')
if "1007" in mysqlMsg:
return mw.returnJson(False, '数据库已经存在!')
return None
def pMysqlDb():
# pymysql
db = mw.getMyORM()
data = getConfigData()
db_data = data['db']
db.setHost(db_data['db_host'])
db.setPort(db_data['db_port'])
db.setUser(db_data['db_user'])
db.setPwd(db_data['db_pass'])
db.setDbName(db_data['db_name'])
return db
def makeInsertSql(table, item):
sql = "insert into " + table
keyStr = '('
valueStr = ' values('
for i in item:
# print i, item[i]
keyStr += '`' + str(i) + '`,'
valueStr += "'" + str(item[i]) + "',"
keyStrLen = len(keyStr)
keyStr = keyStr[0:keyStrLen - 1]
keyStr += ') '
valueStrLen = len(valueStr)
valueStr = valueStr[0:valueStrLen - 1]
valueStr += ') '
sql += keyStr
sql += valueStr
return sql
def makeReplaceSql(table, item):
sql = "replace into " + table
keyStr = '('
valueStr = ' values('
for i in item:
# print i, item[i]
keyStr += '`' + str(i) + '`,'
valueStr += "'" + str(item[i]) + "',"
keyStrLen = len(keyStr)
keyStr = keyStr[0:keyStrLen - 1]
keyStr += ') '
valueStrLen = len(valueStr)
valueStr = valueStr[0:valueStrLen - 1]
valueStr += ') '
sql += keyStr
sql += valueStr
return sql
def makeUpdateSql(table, item, mid):
sql = "update " + table + " set "
keyStr = ''
for i in item:
keyStr += '`' + str(i) + '`=' + "'" + item[i] + "',"
keyStrLen = len(keyStr)
keyStr = keyStr[0:keyStrLen - 1]
sql += keyStr
sql += " where id = '" + str(mid) + "'"
return sql
def dataToDb(table_name, data):
pdb = pMysqlDb()
for i in data:
rdata = {
'addtime': int(i[0] / 1000),
'open': i[1],
'high': i[2],
'low': i[3],
'close': i[4],
'vol': i[5],
}
# sql = """SELECT id FROM %s where addtime='%s' LIMIT 1""" % (
# table_name, rdata['addtime'])
# fdata = pdb.query(sql)
# if fdata:
# print(table_name + ":" + str(rdata['addtime']) + ", old to db ok")
# else:
# isql = makeReplaceSql(table_name, rdata)
# r = pdb.execute(isql)
# print(table_name + ":" + str(rdata['addtime']) + ", go to db ok")
isql = makeReplaceSql(table_name, rdata)
r = pdb.execute(isql)
# print(table_name + ":" + str(rdata['addtime']) + ", go to db ok")
return True
def makeTableName(input_type="btc", input_tf="1m"):
table_name = "ct_%s_%s" % (input_type, input_tf)
return table_name
def isHasTable(input_type="btc", input_tf="1m"):
pdb = pMysqlDb()
# input_type = 'btc'
# input_tf = '1m'
table_name = makeTableName(input_type, input_tf)
mtable = pdb.query("show tables like '%s'" % (table_name,))
if len(mtable) != 0:
return True
return False
def createSql(input_type="btc", input_tf="1m"):
pdb = pMysqlDb()
# input_type = 'btc'
# input_tf = '1m'
table_name = makeTableName(input_type, input_tf)
mtable = pdb.query("show tables like '%s'" % (table_name,))
if len(mtable) != 0:
return True
sql_tpl = getPluginDir() + "/conf/create.sql"
content = mw.readFile(sql_tpl)
content = content.replace("xx1", input_type)
content = content.replace("xx2", input_tf)
res = pdb.execute(content)
# pprint(res)
return True
def getDataFetch(symbol, start_time="2023-3-1", input_tf="1m", limit=500):
start_time = datetime.strptime(start_time, '%Y-%m-%d')
end_time = datetime.strptime(end_time, '%Y-%m-%d')
start_time_stamp = int(time.mktime(start_time.timetuple())) * 1000
data = exchange.fetch_ohlcv(symbol, timeframe=input_tf,
since=start_time_stamp, limit=limit)
return data
def getPointData(symbol, start_time=1677713220000, input_tf="1m", limit=500):
data = exchange.fetch_ohlcv(
symbol, timeframe=input_tf, since=start_time, limit=limit)
return data
def toUnixTimeSecond(tf="1m"):
if tf.find("m") > -1:
v = int(tf.replace("m", ''))
return v * 60
if tf.find("h") > -1:
v = int(tf.replace("h", ''))
return v * 3600
if tf.find("d") > -1:
v = int(tf.replace("d", ''))
return v * 86400
return 0
def findAndUpdateData(tag, input_tf="1m", start_time="2023-1-1", limit=300):
pdb = pMysqlDb()
table_name = makeTableName(tag, input_tf)
sql = 'SELECT addtime FROM ' + table_name + ' order by addtime desc LIMIT 1'
qdata = pdb.query(sql)
start_time = datetime.strptime(start_time, '%Y-%m-%d')
start_time = int(time.mktime(start_time.timetuple())) * 1000
if len(qdata) != 0:
start_time = int(qdata[0]['addtime']) * 1000
pre_time = toUnixTimeSecond(input_tf) * 5 * 1000
start_time = start_time - pre_time
# pprint(start_time)
symbol = tag.upper() + "/USDT"
data = getPointData(symbol, start_time, input_tf, limit)
# pprint(data)
# print("------------lini===========")
# pprint(data[1:])
if len(qdata) == 1:
data = data[1:]
dataToDb(table_name, data)
now = time.strftime("%m/%d %H:%M:%S")
data_len = len(data)
msg_head = now + "|虚拟币:" + tag + ",tf:" + \
input_tf + "!,总数:" + str(data_len)
writeLog(msg_head)
if data_len > 0:
# print("new data time", data[data_len - 1][0])
dt = getTextTimeShow(data[data_len - 1][0] / 1000)
msg = now + "|最新日期:" + dt
writeLog(msg)
table_name = makeTableName(tag, input_tf)
sql = "SELECT addtime FROM " + table_name + \
" order by addtime desc limit 10000,1"
qdata = pdb.query(sql)
if len(qdata) > 0:
print(qdata)
# input_tf_time = toUnixTimeSecond(input_tf)
# now_t = int(time.time())
# del_before_t = now_t - input_tf_time * 1000
sql = "delete from %s where addtime<'%d' " % (
table_name, qdata[0]['addtime'],)
writeLog("删除冗余数据:" + str(sql))
pdb.execute(sql)
return True
def dataToDbTpl(tag='btc', input_tf="1m", start_time="2023-1-1", limit=300):
# tag = "btc"
# tf = "1m"
# start_time = "2023-1-1"
# end_time = "2023-3-2"
symbol = tag.lower() + '/USDT'
if not isHasTable(tag, input_tf):
createSql(tag, input_tf)
data = getDataFetch(symbol, start_time, input_tf, limit)
table_name = makeTableName(tag, input_tf)
dataToDb(table_name, data)
now = time.strftime("%m/%d %H:%M:%S")
data_len = len(data)
writeLog(now + "|数据获取成功!,总数:" + str(data_len))
else:
findAndUpdateData(tag, input_tf, start_time, limit)
return True
def dataToDbList(input_tf="1m", start_time="2023-1-1"):
data = getConfigData()
if not 'token' in data:
writeLog("未设置同步配置,需要添加币种!")
return
for t in data['token']:
dataToDbTpl(t, input_tf, start_time)
time.sleep(4)
def dataRunToDb():
data = getConfigData()
if not 'db' in data:
writeLog("数据库未设置!")
return
tag = "btc"
symbol = tag.upper() + '/USDT'
# pprint(exchange.fetch_ticker('XRP/USDT'))
limit_count = 1
start_time = "2023-1-1"
end_time = "2023-3-2"
tf = "1m"
if not isHasTable(tag, tf):
createSql(tag, tf)
data = getDataFetch(symbol, start_time, end_time, tf, limit_count)
table_name = makeTableName(tag, tf)
dataToDb(table_name, data)
china_datetime = str(datetime.now())
data_len = len(data)
writeLog(china_datetime + ":数据获取成功!,总数:" + str(data_len))
else:
findAndUpdateData(tag, tf, start_time, 30)
return data
def startTask():
# 任务队列
try:
while True:
time.sleep(5)
except Exception as e:
time.sleep(60)
startTask()
def setDaemon(t):
if sys.version_info.major == 3 and sys.version_info.minor >= 10:
t.daemon = True
else:
t.setDaemon(True)
return t
def dataDay():
try:
while True:
if not isSetDbConf():
print("数据库未设置!")
else:
start_time = beforeDate(2 * 365)
dataToDbList("1d", start_time=start_time)
time.sleep(3600)
# time.sleep(10)
except Exception as e:
time.sleep(60)
dataDay()
def data4h():
try:
while True:
if not isSetDbConf():
print("数据库未设置!")
else:
start_time = beforeDate(365)
dataToDbList("4h", start_time=start_time)
time.sleep(10)
except Exception as e:
time.sleep(60)
data4h()
def data1h():
try:
while True:
if not isSetDbConf():
print("数据库未设置!")
else:
start_time = beforeDate(365)
dataToDbList("1h", start_time=start_time)
time.sleep(10)
except Exception as e:
print(e)
time.sleep(60)
data1h()
def data15m():
try:
while True:
if not isSetDbConf():
print("数据库未设置!")
else:
start_time = beforeDate(365)
dataToDbList("15m", start_time=start_time)
time.sleep(10)
except Exception as e:
time.sleep(60)
data15m()
def data5m():
try:
while True:
if not isSetDbConf():
print("数据库未设置!")
else:
start_time = beforeDate(180)
dataToDbList("5m", start_time=start_time)
time.sleep(10)
except Exception as e:
time.sleep(60)
data5m()
def data1m():
try:
while True:
if not isSetDbConf():
print("数据库未设置!")
else:
start_time = beforeDate(30)
dataToDbList("1m", start_time=start_time)
time.sleep(10)
except Exception as e:
time.sleep(60)
data1m()
def longRun():
# 日线
d1_tt = threading.Thread(target=dataDay)
d1_tt = setDaemon(d1_tt)
d1_tt.start()
# 4h
h4_tt = threading.Thread(target=data4h)
h4_tt = setDaemon(h4_tt)
h4_tt.start()
# 1h
h1_tt = threading.Thread(target=data1h)
h1_tt = setDaemon(h1_tt)
h1_tt.start()
# 15m
m15_tt = threading.Thread(target=data15m)
m15_tt = setDaemon(m15_tt)
m15_tt.start()
# 5m
m5_tt = threading.Thread(target=data5m)
m5_tt = setDaemon(m5_tt)
m5_tt.start()
# 1m
# h1m_tt = threading.Thread(target=data1m)
# h1m_tt = setDaemon(h1m_tt)
# h1m_tt.start()
startTask()
def dataRun():
do_num = 0
while True:
if do_num > 1:
break
do_num = do_num + 1
dataRunToDb()
time.sleep(6)
if __name__ == "__main__":
func = sys.argv[1]
if func == 'run':
data5m()()
elif func == 'long':
longRun()
elif func == 'demo':
writeLog('111')
else:
print('error')
@@ -1,12 +0,0 @@
#!/bin/bash
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH
curPath=`pwd`
# bash plugins/cryptocurrency_trade/ccxt/public_data/data.sh
if [ -f ${curPath}/bin/activate ];then
source ${curPath}/bin/activate
fi
python3 plugins/cryptocurrency_trade/ccxt/public_data/data.py long

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