Compare commits

..
196 Commits
Author SHA1 Message Date
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
1492 changed files with 18111 additions and 185745 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: |
gunicorn -c setting.py app:app
python3 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: |
gunicorn -c setting.py app:app
python3 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: |
gunicorn -c setting.py app:app
python3 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: |
gunicorn -c setting.py app:app
python3 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.21.4.1
- name: Start DEBUG
run: |
gunicorn -c setting.py app:app
python3 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: |
gunicorn -c setting.py app:app
python3 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
+6 -74
View File
@@ -8,11 +8,13 @@ __pycache__/
# Distribution / packaging
.Python
build/
develop-eggs/
dist/
downloads/
eggs/
.eggs/
lib/
lib64/
parts/
sdist/
@@ -104,77 +106,7 @@ venv.bak/
.DS_Store
.idea/*.xml
.idea/*.iml
tmp/*
*.swp
*.pem
*.zip
logs/*
data/*.db
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
ssl/input.pl
lib/python*
bin
pyvenv.cfg
include
share
pip-selfcheck.json
scripts/init.d/mw
scripts/mdserver-web
data/api_login.txt
data/sessions
data/ssl.pl
data/port.pl
data/ipv6.pl
data/restart.pl
data/ssl.pl
data/edate.pl
data/osname.pl
data/only_netio_counters.pl
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
plugins/vip_*
plugins/own_*
plugins/my_*
plugins/op_auth
plugins/l2tp
plugins/openlitespeed
plugins/tamper_proof
plugins/cryptocurrency_trade
plugins/mtproxy
plugins/zimg
plugins/bk_demo
plugins/mail
plugins/fastdfs
plugins/v2ray
plugins/frp
plugins/file_search
plugins/proxysql
debug.out
mdioks.session-journal
*.session
tmp/*.pl
tmp/*.log
data/json/index.json
data/system.db
+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 -211
View File
@@ -1,213 +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>
# mdweb
简单的Linux面板
### 简介
简单的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)
[![](https://data.jsdelivr.com/v1/package/gh/midoks/mdserver-web/badge?style=for-the-badge)](https://www.jsdelivr.com/package/gh/midoks/mdserver-web)
* SSH终端工具
* 面板收藏功能
* 网站子目录绑定
* 网站备份功能
* 插件方式管理
基本上可以使用,后续会继续优化!欢迎提供意见!
- 吹水组 - https://t.me/mdserver_web
- 交流论坛 - https://bbs.midoks.icu
- discord - https://discord.gg/3zXk4mES
### 自动安装
```
如果出现问题,最好私给我面板信息。不要让我猜。如果不提供,不要提出问题,自行解决。 — 座右铭
Talk is cheap, show me the code. -- linus
```
- [兼容性测试报告](/compatibility.md)
- [常用命令说明](/cmd.md) [ mw default ] [ mw update_dev ]
### 主要插件介绍
* OpenResty - 轻量级,占有内存少,并发能力强。
* PHP[53-83] - PHP是世界上最好的编程语言。
* MySQL - 一种关系数据库管理系统。
* MariaDB - 是MySQL的一个重要分支。
* MySQL[APT/YUM] - 一种关系数据库管理系统。
* MongoDB - 一种非关系NOSQL数据库管理系统。
* phpMyAdmin - 著名Web端MySQL管理工具。
* Memcached - 一个高性能的分布式内存对象缓存系统。
* Redis - 一个高性能的KV数据库。
* PureFtpd - 一款专注于程序健壮和软件安全的免费FTP服务器软件。
* Gogs - 一款极易搭建的自助Git服务。
* Rsyncd - 通用同步服务。
### 插件开发相关
```
插件文档还不完善,如果有不明白的地方提Issue! 我会尽力完善。
如果你自己写了插件,想分享出来,也可以提Issue。
```
- 简单例子 - https://github.com/mw-plugin/simple-plugin
- 插件地址 - https://github.com/mw-plugin
- 开发文档 - https://github.com/midoks/mdserver-web/wiki/插件开发
## 其他插件
- OP鉴权 - https://github.com/mw-plugin/op_auth
# Note
```
phpMyAdmin[4.4.15]支持MySQL[5.5-5.7]
phpMyAdmin[5.2.0]支持MySQL[8.0]
PHP[53-72]支持phpMyAdmin[4.4.15]
PHP[72-83]支持phpMyAdmin[5.2.0]
```
# 郑重声明
不卖、不会监控(统计使用除外)、更不会注入病毒,大家使用方便(望大家不吝捐赠)。
- https://www.youtube.com/watch?v=2taa5K-Jmmw
# 特别赞助
- [找资源 - 阿里云盘资源搜索引擎](https://zhaoziyuan1.cc)
# AD - VPS推荐 - 🙏
- [ZZZ评测](https://www.zzzvps.com/)
| 服务商 | LOGO | 推广地址 | 优惠码 |
| ------------- |----------|-----------|-------|
| digitalvirt |[![digitalvirt](https://digitalvirt.com/templates/BlueWhite/img/logo-dark.svg)](https://digitalvirt.com/aff.php?aff=154) | https://digitalvirt.com/aff.php?aff=154 | mdserver-web |
# Docker
- 由[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.16.7
- mongodb副本设置/备份/认证。
### JSDelivr安装地址
- 初始安装
```
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 | bash
```
- 卸载脚本
```
wget --no-check-certificate -O uninstall.sh https://cdn.jsdelivr.net/gh/midoks/mdserver-web@latest/scripts/uninstall.sh && bash uninstall.sh
```
### 备用地址
- 初始安装
```
curl --insecure -fsSL https://raw.githubusercontent.com/midoks/mdserver-web/master/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 | bash
```
- 卸载脚本
```
wget --no-check-certificate -O uninstall.sh https://raw.githubusercontent.com/midoks/mdserver-web/master/scripts/uninstall.sh && bash uninstall.sh
```
### 通用软件安装[命令行安装]
- 需已经安装mdserver-web
```
curl --insecure -fsSL https://raw.githubusercontent.com/midoks/mdserver-web/dev/scripts/quick/app.sh | bash
```
### DEV使用
```
curl --insecure -fsSL https://raw.githubusercontent.com/midoks/mdserver-web/dev/scripts/install_dev.sh | bash
curl --insecure -fsSL https://raw.githubusercontent.com/midoks/mdserver-web/dev/scripts/update_dev.sh | bash
wget --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
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_dev.sh | bash
curl --insecure -fsSL https://code.midoks.icu/midoks/mdserver-web/raw/branch/dev/scripts/update_dev.sh | bash
```
### 捐赠地址 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
```
+41 -38
View File
@@ -1,52 +1,55 @@
# coding:utf-8
# ---------------------------------------------------------------------------------
# MW-Linux面板
# ---------------------------------------------------------------------------------
# copyright (c) 2018-∞(https://github.com/midoks/mdserver-web) All rights reserved.
# ---------------------------------------------------------------------------------
# Author: midoks <midoks@163.com>
# ---------------------------------------------------------------------------------
# ---------------------------------------------------------------------------------
# 入口文件
# ---------------------------------------------------------------------------------
# pip install profiler_online
# 性能测试
# from profiler_online import run_profiler
# run_profiler()
import sys
import io
import os
import time
sys.dont_write_bytecode = True
from route import app, socketio
from datetime import timedelta
sys.path.append("/class/core/")
reload(sys)
sys.setdefaultencoding('utf8')
import route
from flask import Flask
from gevent.pywsgi import WSGIServer
from geventwebsocket.handler import WebSocketHandler
app = Flask(__name__)
app.debug = True
from gevent import monkey
monkey.patch_all()
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"),
)
# print "time.time(): %f " % time.time()
app.config.version = "0.0.1" + str(time.time())
app.config['SECRET_KEY'] = os.urandom(24)
app.config['PERMANENT_SESSION_LIFETIME'] = timedelta(days=7)
def setting_modules(app, modules):
for module, url_prefix in modules:
app.register_blueprint(module, url_prefix=url_prefix)
setting_modules(app, DEFAULT_MODULES)
try:
if __name__ == "__main__":
PORT = 7200
if os.path.exists('data/port.pl'):
f = open('data/port.pl')
PORT = int(f.read())
f.close()
# HOST = '0.0.0.0'
# app.run(host=HOST, port=PORT)
http_server = WSGIServer(
(HOST, PORT), app, handler_class=WebSocketHandler)
http_server.serve_forever()
socketio.run(app, host=HOST, port=PORT)
app.run()
except Exception as ex:
print(ex)
print ex
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
-146
View File
@@ -1,146 +0,0 @@
# coding: utf-8
# ---------------------------------------------------------------------------------
# MW-Linux面板
# ---------------------------------------------------------------------------------
# copyright (c) 2018-∞(https://github.com/midoks/mdserver-web) All rights reserved.
# ---------------------------------------------------------------------------------
# Author: midoks <midoks@163.com>
# ---------------------------------------------------------------------------------
# ---------------------------------------------------------------------------------
# 公共操作
# ---------------------------------------------------------------------------------
import os
import sys
import time
import string
import json
import hashlib
import shlex
import datetime
import subprocess
import re
import hashlib
from random import Random
import mw
import db
from flask import redirect
def init():
initDB()
initUserInfo()
initInitD()
initInitTask()
return True
def local():
result = checkClose()
if result:
return result
# 检查面板是否关闭
def checkClose():
if os.path.exists('data/close.pl'):
return redirect('/close')
def initDB():
try:
sql = db.Sql().dbfile('default')
csql = mw.readFile('data/sql/default.sql')
csql_list = csql.split(';')
for index in range(len(csql_list)):
sql.execute(csql_list[index], ())
except Exception as ex:
print(str(ex))
def doContentReplace(src, dst):
content = mw.readFile(src)
content = content.replace("{$SERVER_PATH}", mw.getRunDir())
mw.writeFile(dst, content)
def initInitD():
# systemctl
# sysCfgDir = mw.systemdCfgDir()
# if os.path.exists(sysCfgDir) and mw.getOsName() == 'centos' and mw.getOsID() == '9':
# systemd_mw = sysCfgDir + '/mw.service'
# systemd_mw_task = sysCfgDir + '/mw-task.service'
# systemd_mw_tpl = mw.getRunDir() + '/scripts/init.d/mw.service.tpl'
# systemd_mw_task_tpl = mw.getRunDir() + '/scripts/init.d/mw-task.service.tpl'
# if os.path.exists(systemd_mw):
# os.remove(systemd_mw)
# if os.path.exists(systemd_mw_task):
# os.remove(systemd_mw_task)
# doContentReplace(systemd_mw_tpl, systemd_mw)
# doContentReplace(systemd_mw_task_tpl, systemd_mw_task)
# mw.execShell('systemctl enable mw')
# mw.execShell('systemctl enable mw-task')
# mw.execShell('systemctl daemon-reload')
script = mw.getRunDir() + '/scripts/init.d/mw.tpl'
script_bin = mw.getRunDir() + '/scripts/init.d/mw'
doContentReplace(script, script_bin)
mw.execShell('chmod +x ' + script_bin)
# 在linux系统中,确保/etc/init.d存在
if not mw.isAppleSystem() and not os.path.exists("/etc/rc.d/init.d"):
mw.execShell('mkdir -p /etc/rc.d/init.d')
if not mw.isAppleSystem() and not os.path.exists("/etc/init.d"):
mw.execShell('mkdir -p /etc/init.d')
# initd
if os.path.exists('/etc/rc.d/init.d'):
initd_bin = '/etc/rc.d/init.d/mw'
if not os.path.exists(initd_bin):
import shutil
shutil.copyfile(script_bin, initd_bin)
mw.execShell('chmod +x ' + initd_bin)
# 加入自启动
mw.execShell('which chkconfig && chkconfig --add mw')
if os.path.exists('/etc/init.d'):
initd_bin = '/etc/init.d/mw'
if not os.path.exists(initd_bin):
import shutil
shutil.copyfile(script_bin, initd_bin)
mw.execShell('chmod +x ' + initd_bin)
# 加入自启动
mw.execShell('which update-rc.d && update-rc.d -f mw defaults')
# 获取系统IPV4
mw.setHostAddr(mw.getLocalIp())
def initInitTask():
# 创建证书同步命令
import cert_api
api = cert_api.cert_api()
api.createCertCron()
def initUserInfo():
data = mw.M('users').where('id=?', (1,)).getField('password')
if data == '21232f297a57a5a743894a0e4a801fc3':
pwd = mw.getRandomString(8).lower()
file_pw = mw.getRunDir() + '/data/default.pl'
mw.writeFile(file_pw, pwd)
mw.M('users').where('id=?', (1,)).setField(
'password', mw.md5(pwd))
File diff suppressed because it is too large Load Diff
-690
View File
@@ -1,690 +0,0 @@
# coding: utf-8
# ---------------------------------------------------------------------------------
# MW-Linux面板
# ---------------------------------------------------------------------------------
# copyright (c) 2018-∞(https://github.com/midoks/mdserver-web) All rights reserved.
# ---------------------------------------------------------------------------------
# Author: midoks <midoks@163.com>
# ---------------------------------------------------------------------------------
# ---------------------------------------------------------------------------------
# 计划任务操作
# ---------------------------------------------------------------------------------
import psutil
import time
import os
import sys
import mw
import re
import json
import pwd
from flask import request
class crontab_api:
field = 'id,name,type,where1,where_hour,where_minute,echo,addtime,status,save,backup_to,stype,sname,sbody,urladdress'
def __init__(self):
pass
##### ----- start ----- ###
def listApi(self):
p = request.args.get('p', 1)
psize = 10
startPage = (int(p) - 1) * psize
pageInfo = str(startPage) + ',' + str(psize)
_list = mw.M('crontab').where('', ()).field(
self.field).limit(pageInfo).order('id desc').select()
data = []
for i in range(len(_list)):
tmp = _list[i]
if _list[i]['type'] == "day":
tmp['type'] = '每天'
tmp['cycle'] = mw.getInfo('每天, {1}点{2}分 执行', (str(
_list[i]['where_hour']), str(_list[i]['where_minute'])))
elif _list[i]['type'] == "day-n":
tmp['type'] = mw.getInfo(
'每{1}天', (str(_list[i]['where1']),))
tmp['cycle'] = mw.getInfo('每隔{1}天, {2}点{3}分 执行', (str(
_list[i]['where1']), str(_list[i]['where_hour']), str(_list[i]['where_minute'])))
elif _list[i]['type'] == "hour":
tmp['type'] = '每小时'
tmp['cycle'] = mw.getInfo(
'每小时, 第{1}分钟 执行', (str(_list[i]['where_minute']),))
elif _list[i]['type'] == "hour-n":
tmp['type'] = mw.getInfo(
'每{1}小时', (str(_list[i]['where1']),))
tmp['cycle'] = mw.getInfo('每{1}小时, 第{2}分钟 执行', (str(
_list[i]['where1']), str(_list[i]['where_minute'])))
elif _list[i]['type'] == "minute-n":
tmp['type'] = mw.getInfo(
'每{1}分钟', (str(_list[i]['where1']),))
tmp['cycle'] = mw.getInfo(
'每隔{1}分钟执行', (str(_list[i]['where1']),))
elif _list[i]['type'] == "week":
tmp['type'] = '每周'
if not _list[i]['where1']:
_list[i]['where1'] = '0'
tmp['cycle'] = mw.getInfo('每周{1}, {2}点{3}分执行', (self.toWeek(int(
_list[i]['where1'])), str(_list[i]['where_hour']), str(_list[i]['where_minute'])))
elif _list[i]['type'] == "month":
tmp['type'] = '每月'
tmp['cycle'] = mw.getInfo('每月, {1}日 {2}点{3}分执行', (str(_list[i]['where1']), str(
_list[i]['where_hour']), str(_list[i]['where_minute'])))
data.append(tmp)
rdata = {}
rdata['data'] = data
count = mw.M('crontab').where('', ()).count()
_page = {}
_page['count'] = count
_page['p'] = p
_page['row'] = psize
_page['tojs'] = "getCronData"
rdata['list'] = mw.getPage(_page)
rdata['p'] = p
# backup hook
bh_file = mw.getPanelDataDir() + "/hook_backup.json"
if os.path.exists(bh_file):
hb_data = mw.readFile(bh_file)
hb_data = json.loads(hb_data)
rdata['backup_hook'] = hb_data
return mw.getJson(rdata)
# 设置计划任务状态
def setCronStatusApi(self):
mid = request.form.get('id', '')
cronInfo = mw.M('crontab').where(
'id=?', (mid,)).field(self.field).find()
status = 1
if cronInfo['status'] == status:
status = 0
self.removeForCrond(cronInfo['echo'])
else:
cronInfo['status'] = 1
self.syncToCrond(cronInfo)
mw.M('crontab').where('id=?', (mid,)).setField('status', status)
mw.writeLog(
'计划任务', '修改计划任务[' + cronInfo['name'] + ']状态为[' + str(status) + ']')
return mw.returnJson(True, '设置成功')
# 获取指定任务数据
def getCrondFindApi(self):
sid = request.form.get('id', '')
data = mw.M('crontab').where(
'id=?', (sid,)).field(self.field).find()
return mw.getJson(data)
# 参数校验
def cronCheck(self, params):
if params['stype'] == 'site' or params['stype'] == 'database' or params['stype'].find('database_') > -1 or params['stype'] == 'logs' or params['stype'] == 'path':
if params['save'] == '':
return False, '保留份数不能为空!'
if params['type'] == 'day':
if params['hour'] == '':
return False, '小时不能为空!'
if params['minute'] == '':
return False, '分钟不能为空!'
if params['type'] == 'day-n':
if params['where1'] == '':
return False, '天不能为空!'
if params['hour'] == '':
return False, '小时不能为空!'
if params['minute'] == '':
return False, '分钟不能为空!'
if params['type'] == 'hour':
if params['minute'] == '':
return False, '分钟不能为空!'
if params['type'] == 'hour-n':
if params['where1'] == '':
return False, '小时不能为空!'
if params['minute'] == '':
return False, '分钟不能为空!'
if params['type'] == 'minute-n':
if params['where1'] == '':
return False, '分钟不能为空!'
if params['type'] == 'week':
if params['hour'] == '':
return False, '小时不能为空!'
if params['minute'] == '':
return False, '分钟不能为空!'
if params['type'] == 'month':
if params['where1'] == '':
return False, '日不能为空!'
if params['hour'] == '':
return False, '小时不能为空!'
if params['minute'] == '':
return False, '分钟不能为空!'
return True, 'OK'
def modifyCrondApi(self):
sid = request.form.get('id', '')
iname = request.form.get('name', '')
field_type = request.form.get('type', '')
week = request.form.get('week', '')
where1 = request.form.get('where1', '')
hour = request.form.get('hour', '')
minute = request.form.get('minute', '')
save = request.form.get('save', '')
backup_to = request.form.get('backup_to', '')
stype = request.form.get('stype', '')
sname = request.form.get('sname', '')
sbody = request.form.get('sbody', '')
urladdress = request.form.get('urladdress', '')
if len(iname) < 1:
return mw.returnJson(False, '任务名称不能为空!')
params = {
'name': iname,
'type': field_type,
'week': week,
'where1': where1,
'hour': hour,
'minute': minute,
'save': save,
'backup_to': backup_to,
'stype': stype,
'sname': sname,
'sbody': sbody,
'urladdress': urladdress,
}
is_check_pass, msg = self.cronCheck(params)
if not is_check_pass:
return mw.returnJson(is_check_pass, msg)
cuonConfig, get, name = self.getCrondCycle(params)
cronInfo = mw.M('crontab').where(
'id=?', (sid,)).field(self.field).find()
del(cronInfo['id'])
del(cronInfo['addtime'])
cronInfo['name'] = get['name']
cronInfo['type'] = get['type']
cronInfo['where1'] = get['where1']
cronInfo['where_hour'] = get['hour']
cronInfo['where_minute'] = get['minute']
cronInfo['save'] = get['save']
cronInfo['backup_to'] = get['backup_to']
cronInfo['sbody'] = get['sbody']
cronInfo['urladdress'] = get['urladdress']
addData = mw.M('crontab').where('id=?', (sid,)).save('name,type,where1,where_hour,where_minute,save,backup_to, sname, sbody,urladdress',
(iname, field_type, get['where1'], get['hour'], get['minute'], get['save'], get['backup_to'], sname, get['sbody'], get['urladdress']))
self.removeForCrond(cronInfo['echo'])
self.syncToCrond(cronInfo)
mw.writeLog('计划任务', '修改计划任务[' + cronInfo['name'] + ']成功')
return mw.returnJson(True, '修改成功')
def logsApi(self):
sid = request.form.get('id', '')
echo = mw.M('crontab').where("id=?", (sid,)).field('echo').find()
logFile = mw.getServerDir() + '/cron/' + echo['echo'] + '.log'
if not os.path.exists(logFile):
return mw.returnJson(False, '当前日志为空!')
log = mw.getLastLine(logFile, 500)
return mw.returnJson(True, log)
def addApi(self):
iname = request.form.get('name', '')
field_type = request.form.get('type', '')
week = request.form.get('week', '')
where1 = request.form.get('where1', '')
hour = request.form.get('hour', '')
minute = request.form.get('minute', '')
save = request.form.get('save', '')
backup_to = request.form.get('backupTo', '')
stype = request.form.get('sType', '')
sname = request.form.get('sName', '')
sbody = request.form.get('sBody', '')
urladdress = request.form.get('urladdress', '')
if len(iname) < 1:
return mw.returnJson(False, '任务名称不能为空!')
params = {
'name': iname,
'type': field_type,
'week': week,
'where1': where1,
'hour': hour,
'minute': minute,
'save': save,
'backup_to': backup_to,
'stype': stype,
'sname': sname,
'sbody': sbody,
'urladdress': urladdress,
}
is_check_pass, msg = self.cronCheck(params)
if not is_check_pass:
return mw.returnJson(is_check_pass, msg)
addData = self.add(params)
if type(addData) == str:
return addData
if addData > 0:
return mw.returnJson(True, '添加成功')
return mw.returnJson(False, '添加失败')
def add(self, params):
iname = params["name"]
field_type = params["type"]
week = params["week"]
where1 = params["where1"]
hour = params["hour"]
minute = params["minute"]
save = params["save"]
backup_to = params["backup_to"]
stype = params["stype"]
sname = params["sname"]
sbody = params["sbody"]
urladdress = params["urladdress"]
# print params
cronConfig, get, name = self.getCrondCycle(params)
cronPath = mw.getServerDir() + '/cron'
cronName = self.getShell(params)
if type(cronName) == dict:
return cronName
cronConfig += ' ' + cronPath + '/' + cronName + \
' >> ' + cronPath + '/' + cronName + '.log 2>&1'
# print(cronConfig)
if not mw.isAppleSystem():
wRes = self.writeShell(cronConfig)
if type(wRes) != bool:
return wRes
self.crondReload()
add_time = time.strftime('%Y-%m-%d %X', time.localtime())
task_id = mw.M('crontab').add('name,type,where1,where_hour,where_minute,echo,addtime,status,save,backup_to,stype,sname,sbody,urladdress',
(iname, field_type, where1, hour, minute, cronName, add_time, 1, save, backup_to, stype, sname, sbody, urladdress,))
return task_id
def startTaskApi(self):
sid = request.form.get('id', '')
echo = mw.M('crontab').where('id=?', (sid,)).getField('echo')
execstr = mw.getServerDir() + '/cron/' + echo
os.system('chmod +x ' + execstr)
os.system('nohup ' + execstr + ' >> ' + execstr + '.log 2>&1 &')
return mw.returnJson(True, '任务已执行!')
def delApi(self):
task_id = request.form.get('id', '')
try:
data = self.delete(task_id)
if not data[0]:
return mw.returnJson(False, data[1])
return mw.returnJson(True, '删除成功')
except Exception as e:
return mw.returnJson(False, '删除失败:' + str(e))
def delete(self, tid):
find = mw.M('crontab').where("id=?", (tid,)).field('name,echo').find()
if not self.removeForCrond(find['echo']):
return (False, '无法写入文件,请检查是否开启了系统加固功能!')
cronPath = mw.getServerDir() + '/cron'
sfile = cronPath + '/' + find['echo']
if os.path.exists(sfile):
os.remove(sfile)
sfile = cronPath + '/' + find['echo'] + '.log'
if os.path.exists(sfile):
os.remove(sfile)
mw.M('crontab').where("id=?", (tid,)).delete()
mw.writeLog('计划任务', mw.getInfo('删除计划任务[{1}]成功!', (find['name'],)))
return (True, "OK")
def delLogsApi(self):
sid = request.form.get('id', '')
try:
echo = mw.M('crontab').where("id=?", (sid,)).getField('echo')
logFile = mw.getServerDir() + '/cron/' + echo + '.log'
os.remove(logFile)
return mw.returnJson(True, '任务日志已清空!')
except:
return mw.returnJson(False, '任务日志清空失败!')
# 取数据列表
def getDataListApi(self):
stype = request.form.get('type', '')
bak_data = []
if stype == 'site' or stype == 'sites' or stype == 'database' or stype.find('database_') > -1 or stype == 'path':
hookPath = mw.getPanelDataDir() + "/hook_backup.json"
if os.path.exists(hookPath):
t = mw.readFile(hookPath)
bak_data = json.loads(t)
if stype == 'database' or stype.find('database_') > -1:
sqlite3_name = 'mysql'
path = mw.getServerDir() + '/mysql'
if stype != 'database':
soft_name = stype.replace('database_', '')
path = mw.getServerDir() + '/' + soft_name
if soft_name == 'postgresql':
sqlite3_name = 'pgsql'
if soft_name == 'mongodb':
sqlite3_name = 'mongodb'
db_list = {}
db_list['orderOpt'] = bak_data
if not os.path.exists(path + '/' + sqlite3_name + '.db'):
db_list['data'] = []
else:
db_list['data'] = mw.M('databases').dbPos(path, sqlite3_name).field('name,ps').select()
return mw.getJson(db_list)
if stype == 'path':
db_list = {}
db_list['data'] = [{"name": mw.getWwwDir(), "ps": "www"}]
db_list['orderOpt'] = bak_data
return mw.getJson(db_list)
data = {}
data['orderOpt'] = bak_data
default_db = 'sites'
# if stype == 'site' or stype == 'logs':
# stype == 'sites'
data['data'] = mw.M(default_db).field('name,ps').select()
return mw.getJson(data)
##### ----- start ----- ###
# 转换大写星期
def toWeek(self, num):
wheres = {
0: '日',
1: '一',
2: '二',
3: '三',
4: '四',
5: '五',
6: '六'
}
try:
return wheres[num]
except:
return ''
def getCrondCycle(self, params):
cuonConfig = ''
name = ''
if params['type'] == "day":
cuonConfig = self.getDay(params)
name = '每天'
elif params['type'] == "day-n":
cuonConfig = self.getDay_N(params)
name = mw.getInfo('每{1}天', (params['where1'],))
elif params['type'] == "hour":
cuonConfig = self.getHour(params)
name = '每小时'
elif params['type'] == "hour-n":
cuonConfig = self.getHour_N(params)
name = '每小时'
elif params['type'] == "minute-n":
cuonConfig = self.minute_N(params)
elif params['type'] == "week":
params['where1'] = params['week']
cuonConfig = self.week(params)
elif params['type'] == "month":
cuonConfig = self.month(params)
return cuonConfig, params, name
# 取任务构造Day
def getDay(self, param):
cuonConfig = "{0} {1} * * * ".format(param['minute'], param['hour'])
return cuonConfig
# 取任务构造Day_n
def getDay_N(self, param):
cuonConfig = "{0} {1} */{2} * * ".format(
param['minute'], param['hour'], param['where1'])
return cuonConfig
# 取任务构造Hour
def getHour(self, param):
cuonConfig = "{0} * * * * ".format(param['minute'])
return cuonConfig
# 取任务构造Hour-N
def getHour_N(self, param):
cuonConfig = "{0} */{1} * * * ".format(
param['minute'], param['where1'])
return cuonConfig
# 取任务构造Minute-N
def minute_N(self, param):
cuonConfig = "*/{0} * * * * ".format(param['where1'])
return cuonConfig
# 取任务构造week
def week(self, param):
cuonConfig = "{0} {1} * * {2}".format(
param['minute'], param['hour'], param['week'])
return cuonConfig
# 取任务构造Month
def month(self, param):
cuonConfig = "{0} {1} {2} * * ".format(
param['minute'], param['hour'], param['where1'])
return cuonConfig
# 取执行脚本
def getShell(self, param):
# try:
stype = param['stype']
if stype == 'toFile':
shell = param.sFile
else:
head = "#!/bin/bash\nPATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin\nexport PATH\n"
start_head = '''
SCRIPT_RUN_TIME="0s"
MW_ToSeconds()
{
SEC=$1
if [ $SEC -lt 60 ]; then
SCRIPT_RUN_TIME="${SEC}s"
elif [ $SEC -ge 60 ] && [ $SEC -lt 3600 ];then
SCRIPT_RUN_TIME="$(( SEC / 60 ))m$(( SEC % 60 ))s"
elif [ $SEC -ge 3600 ]; then
SCRIPT_RUN_TIME="$(( SEC / 3600 ))h$(( (SEC % 3600) / 60 ))m$(( (SEC % 3600) % 60 ))s"
fi
}
START_MW_SHELL_TIME=`date +%s`
'''
source_bin_activate = '''
export LANG=en_US.UTF-8
MW_PATH=%s/bin/activate
if [ -f $MW_PATH ];then
source $MW_PATH
fi''' % (mw.getRunDir(),)
head = head + start_head + source_bin_activate + "\n"
log = '.log'
script_dir = mw.getRunDir() + "/scripts"
source_stype = 'database'
if stype.find('database_') > -1:
plugin_name = stype.replace('database_', '')
script_dir = mw.getRunDir() + "/plugins/" + plugin_name + "/scripts"
source_stype = stype
stype = 'database'
wheres = {
'path': head + "python3 " + script_dir + "/backup.py path " + param['sname'] + " " + str(param['save']),
'site': head + "python3 " + script_dir + "/backup.py site " + param['sname'] + " " + str(param['save']),
'database': head + "python3 " + script_dir + "/backup.py database " + param['sname'] + " " + str(param['save']),
'logs': head + "python3 " + script_dir + "/logs_backup.py " + param['sname'] + log + " " + str(param['save']),
'rememory': head + "/bin/bash " + script_dir + '/rememory.sh'
}
if param['backup_to'] != 'localhost':
cfile = mw.getPluginDir() + "/" + \
param['backup_to'] + "/index.py"
wheres['path'] = head + "python3 " + cfile + \
" path " + param['sname'] + " " + str(param['save'])
wheres['site'] = head + "python3 " + cfile + \
" site " + param['sname'] + " " + str(param['save'])
wheres['database'] = head + "python3 " + cfile + " " + \
source_stype + " " + \
param['sname'] + " " + str(param['save'])
try:
shell = wheres[stype]
except:
if stype == 'toUrl':
shell = head + "curl -sS --connect-timeout 10 -m 60 '" + \
param['urladdress'] + "'"
else:
shell = head + param['sbody'].replace("\r\n", "\n")
shell += '''
echo "----------------------------------------------------------------------------"
endDate=`date +"%Y-%m-%d %H:%M:%S"`
END_MW_SHELL_TIME=`date +"%s"`
((SHELL_COS_TIME=($END_MW_SHELL_TIME-$START_MW_SHELL_TIME)))
MW_ToSeconds $SHELL_COS_TIME
echo "★[$endDate] Successful | Script Run [$SCRIPT_RUN_TIME] "
echo "----------------------------------------------------------------------------"
'''
cronPath = mw.getServerDir() + '/cron'
if not os.path.exists(cronPath):
mw.execShell('mkdir -p ' + cronPath)
if not 'echo' in param:
cronName = mw.md5(mw.md5(str(time.time()) + '_mw'))
else:
cronName = param['echo']
file = cronPath + '/' + cronName
mw.writeFile(file, self.checkScript(shell))
mw.execShell('chmod 750 ' + file)
return cronName
# 检查脚本
def checkScript(self, shell):
keys = ['shutdown', 'init 0', 'mkfs', 'passwd',
'chpasswd', '--stdin', 'mkfs.ext', 'mke2fs']
for key in keys:
shell = shell.replace(key, '[***]')
return shell
# 将Shell脚本写到文件
def writeShell(self, config):
file = '/var/spool/cron/crontabs/root'
current_os = mw.getOs()
if current_os == 'darwin':
file = '/etc/crontab'
elif current_os.startswith("freebsd"):
file = '/var/cron/tabs/root'
if not os.path.exists(file):
file = '/var/spool/cron/root'
if not os.path.exists(file):
mw.writeFile(file, '')
conf = mw.readFile(file)
conf += str(config) + "\n"
if mw.writeFile(file, conf):
if not os.path.exists(file):
mw.execShell("chmod 600 '" + file +
"' && chown root.root " + file)
else:
mw.execShell("chmod 600 '" + file +
"' && chown root.crontab " + file)
return True
return mw.returnJson(False, '文件写入失败,请检查是否开启系统加固功能!')
# 重载配置
def crondReload(self):
if mw.isAppleSystem():
# mw.execShell('/usr/sbin/cron restart')
if os.path.exists('/etc/crontab'):
pass
else:
if os.path.exists('/etc/init.d/crond'):
mw.execShell('/etc/init.d/crond reload')
elif os.path.exists('/etc/init.d/cron'):
mw.execShell('service cron restart')
else:
mw.execShell("systemctl reload crond")
# 从crond删除
def removeForCrond(self, echo):
u_file = '/var/spool/cron/crontabs/root'
if not os.path.exists(u_file):
file = '/var/spool/cron/root'
if mw.isAppleSystem():
file = '/etc/crontab'
if not os.path.exists(file):
return False
else:
file = u_file
if mw.isAppleSystem():
return True
conf = mw.readFile(file)
rep = ".+" + str(echo) + ".+\n"
conf = re.sub(rep, "", conf)
if not mw.writeFile(file, conf):
return False
self.crondReload()
return True
def syncToCrond(self, cronInfo):
if 'status' in cronInfo:
if cronInfo['status'] == 0:
return False
if 'where_hour' in cronInfo:
cronInfo['hour'] = cronInfo['where_hour']
cronInfo['minute'] = cronInfo['where_minute']
cronInfo['week'] = cronInfo['where1']
cuonConfig, cronInfo, name = self.getCrondCycle(cronInfo)
cronPath = mw.getServerDir() + '/cron'
cronName = self.getShell(cronInfo)
if type(cronName) == dict:
return cronName
cuonConfig += ' ' + cronPath + '/' + cronName + \
' >> ' + cronPath + '/' + cronName + '.log 2>&1'
wRes = self.writeShell(cuonConfig)
if type(wRes) != bool:
return False
self.crondReload()
+41 -161
View File
@@ -1,21 +1,8 @@
# coding: utf-8
# ---------------------------------------------------------------------------------
# MW-Linux面板
# ---------------------------------------------------------------------------------
# copyright (c) 2018-∞(https://github.com/midoks/mdserver-web) All rights reserved.
# ---------------------------------------------------------------------------------
# Author: midoks <midoks@163.com>
# ---------------------------------------------------------------------------------
# ---------------------------------------------------------------------------------
# sqlite3操作
# ---------------------------------------------------------------------------------
import sqlite3
import os
import sys
class Sql():
@@ -24,84 +11,50 @@ class Sql():
#------------------------------
__DB_FILE = None # 数据库文件
__DB_CONN = None # 数据库连接对象
__DB_TABLE = "" # 被操作的表名称
__OPT_WHERE = "" # where条件
__OPT_LIMIT = "" # limit条件
__OPT_GROUP = "" # group条件
__OPT_ORDER = "" # order条件
__OPT_FIELD = "*" # field条件
__OPT_PARAM = () # where值
__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):
def __GetConn(self):
# 取数据库对象
try:
if self.__DB_CONN == None:
self.__DB_CONN = sqlite3.connect(self.__DB_FILE)
self.__DB_CONN.text_factory = str
except Exception as ex:
except Exception, ex:
return "error: " + str(ex)
def autoTextFactory(self):
if sys.version_info[0] == 3:
self.__DB_CONN.text_factory = lambda x: str(
x, encoding="utf-8", errors='ignore')
else:
self.__DB_CONN.text_factory = lambda x: unicode(
x, "utf-8", "ignore")
def dbfile(self, name):
self.__DB_FILE = 'data/' + name + '.db'
return self
def dbPos(self, path, name):
self.__DB_FILE = path + '/' + name + '.db'
return self
def table(self, table):
# 设置表名
self.__DB_TABLE = table
return self
def where(self, where, param=()):
def where(self, where, param):
# WHERE条件
if where:
self.__OPT_WHERE = " WHERE " + where
self.__OPT_PARAM = param
return self
def andWhere(self, where, param):
# WHERE条件
if where:
self.__OPT_WHERE = self.__OPT_WHERE + " and " + where
# print(param)
# print(self.__OPT_PARAM)
self.__OPT_PARAM = self.__OPT_PARAM + param
return self
def order(self, order):
# ORDER条件
if len(order):
self.__OPT_ORDER = " ORDER BY " + order
else:
self.__OPT_ORDER = ""
return self
def group(self, group):
if len(group):
self.__OPT_GROUP = " GROUP BY " + group
else:
self.__OPT_GROUP = ""
return self
def limit(self, limit):
# LIMIT条件
if len(limit):
self.__OPT_LIMIT = " LIMIT " + limit
else:
self.__OPT_LIMIT = ""
return self
def field(self, field):
@@ -112,12 +65,10 @@ class Sql():
def select(self):
# 查询数据集
self.__getConn()
self.__GetConn()
try:
sql = "SELECT " + self.__OPT_FIELD + " FROM " + self.__DB_TABLE + \
self.__OPT_WHERE + self.__OPT_GROUP + self.__OPT_ORDER + self.__OPT_LIMIT
# print(sql)
# print(self.__OPT_PARAM)
self.__OPT_WHERE + self.__OPT_ORDER + self.__OPT_LIMIT
result = self.__DB_CONN.execute(sql, self.__OPT_PARAM)
data = result.fetchall()
# 构造字曲系列
@@ -141,53 +92,12 @@ class Sql():
del(tmp)
self.__close()
return data
except Exception as ex:
return "error: " + str(ex)
def inquiry(self, input_field=''):
# 查询数据集
# 不清空查询参数
self.__getConn()
try:
sql = "SELECT " + self.__OPT_FIELD + " FROM " + self.__DB_TABLE + \
self.__OPT_WHERE + self.__OPT_GROUP + self.__OPT_ORDER + self.__OPT_LIMIT
if os.path.exists('data/debug.pl'):
print(sql, self.__OPT_PARAM)
result = self.__DB_CONN.execute(sql, self.__OPT_PARAM)
data = result.fetchall()
# 构造字曲系列
if self.__OPT_FIELD != "*":
if input_field != "":
field = input_field.split(',')
else:
field = self.__OPT_FIELD.split(',')
tmp = []
for row in data:
i = 0
tmp1 = {}
for key in field:
tmp1[key] = row[i]
i += 1
tmp.append(tmp1)
del(tmp1)
data = tmp
del(tmp)
else:
# 将元组转换成列表
tmp = map(list, data)
data = tmp
del(tmp)
return data
except Exception as ex:
except Exception, ex:
return "error: " + str(ex)
def getField(self, keyName):
# 取回指定字段
result = self.field(keyName).select()
# print(result)
if len(result) == 1:
return result[0][keyName]
return result
@@ -214,7 +124,8 @@ class Sql():
def add(self, keys, param):
# 插入数据
self.__getConn()
self.__GetConn()
self.__DB_CONN.text_factory = str
try:
values = ""
for key in keys.split(','):
@@ -223,36 +134,13 @@ class Sql():
sql = "INSERT INTO " + self.__DB_TABLE + \
"(" + keys + ") " + "VALUES(" + values + ")"
result = self.__DB_CONN.execute(sql, param)
last_id = result.lastrowid
id = result.lastrowid
self.__close()
self.__DB_CONN.commit()
return last_id
except Exception as ex:
return id
except Exception, ex:
return "error: " + str(ex)
# 插入数据
def insert(self, pdata):
if not pdata:
return False
keys, param = self.__format_pdata(pdata)
return self.add(keys, param)
# 更新数据
def update(self, pdata):
if not pdata:
return False
keys, param = self.__format_pdata(pdata)
return self.save(keys, param)
# 构造数据
def __format_pdata(self, pdata):
keys = pdata.keys()
keys_str = ','.join(keys)
param = []
for k in keys:
param.append(pdata[k])
return keys_str, tuple(param)
def checkInput(self, data):
if not data:
return data
@@ -273,7 +161,8 @@ class Sql():
def addAll(self, keys, param):
# 插入数据
self.__getConn()
self.__GetConn()
self.__DB_CONN.text_factory = str
try:
values = ""
for key in keys.split(','):
@@ -283,7 +172,7 @@ class Sql():
"(" + keys + ") " + "VALUES(" + values + ")"
result = self.__DB_CONN.execute(sql, param)
return True
except Exception as ex:
except Exception, ex:
return "error: " + str(ex)
def commit(self):
@@ -292,7 +181,8 @@ class Sql():
def save(self, keys, param):
# 更新数据
self.__getConn()
self.__GetConn()
self.__DB_CONN.text_factory = str
try:
opt = ""
for key in keys.split(','):
@@ -300,8 +190,8 @@ class Sql():
opt = opt[0:len(opt) - 1]
sql = "UPDATE " + self.__DB_TABLE + " SET " + opt + self.__OPT_WHERE
# import mw
# mw.writeFile('/tmp/test.pl', sql)
import public
public.writeFile('/tmp/test.pl', sql)
# 处理拼接WHERE与UPDATE参数
tmp = list(param)
@@ -312,12 +202,12 @@ class Sql():
self.__close()
self.__DB_CONN.commit()
return result.rowcount
except Exception as ex:
except Exception, ex:
return "error: " + str(ex)
def delete(self, id=None):
# 删除数据
self.__getConn()
self.__GetConn()
try:
if id:
self.__OPT_WHERE = " WHERE id=?"
@@ -327,54 +217,44 @@ class Sql():
self.__close()
self.__DB_CONN.commit()
return result.rowcount
except Exception as ex:
except Exception, ex:
return "error: " + str(ex)
def originExecute(self, sql, param=()):
self.__getConn()
try:
result = self.__DB_CONN.execute(sql, param)
self.__DB_CONN.commit()
return result
except Exception as ex:
return "error: " + str(ex)
def execute(self, sql, param=()):
def execute(self, sql, param):
# 执行SQL语句返回受影响行
self.__getConn()
# print sql, param
self.__GetConn()
try:
result = self.__DB_CONN.execute(sql, param)
self.__DB_CONN.commit()
return result.rowcount
except Exception as ex:
except Exception, ex:
return "error: " + str(ex)
def query(self, sql, param=()):
def query(self, sql, param):
# 执行SQL语句返回数据集
self.__getConn()
self.__GetConn()
try:
result = self.__DB_CONN.execute(sql, param)
# 将元组转换成列表
# data = map(list, result)
return result
except Exception as ex:
data = map(list, result)
return data
except Exception, ex:
return "error: " + str(ex)
def create(self, name):
# 创建数据表
self.__getConn()
import mw
script = mw.readFile('data/' + name + '.sql')
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 mw
script = mw.readFile(filename)
self.__GetConn()
import public
script = public.readFile(filename)
result = self.__DB_CONN.executescript(script)
self.__DB_CONN.commit()
return result.rowcount
File diff suppressed because it is too large Load Diff
-513
View File
@@ -1,513 +0,0 @@
# coding: utf-8
# ---------------------------------------------------------------------------------
# MW-Linux面板
# ---------------------------------------------------------------------------------
# copyright (c) 2018-∞(https://github.com/midoks/mdserver-web) All rights reserved.
# ---------------------------------------------------------------------------------
# Author: midoks <midoks@163.com>
# ---------------------------------------------------------------------------------
# ---------------------------------------------------------------------------------
# 防火墙操作
# ---------------------------------------------------------------------------------
import psutil
import time
import os
import sys
import mw
import re
import json
import pwd
from flask import request
class firewall_api:
__isFirewalld = False
__isIptables = False
__isUfw = False
__isMac = False
def __init__(self):
iptables_file = mw.systemdCfgDir() + '/iptables.service'
if os.path.exists('/usr/sbin/firewalld'):
self.__isFirewalld = True
elif os.path.exists('/usr/sbin/ufw'):
self.__isUfw = True
elif os.path.exists(iptables_file):
self.__isIptables = True
elif mw.isAppleSystem():
self.__isMac = True
##### ----- start ----- ###
# 添加屏蔽IP
def addDropAddressApi(self):
import re
port = request.form.get('port', '').strip()
ps = request.form.get('ps', '').strip()
rep = "^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}(\/\d{1,2})?$"
if not re.search(rep, port):
return mw.returnJson(False, '您输入的IP地址不合法!')
address = port
if mw.M('firewall').where("port=?", (address,)).count() > 0:
return mw.returnJson(False, '您要放屏蔽的IP已存在屏蔽列表,无需重复处理!')
if self.__isUfw:
mw.execShell('ufw deny from ' + address + ' to any')
else:
if self.__isIptables:
cmd = 'iptables -I INPUT -s ' + address + ' -j DROP'
mw.execShell(cmd)
elif self.__isFirewalld:
cmd = 'firewall-cmd --permanent --add-rich-rule=\'rule family=ipv4 source address="' + \
address + '" drop\''
mw.execShell(cmd)
else:
pass
msg = mw.getInfo('屏蔽IP[{1}]成功!', (address,))
mw.writeLog("防火墙管理", msg)
addtime = time.strftime('%Y-%m-%d %X', time.localtime())
mw.M('firewall').add('port,ps,addtime', (address, ps, addtime))
self.firewallReload()
return mw.returnJson(True, '添加成功!')
# 添加放行端口
def addAcceptPortApi(self):
if not self.getFwStatus():
return mw.returnJson(False, '防火墙启动时,才能添加规则!')
port = request.form.get('port', '').strip()
ps = request.form.get('ps', '').strip()
stype = request.form.get('type', '').strip()
protocol = request.form.get('protocol', '').strip()
data = self.addAcceptPortArgs(port, ps, stype, protocol)
return mw.getJson(data)
# 添加放行端口
def addAcceptPortArgs(self, port, ps, stype, protocol='tcp'):
import re
import time
if not self.getFwStatus():
self.setFw(0)
rep = "^\d{1,5}(:\d{1,5})?$"
if not re.search(rep, port):
return mw.returnData(False, '端口范围不正确!')
if mw.M('firewall').where("port=?", (port,)).count() > 0:
return mw.returnData(False, '您要放行的端口已存在,无需重复放行!')
msg = mw.getInfo('放行端口[{1}][{2}]成功', (port, protocol,))
mw.writeLog("防火墙管理", msg)
addtime = time.strftime('%Y-%m-%d %X', time.localtime())
mw.M('firewall').add('port,protocol,ps,addtime',
(port, protocol, ps, addtime))
self.addAcceptPort(port, protocol)
self.firewallReload()
return mw.returnData(True, '添加放行(' + port + ')端口成功!')
# 删除IP屏蔽
def delDropAddressApi(self):
if not self.getFwStatus():
return mw.returnJson(False, '防火墙启动时,才能删除规则!')
port = request.form.get('port', '').strip()
ps = request.form.get('ps', '').strip()
sid = request.form.get('id', '').strip()
address = port
if self.__isUfw:
mw.execShell('ufw delete deny from ' + address + ' to any')
elif self.__isFirewalld:
mw.execShell(
'firewall-cmd --permanent --remove-rich-rule=\'rule family=ipv4 source address="' + address + '" drop\'')
elif self.__isIptables:
cmd = 'iptables -D INPUT -s ' + address + ' -j DROP'
mw.execShell(cmd)
else:
pass
msg = mw.getInfo('解除IP[{1}]的屏蔽!', (address,))
mw.writeLog("防火墙管理", msg)
mw.M('firewall').where("id=?", (sid,)).delete()
self.firewallReload()
return mw.returnJson(True, '删除成功!')
def delAcceptPortArgs(self, port, protocol='tcp'):
if self.__isUfw:
if protocol == 'tcp':
mw.execShell('ufw delete allow ' + port + '/tcp')
if protocol == 'udp':
mw.execShell('ufw delete allow ' + port + '/udp')
if protocol == 'tcp/udp':
mw.execShell('ufw delete allow ' + port + '/tcp')
mw.execShell('ufw delete allow ' + port + '/udp')
elif self.__isFirewalld:
port = port.replace(':', '-')
if protocol == 'tcp':
mw.execShell(
'firewall-cmd --permanent --zone=public --remove-port=' + port + '/tcp')
if protocol == 'udp':
mw.execShell(
'firewall-cmd --permanent --zone=public --remove-port=' + port + '/udp')
if protocol == 'tcp/udp':
mw.execShell(
'firewall-cmd --permanent --zone=public --remove-port=' + port + '/tcp')
mw.execShell(
'firewall-cmd --permanent --zone=public --remove-port=' + port + '/udp')
elif self.__isIptables:
if protocol == 'tcp':
mw.execShell(
'iptables -D INPUT -p tcp -m state --state NEW -m tcp --dport ' + port + ' -j ACCEPT')
if protocol == 'udp':
mw.execShell(
'iptables -D INPUT -p udp -m state --state NEW -m udp --dport ' + port + ' -j ACCEPT')
if protocol == 'tcp/udp':
mw.execShell(
'iptables -D INPUT -p tcp -m state --state NEW -m tcp --dport ' + port + ' -j ACCEPT')
mw.execShell(
'iptables -D INPUT -p udp -m state --state NEW -m udp --dport ' + port + ' -j ACCEPT')
else:
pass
mw.M('firewall').where("port=?", (port,)).delete()
msg = mw.getInfo('删除防火墙放行端口[{1}][{2}]成功!', (port, protocol,))
mw.writeLog("防火墙管理", msg)
self.firewallReload()
return True
# 删除放行端口
def delAcceptPortApi(self):
port = request.form.get('port', '').strip()
protocol = request.form.get('protocol', 'tcp').strip()
sid = request.form.get('id', '').strip()
mw_port = mw.readFile('data/port.pl')
if(port == mw_port):
return mw.returnJson(False, '失败,不能删除当前面板端口!')
try:
self.delAcceptPortArgs(port, protocol)
mw.M('firewall').where("id=?", (sid,)).delete()
return mw.returnJson(True, '删除成功!')
except Exception as e:
return mw.returnJson(False, '删除失败!:' + str(e))
def getWwwPathApi(self):
path = mw.getLogsDir()
return mw.getJson({'path': path})
def getListApi(self):
p = request.form.get('p', '1').strip()
limit = request.form.get('limit', '10').strip()
return self.getList(int(p), int(limit))
def getSshInfoApi(self):
data = {}
file = '/etc/ssh/sshd_config'
conf = mw.readFile(file)
rep = "#*Port\s+([0-9]+)\s*\n"
port = re.search(rep, conf).groups(0)[0]
isPing = True
try:
if mw.isAppleSystem():
isPing = True
else:
file = '/etc/sysctl.conf'
sys_conf = mw.readFile(file)
rep = "#*net\.ipv4\.icmp_echo_ignore_all\s*=\s*([0-9]+)"
tmp = re.search(rep, sys_conf).groups(0)[0]
if tmp == '1':
isPing = False
except:
isPing = True
# sshd 检测
status = True
cmd = "service sshd status | grep -P '(dead|stop)'|grep -v grep"
ssh_status = mw.execShell(cmd)
if ssh_status[0] != '':
status = False
cmd = "systemctl status sshd.service | grep 'dead'|grep -v grep"
ssh_status = mw.execShell(cmd)
if ssh_status[0] != '':
status = False
data['pass_prohibit_status'] = False
# 密码登陆配置检查
pass_rep = "PasswordAuthentication\s+(\w*)\s*\n"
pass_status = re.search(pass_rep, conf)
if pass_status:
if pass_status and pass_status.groups(0)[0].strip() == 'no':
data['pass_prohibit_status'] = True
else:
data['pass_prohibit_status'] = True
data['port'] = port
data['status'] = status
data['ping'] = isPing
if mw.isAppleSystem():
data['firewall_status'] = False
else:
data['firewall_status'] = self.getFwStatus()
return mw.getJson(data)
def setSshPortApi(self):
port = request.form.get('port', '1').strip()
if int(port) < 22 or int(port) > 65535:
return mw.returnJson(False, '端口范围必需在22-65535之间!')
ports = ['21', '25', '80', '443', '888']
if port in ports:
return mw.returnJson(False, '(' + port + ')' + '特殊端口不可设置!')
file = '/etc/ssh/sshd_config'
conf = mw.readFile(file)
rep = "#*Port\s+([0-9]+)\s*\n"
conf = re.sub(rep, "Port " + port + "\n", conf)
mw.writeFile(file, conf)
self.addAcceptPortArgs(port, 'SSH端口修改', 'port')
if self.__isUfw:
mw.execShell("service ssh restart")
elif self.__isIptables:
mw.execShell("/etc/init.d/sshd restart")
elif self.__isFirewalld:
mw.execShell("systemctl restart sshd.service")
else:
return mw.returnJson(False, '修改失败!')
return mw.returnJson(True, '修改成功!')
def setSshStatusApi(self):
if mw.isAppleSystem():
return mw.returnJson(True, '开发机不能操作!')
status = request.form.get('status', '1').strip()
msg = 'SSH服务已启用'
act = 'start'
if status == "1":
msg = 'SSH服务已停用'
act = 'stop'
ssh_service = mw.systemdCfgDir() + '/sshd.service'
if os.path.exists(ssh_service):
mw.execShell("systemctl " + act + " sshd.service")
else:
mw.execShell('service sshd ' + act)
if os.path.exists('/etc/init.d/sshd'):
mw.execShell('/etc/init.d/sshd ' + act)
mw.writeLog("防火墙管理", msg)
return mw.returnJson(True, '操作成功!')
def setSshPassStatusApi(self):
if mw.isAppleSystem():
return mw.returnJson(True, '开发机不能操作!')
status = request.form.get('status', '1').strip()
msg = '禁止密码登陆成功'
if status == "1":
msg = '开启密码登陆成功'
file = '/etc/ssh/sshd_config'
if not os.path.exists(file):
return mw.returnJson(False, '无法设置!')
conf = mw.readFile(file)
pass_rep = "PasswordAuthentication\s+(\w*)\s*\n"
pass_status = re.search(pass_rep, conf)
if not pass_status:
rep = "(#)?PasswordAuthentication\s+(\w*)\s*\n"
conf = re.sub(rep, "PasswordAuthentication yes\n", conf)
if status == '1':
rep = "PasswordAuthentication\s+(\w*)\s*\n"
conf = re.sub(rep, "PasswordAuthentication yes\n", conf)
else:
rep = "PasswordAuthentication\s+(\w*)\s*\n"
conf = re.sub(rep, "PasswordAuthentication no\n", conf)
mw.writeFile(file, conf)
mw.execShell("systemctl restart sshd.service")
mw.writeLog("SSH管理", msg)
return mw.returnJson(True, msg)
def setPingApi(self):
if mw.isAppleSystem():
return mw.returnJson(True, '开发机不能操作!')
status = request.form.get('status')
filename = '/etc/sysctl.conf'
conf = mw.readFile(filename)
if conf.find('net.ipv4.icmp_echo') != -1:
rep = u"net\.ipv4\.icmp_echo.*"
conf = re.sub(rep, 'net.ipv4.icmp_echo_ignore_all=' + status, conf)
else:
conf += "\nnet.ipv4.icmp_echo_ignore_all=" + status
mw.writeFile(filename, conf)
mw.execShell('sysctl -p')
return mw.returnJson(True, '设置成功!')
def setFwApi(self):
if mw.isAppleSystem():
return mw.returnJson(True, '开发机不能设置!')
status = request.form.get('status', '1')
return mw.getJson(self.setFw(status))
def setFwIptables(self, status):
# iptables特殊处理
if status == '1':
mw.execShell('service iptables save')
mw.execShell('service iptables stop')
else:
# 重新导入数据
_list = mw.M('firewall').field('id,port,ps,addtime').limit(
'0,1000').order('id desc').select()
mw.execShell('iptables -P INPUT DROP')
mw.execShell('iptables -P OUTPUT ACCEPT')
for x in _list:
port = x['port']
if mw.isIpAddr(port):
cmd = 'iptables -I INPUT -s ' + port + ' -j DROP'
mw.execShell(cmd)
else:
self.addAcceptPort(port)
mw.execShell('service iptables save')
mw.execShell('service iptables start')
def setFw(self, status):
if self.__isIptables:
self.setFwIptables(status)
return mw.returnData(True, '设置成功!')
if status == '1':
if self.__isUfw:
mw.execShell('/usr/sbin/ufw disable')
elif self.__isFirewalld:
mw.execShell('systemctl stop firewalld.service')
mw.execShell('systemctl disable firewalld.service')
else:
pass
else:
if self.__isUfw:
mw.execShell("echo 'y'| ufw enable")
elif self.__isFirewalld:
mw.execShell('systemctl start firewalld.service')
mw.execShell('systemctl enable firewalld.service')
else:
pass
return mw.returnData(True, '设置成功!')
##### ----- start ----- ###
def getList(self, page, limit):
start = (page - 1) * limit
_list = mw.M('firewall').field('id,port,protocol,ps,addtime').limit(
str(start) + ',' + str(limit)).order('id desc').select()
data = {}
data['data'] = _list
count = mw.M('firewall').count()
_page = {}
_page['count'] = count
_page['tojs'] = 'showAccept'
_page['p'] = page
data['page'] = mw.getPage(_page)
return mw.getJson(data)
def addAcceptPort(self, port, protocol='tcp'):
if self.__isUfw:
if protocol == 'tcp':
mw.execShell('ufw allow ' + port + '/tcp')
if protocol == 'udp':
mw.execShell('ufw allow ' + port + '/udp')
if protocol == 'tcp/udp':
mw.execShell('ufw allow ' + port + '/tcp')
mw.execShell('ufw allow ' + port + '/udp')
elif self.__isFirewalld:
port = port.replace(':', '-')
if protocol == 'tcp':
cmd = 'firewall-cmd --permanent --zone=public --add-port=' + port + '/tcp'
mw.execShell(cmd)
if protocol == 'udp':
cmd = 'firewall-cmd --permanent --zone=public --add-port=' + port + '/udp'
mw.execShell(cmd)
if protocol == 'tcp/udp':
cmd = 'firewall-cmd --permanent --zone=public --add-port=' + port + '/tcp'
mw.execShell(cmd)
cmd = 'firewall-cmd --permanent --zone=public --add-port=' + port + '/udp'
mw.execShell(cmd)
elif self.__isIptables:
if protocol == 'tcp':
cmd = 'iptables -I INPUT -p tcp -m state --state NEW -m tcp --dport ' + port + ' -j ACCEPT'
mw.execShell(cmd)
if protocol == 'udp':
cmd = 'iptables -I INPUT -p udp -m state --state NEW -m udp --dport ' + port + ' -j ACCEPT'
mw.execShell(cmd)
if protocol == 'tcp/udp':
cmd = 'iptables -I INPUT -p tcp -m state --state NEW -m tcp --dport ' + port + ' -j ACCEPT'
mw.execShell(cmd)
cmd = 'iptables -I INPUT -p udp -m state --state NEW -m udp --dport ' + port + ' -j ACCEPT'
mw.execShell(cmd)
else:
pass
return True
def firewallReload(self):
if self.__isUfw:
mw.execShell('/usr/sbin/ufw reload')
return
elif self.__isIptables:
mw.execShell('service iptables save')
mw.execShell('service iptables restart')
elif self.__isFirewalld:
mw.execShell('firewall-cmd --reload')
else:
pass
def getFwStatus(self):
if self.__isUfw:
cmd = "/usr/sbin/ufw status| grep Status | awk -F ':' '{print $2}'"
data = mw.execShell(cmd)
if data[0].strip() == 'inactive':
return False
return True
elif self.__isIptables:
cmd = "systemctl status iptables | grep 'inactive'"
data = mw.execShell(cmd)
if data[0] != '':
return False
return True
elif self.__isFirewalld:
cmd = "ps -ef|grep firewalld |grep -v grep | awk '{print $2}'"
data = mw.execShell(cmd)
if data[0] == '':
return False
return True
else:
return False
-354
View File
@@ -1,354 +0,0 @@
# coding: utf-8
# ---------------------------------------------------------------------------------
# MW-Linux面板
# ---------------------------------------------------------------------------------
# copyright (c) 2018-∞(https://github.com/midoks/mdserver-web) All rights reserved.
# ---------------------------------------------------------------------------------
# Author: midoks <midoks@163.com>
# ---------------------------------------------------------------------------------
# ---------------------------------------------------------------------------------
# 日志类操作
# ---------------------------------------------------------------------------------
import psutil
import time
import os
import sys
import mw
import re
import json
import pwd
from datetime import datetime
from flask import request
class logs_api:
__months = {'Jan': '01', 'Feb': '02', 'Mar': '03', 'Apr': '04', 'May': '05', 'Jun': '06',
'Jul': '07', 'Aug': '08', 'Sep': '09', 'Sept': '09', 'Oct': '10', 'Nov': '11', 'Dec': '12'}
def __init__(self):
pass
def delPanelLogsApi(self):
mw.M('logs').where('id>?', (0,)).delete()
mw.writeLog('面板设置', '面板操作日志已清空!')
return mw.returnJson(True, '面板操作日志已清空!')
def getLogListApi(self):
p = request.form.get('p', '1').strip()
limit = request.form.get('limit', '10').strip()
search = request.form.get('search', '').strip()
return self.getLogList(int(p), int(limit), search)
def getLogList(self, page, limit, search=''):
find_search = ''
if search != '':
find_search = "type like '%" + search + "%' or log like '%" + \
search + "%' or addtime like '%" + search + "%'"
start = (page - 1) * limit
_list = mw.M('logs').where(find_search, ()).field(
'id,type,log,addtime').limit(str(start) + ',' + str(limit)).order('id desc').select()
data = {}
data['data'] = _list
count = mw.M('logs').where(find_search, ()).count()
_page = {}
_page['count'] = count
_page['tojs'] = 'getLogs'
_page['p'] = page
data['page'] = mw.getPage(_page)
return mw.getJson(data)
def getLogsTitle(self, log_name):
log_name = log_name.replace('.1', '')
if log_name in ['mw-update.log']:
return '面板更新日志'
if log_name in ['mw-install.log']:
return '面板安装日志'
if log_name in ['auth.log', 'secure'] or log_name.find('auth.') == 0:
return '授权日志'
if log_name in ['dmesg'] or log_name.find('dmesg') == 0:
return '内核缓冲区日志'
if log_name in ['syslog'] or log_name.find('syslog') == 0:
return '系统日志'
if log_name in ['rsyncd.log']:
return '远程同步日志'
if log_name in ['btmp']:
return '失败的登录记录'
if log_name in ['utmp', 'wtmp']:
return '登录和重启记录'
if log_name in ['lastlog']:
return '用户最后登录'
if log_name in ['yum.log']:
return 'yum包管理器日志'
if log_name in ['anaconda.log']:
return 'Anaconda日志'
if log_name in ['dpkg.log']:
return 'dpkg日志'
if log_name in ['daemon.log']:
return '系统后台守护进程日志'
if log_name in ['boot.log']:
return '启动日志'
if log_name in ['kern.log']:
return '内核日志'
if log_name in ['maillog', 'mail.log']:
return '邮件日志'
if log_name.find('Xorg') == 0:
return 'Xorg日志'
if log_name in ['cron.log']:
return '定时任务日志'
if log_name in ['alternatives.log']:
return '更新替代信息'
if log_name in ['debug']:
return '调试信息'
if log_name.find('apt') == 0:
return 'apt-get相关日志'
if log_name.find('installer') == 0:
return '系统安装相关日志'
if log_name in ['messages']:
return '综合日志'
return '{}日志'.format(log_name.split('.')[0])
def getAuditLogsFilesApi(self):
log_dir = '/var/log'
log_files = []
for log_file in os.listdir(log_dir):
log_suffix = log_file.split('.')[-1:]
if log_suffix[0] in ['gz', 'xz', 'bz2', 'asl']:
continue
if log_file in ['.', '..', 'faillog', 'fontconfig.log', 'unattended-upgrades', 'tallylog']:
continue
filename = os.path.join(log_dir, log_file)
if os.path.isfile(filename):
file_size = os.path.getsize(filename)
if not file_size:
continue
tmp = {
'name': log_file,
'size': file_size,
'log_file': filename,
'title': self.getLogsTitle(log_file),
'uptime': os.path.getmtime(filename)
}
log_files.append(tmp)
else:
for next_name in os.listdir(filename):
if next_name[-3:] in ['.gz', '.xz']:
continue
next_file = os.path.join(filename, next_name)
if not os.path.isfile(next_file):
continue
file_size = os.path.getsize(next_file)
if not file_size:
continue
log_name = '{}/{}'.format(log_file, next_name)
tmp = {
'name': log_name,
'size': file_size,
'log_file': next_file,
'title': self.getLogsTitle(log_name),
'uptime': os.path.getmtime(next_file)
}
log_files.append(tmp)
log_files = sorted(log_files, key=lambda x: x['name'], reverse=True)
return mw.getJson(log_files)
def getAuditFileApi(self):
log_name = request.form.get('log_name', '').strip()
return self.getAuditLogsName(log_name)
def __to_date2(self, date_str):
tmp = date_str.split()
s_date = str(tmp[-1]) + '-' + self.__months.get(tmp[1],
tmp[1]) + '-' + tmp[2] + ' ' + tmp[3]
return s_date
def __to_date3(self, date_str):
tmp = date_str.split()
s_date = str(datetime.now().year) + '-' + \
self.__months.get(tmp[1], tmp[1]) + '-' + tmp[2] + ' ' + tmp[3]
return s_date
def __to_date4(self, date_str):
tmp = date_str.split()
s_date = str(datetime.now().year) + '-' + \
self.__months.get(tmp[0], tmp[0]) + '-' + tmp[1] + ' ' + tmp[2]
return s_date
def getAuditLast(self, log_name):
# 获取日志
cmd = '''LANG=en_US.UTF-8 last -n 200 -x -f {} |grep -v 127.0.0.1|grep -v " begins"'''.format(
'/var/log/' + log_name)
result = mw.execShell(cmd)
lastlog_list = []
for _line in result[0].split("\n"):
if not _line:
continue
tmp = {}
sp_arr = _line.split()
tmp['用户'] = sp_arr[0]
if sp_arr[0] == 'runlevel':
tmp['来源'] = sp_arr[4]
tmp['端口'] = ' '.join(sp_arr[1:4])
tmp['时间'] = self.__to_date3(
' '.join(sp_arr[5:])) + ' ' + ' '.join(sp_arr[-2:])
elif sp_arr[0] in ['reboot', 'shutdown']:
tmp['来源'] = sp_arr[3]
tmp['端口'] = ' '.join(sp_arr[1:3])
if sp_arr[-3] == '-':
tmp['时间'] = self.__to_date3(
' '.join(sp_arr[4:])) + ' ' + ' '.join(sp_arr[-3:])
else:
tmp['时间'] = self.__to_date3(
' '.join(sp_arr[4:])) + ' ' + ' '.join(sp_arr[-2:])
elif sp_arr[1] in ['tty1', 'tty', 'tty2', 'tty3', 'hvc0', 'hvc1', 'hvc2'] or len(sp_arr) == 9:
tmp['来源'] = ''
tmp['端口'] = sp_arr[1]
tmp['时间'] = self.__to_date3(
' '.join(sp_arr[2:])) + ' ' + ' '.join(sp_arr[-3:])
else:
tmp['来源'] = sp_arr[2]
tmp['端口'] = sp_arr[1]
tmp['时间'] = self.__to_date3(
' '.join(sp_arr[3:])) + ' ' + ' '.join(sp_arr[-3:])
# tmp['_line'] = _line
lastlog_list.append(tmp)
# lastlog_list = sorted(lastlog_list,key=lambda x:x['时间'],reverse=True)
return mw.returnData(True, 'ok!', lastlog_list)
def getAuditLastLog(self):
cmd = '''LANG=en_US.UTF-8 lastlog|grep -v Username'''
result = mw.execShell(cmd)
lastlog_list = []
for _line in result[0].split("\n"):
if not _line:
continue
tmp = {}
sp_arr = _line.split()
tmp['用户'] = sp_arr[0]
# tmp['_line'] = _line
if _line.find('Never logged in') != -1:
tmp['最后登录时间'] = '0'
tmp['最后登录来源'] = '-'
tmp['最后登录端口'] = '-'
lastlog_list.append(tmp)
continue
tmp['最后登录来源'] = sp_arr[2]
tmp['最后登录端口'] = sp_arr[1]
tmp['最后登录时间'] = self.__to_date2(' '.join(sp_arr[3:]))
lastlog_list.append(tmp)
lastlog_list = sorted(lastlog_list, key=lambda x: x[
'最后登录时间'], reverse=True)
for i in range(len(lastlog_list)):
if lastlog_list[i]['最后登录时间'] == '0':
lastlog_list[i]['最后登录时间'] = '从未登录过'
return mw.returnData(True, 'ok!', lastlog_list)
def parseAuditFile(self, log_name, result):
log_list = []
is_string = True
for _line in result.split("\n"):
if not _line.strip():
continue
if log_name.find('sa/sa') == -1:
if _line[:3] in self.__months:
_msg = _line[16:]
_tmp = _msg.split(": ")
_act = ''
if len(_tmp) > 1:
_act = _tmp[0]
_msg = _tmp[1]
else:
_msg = _tmp[0]
_line = {
"时间": self.__to_date4(_line[:16].strip()),
"角色": _act,
"事件": _msg
}
is_string = False
elif _line[:2] in ['19', '20', '21', '22', '23', '24']:
_msg = _line[19:]
_tmp = _msg.split(" ")
_act = _tmp[1]
_msg = ' '.join(_tmp[2:])
_line = {
"时间": _line[:19].strip(),
"角色": _act,
"事件": _msg
}
is_string = False
elif log_name.find('alternatives') == 0:
_tmp = _line.split(": ")
_last = _tmp[0].split(" ")
_act = _last[0]
_msg = ' '.join(_tmp[1:])
_line = {
"时间": ' '.join(_last[1:]).strip(),
"角色": _act,
"事件": _msg
}
is_string = False
else:
if not is_string:
if type(_line) != dict:
continue
log_list.append(_line)
return log_list
def getAuditLogsName(self, log_name):
# print(log_name)
if log_name in ['wtmp', 'btmp', 'utmp'] or log_name.find('wtmp') == 0 or log_name.find('btmp') == 0 or log_name.find('utmp') == 0:
return self.getAuditLast(log_name)
if log_name.find('lastlog') == 0:
return self.getAuditLastLog()
if log_name.find('sa/sa') == 0:
if log_name.find('sa/sar') == -1:
return mw.execShell("sar -f /var/log/{}".format(log_name))[0]
log_dir = '/var/log'
log_file = log_dir + '/' + log_name
if not os.path.exists(log_file):
return mw.returnData(False, '日志文件不存在!')
result = mw.getLastLine(log_file, 100)
try:
log_list = self.parseAuditFile(log_name, result)
_string = []
_dict = []
_list = []
for _line in log_list:
if isinstance(_line, str):
_string.append(_line.strip())
elif isinstance(_line, dict):
_dict.append(_line)
elif isinstance(_line, list):
_list.append(_line)
else:
continue
_str_len = len(_string)
_dict_len = len(_dict)
_list_len = len(_list)
if _str_len > _dict_len + _list_len:
return "\n".join(_string)
elif _dict_len > _str_len + _list_len:
return mw.returnData(True, 'ok!', _dict)
else:
return mw.returnData(True, 'ok!', _list)
except:
return mw.returnData(True, 'ok!', result)
-2212
View File
File diff suppressed because it is too large Load Diff
+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)
+291
View File
@@ -0,0 +1,291 @@
# coding: utf-8
import psutil
import time
import os
import public
import re
import json
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):
pass
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['path'])
# pluginInfo['status'] = os.path.exists(pluginInfo['install_checks'])
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'):
ret = {}
ret['type'] = json.loads(public.readFile(self.__type))
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 getPluginList(self, sType, sPage=1, sPageSize=15):
ret = {}
ret['type'] = json.loads(public.readFile(self.__type))
plugins_info = self.getAllList(sType)
args = {}
args['count'] = len(plugins_info)
args['p1'] = sPage
ret['data'] = plugins_info
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
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):
pass
File diff suppressed because it is too large Load Diff
+733
View File
@@ -0,0 +1,733 @@
# 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 getOs():
os = execShell('uname')
return os[0].lower().strip()
def M(table):
sql = db.Sql()
return sql.table(table)
def getPage(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'] = 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)
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=()):
# 取通用字曲返回
logMessage = json.loads(
readFile('static/language/' + getLanguage() + '/public.json'))
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 serviceReload():
# 重载Web服务配置
if os.path.exists('/www/server/nginx/sbin/nginx'):
result = execShell('/etc/init.d/nginx reload')
if result[1].find('nginx.pid') != -1:
execShell('pkill -9 nginx && sleep 1')
execShell('/etc/init.d/nginx start')
else:
result = execShell('/etc/init.d/httpd reload')
return result
def phpReload(version):
# 重载PHP配置
import os
if os.path.exists('/www/server/php/' + version + '/libphp5.so'):
execShell('/etc/init.d/httpd reload')
else:
execShell('/etc/init.d/php-fpm-' + version + ' reload')
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:
pass
# try:
# url = web.ctx.session.home + '/Api/getIpAddress'
# opener = urllib2.urlopen(url)
# return opener.read()
# except:
# return web.ctx.host.split(':')[0]
def inArray(arrays, searchStr):
# 搜索数据中是否存在
for key in arrays:
if key == searchStr:
return True
return False
def checkWebConfig():
# 检查Web服务器配置文件是否有错误
if get_webserver() == 'nginx':
result = ExecShell(
"ulimit -n 10240 && /www/server/nginx/sbin/nginx -t -c /www/server/nginx/conf/nginx.conf")
searchStr = 'successful'
else:
result = ExecShell(
"ulimit -n 10240 && /www/server/apache/bin/apachectl -t")
searchStr = 'Syntax OK'
if result[1].find(searchStr) == -1:
WriteLog("TYPE_SOFT", 'CONF_CHECK_ERR', (result[1],))
return result[1]
return True
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 hasPwd(password):
# 加密密码字符
import crypt
return crypt.crypt(password, password)
def get_url(timeout=0.5):
import json
try:
nodeFile = '/www/server/panel/data/node.json'
node_list = json.loads(readFile(nodeFile))
mnode = None
for node in node_list:
node['ping'] = get_timeout(
node['protocol'] + node['address'] + ':' + node['port'] + '/check.txt')
if not node['ping']:
continue
if not mnode:
mnode = node
if node['ping'] < mnode['ping']:
mnode = node
return mnode['protocol'] + mnode['address'] + ':' + mnode['port']
except:
return 'http://download.bt.cn'
def get_timeout(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 get_webserver():
# 获取Web服务器
webserver = 'nginx'
if not os.path.exists('/www/server/nginx/sbin/nginx'):
webserver = 'apache'
return webserver
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 to_size(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 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
File diff suppressed because it is too large Load Diff
-456
View File
@@ -1,456 +0,0 @@
# coding: utf-8
# ---------------------------------------------------------------------------------
# MW-Linux面板
# ---------------------------------------------------------------------------------
# copyright (c) 2018-∞(https://github.com/midoks/mdserver-web) All rights reserved.
# ---------------------------------------------------------------------------------
# Author: midoks <midoks@163.com>
# ---------------------------------------------------------------------------------
# ---------------------------------------------------------------------------------
# SSH终端操作
# ---------------------------------------------------------------------------------
import json
import time
import os
import sys
import socket
import threading
import re
from io import BytesIO, StringIO
import mw
import paramiko
from flask_socketio import SocketIO, emit, send
class ssh_terminal:
__debug_file = 'logs/terminal.log'
__log_type = 'SSH终端'
# websocketio 唯一标识
__sid = ''
__host = None
__type = '0'
__port = 22
__user = None
__pass = None
__pkey = None
__key_passwd = None
__rep_ssh_config = False
__rep_ssh_service = False
__sshd_config_backup = None
__ssh = None
__tp = None
__ps = None
__ssh_list = {}
__ssh_last_request_time = {}
def __init__(self):
ht = threading.Thread(target=self.heartbeat)
ht.start()
def debug(self, msg):
msg = "{} - {}:{} => {} \n".format(mw.formatDate(),
self.__host, self.__port, msg)
if not mw.isDebugMode():
return
mw.writeFile(self.__debug_file, msg, 'a+')
def returnMsg(self, status, msg):
return {'status': status, 'msg': msg}
def restartSsh(self, act='reload'):
'''
重启ssh 无参数传递
'''
version = mw.readFile('/etc/redhat-release')
if not os.path.exists('/etc/redhat-release'):
mw.execShell('service ssh ' + act)
elif version.find(' 7.') != -1 or version.find(' 8.') != -1:
mw.execShell("systemctl " + act + " sshd.service")
else:
mw.execShell("/etc/init.d/sshd " + act)
def isRunning(self, rep=False):
try:
if rep and self.__rep_ssh_service:
self.restartSsh('stop')
return True
status = self.getSshStatus()
if not status:
self.restartSsh('start')
self.__rep_ssh_service = True
return True
return False
except:
return False
def setSshdConfig(self, rep=False):
self.isRunning(rep)
if rep and not self.__rep_ssh_config:
return False
try:
sshd_config_file = '/etc/ssh/sshd_config'
if not os.path.exists(sshd_config_file):
return False
sshd_config = mw.readFile(sshd_config_file)
if not sshd_config:
return False
if rep:
if self.__sshd_config_backup:
mw.writeFile(sshd_config_file, self.__sshd_config_backup)
self.restartSsh()
return True
pin = r'^\s*PubkeyAuthentication\s+(yes|no)'
pubkey_status = re.findall(pin, sshd_config, re.I)
if pubkey_status:
if pubkey_status[0] == 'yes':
pubkey_status = True
else:
pubkey_status = False
pin = r'^\s*RSAAuthentication\s+(yes|no)'
rsa_status = re.findall(pin, sshd_config, re.I)
if rsa_status:
if rsa_status[0] == 'yes':
rsa_status = True
else:
rsa_status = False
self._sshd_config_backup = sshd_config
is_write = False
if not pubkey_status:
sshd_config = re.sub(
r'\n#?PubkeyAuthentication\s\w+', '\nPubkeyAuthentication yes', sshd_config)
is_write = True
if not rsa_status:
sshd_config = re.sub(
r'\n#?RSAAuthentication\s\w+', '\nRSAAuthentication yes', sshd_config)
is_write = True
if is_write:
mw.writeFile(sshd_config_file, sshd_config)
self.__rep_ssh_config = True
self.restartSsh()
else:
self.__sshd_config_backup = None
return True
except:
return False
def setSid(self, sid):
self.__sid = sid
def connect(self, sid):
# self.connectBySocket()
if self.__host in ['127.0.0.1', 'localhost']:
return self.connectLocalSsh(sid)
else:
return self.connectBySocket(sid)
def connectLocalSsh(self, sid):
mw.createSshInfo()
self.__ps = paramiko.SSHClient()
self.__ps.set_missing_host_key_policy(paramiko.AutoAddPolicy())
self.__port = mw.getSSHPort()
try:
self.__ps.connect(self.__host, self.__port, timeout=60)
except Exception as e:
self.__ps.connect('127.0.0.1', self.__port)
except Exception as e:
self.__ps.connect('localhost', self.__port)
except Exception as e:
self.setSshdConfig(True)
self.__ps.close()
e = str(e)
if e.find('websocket error!') != -1:
return self.returnMsg(True, '连接成功')
if e.find('Authentication timeout') != -1:
self.debug("认证超时{}".format(e))
return self.returnMsg(False, '认证超时,请按回车重试!{}'.format(e))
if e.find('Connection reset by peer') != -1:
self.debug('目标服务器主动拒绝连接')
return self.returnMsg(False, '目标服务器主动拒绝连接')
if e.find('Error reading SSH protocol banner') != -1:
self.debug('协议头响应超时')
return self.returnMsg(False, '协议头响应超时,与目标服务器之间的网络质量太糟糕:' + e)
if not e:
self.debug('SSH协议握手超时')
return self.returnMsg(False, "SSH协议握手超时,与目标服务器之间的网络质量太糟糕")
err = mw.getTracebackInfo()
self.debug(err)
return self.returnMsg(False, "未知错误: {}".format(err))
self.debug('local-ssh:认证成功,正在构建会话通道')
ssh = self.__ps.invoke_shell(
term='xterm', width=83, height=21)
ssh.setblocking(0)
self.__ssh_list[sid] = ssh
mw.writeLog(self.__log_type, '成功登录到SSH服务器 [{}:{}]'.format(
self.__host, self.__port))
self.debug('local-ssh:通道已构建')
return self.returnMsg(True, '连接成功!')
def connectBySocket(self, sid):
if not self.__host:
return self.returnMsg(False, '错误的连接地址')
if not self.__user:
self.__user = 'root'
if not self.__port:
self.__port = 22
self.setSshdConfig(True)
num = 0
while num < 5:
num += 1
try:
self.debug('正在尝试第{}次连接'.format(num))
if self.__rep_ssh_config:
time.sleep(0.1)
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.settimeout(2 + num)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_SNDBUF, 8192)
sock.connect((self.__host, self.__port))
break
except Exception as e:
if num == 5:
self.setSshdConfig(True)
self.debug('重试连接失败,{}'.format(e))
return self.returnMsg(False, '连接目标服务器失败, {}:{}'.format(self.__host, self.__port))
else:
time.sleep(0.2)
# print(self.__host, sock)
self.__tp = paramiko.Transport(sock)
try:
self.__tp.start_client()
self.__tp.banner_timeout = 60
if not self.__pass and not self.__pkey:
return self.returnMsg(False, '密码或私钥不能都为空: {}:{}'.format(self.__host, self.__port))
if self.__pkey != '' and self.__type != '0':
self.debug('正在认证私钥')
p_file = StringIO(str(self.__pkey.replace('\\n', '\n')))
# p_file = "/tmp/t_ssh_pkey.txt"
# mw.writeFile(p_file, self.__pkey.replace('\\n', '\n'))
# mw.execShell('chmod 600 ' + p_file)
try:
p_file.seek(0)
pkey = paramiko.RSAKey.from_private_key(p_file)
except:
try:
p_file.seek(0) # 重置游标
pkey = paramiko.Ed25519Key.from_private_key(
p_file)
except:
try:
p_file.seek(0)
pkey = paramiko.ECDSAKey.from_private_key(p_file)
except:
p_file.seek(0)
pkey = paramiko.DSSKey.from_private_key(p_file)
self.__tp.auth_publickey(username=self.__user, key=pkey)
else:
try:
self.__tp.auth_none(self.__user)
except Exception as e:
e = str(e)
if e.find('keyboard-interactive') >= 0:
self._auth_interactive()
else:
self.debug('正在认证密码')
self.__tp.auth_password(
username=self.__user, password=self.__pass)
except Exception as e:
self.setSshdConfig(True)
self.__tp.close()
e = str(e)
# print(e)
if e.find('Authentication timeout') != -1:
self.debug("认证超时{}".format(e))
return self.returnMsg(False, '认证超时,请按回车重试!{}'.format(e))
if e.find('Authentication failed') != -1:
self.debug('认证失败{}'.format(e))
return self.returnMsg(False, '帐号或密码错误: {}'.format(e + "," + self.__user + "@" + self.__host + ":" + str(self.__port)))
if e.find('Bad authentication type; allowed types') != -1:
self.debug('认证失败{}'.format(e))
if self.__host in ['127.0.0.1', 'localhost'] and self.__pass == 'none':
return self.returnMsg(False, '帐号或密码错误: {}'.format("Authentication failed ," + self.__user + "@" + self.__host + ":" + str(self.__port)))
return self.returnMsg(False, '不支持的身份验证类型: {}'.format(e))
if e.find('Connection reset by peer') != -1:
self.debug('目标服务器主动拒绝连接')
return self.returnMsg(False, '目标服务器主动拒绝连接')
if e.find('Error reading SSH protocol banner') != -1:
self.debug('协议头响应超时')
return self.returnMsg(False, '协议头响应超时,与目标服务器之间的网络质量太糟糕:' + e)
if not e:
self.debug('SSH协议握手超时')
return self.returnMsg(False, "SSH协议握手超时,与目标服务器之间的网络质量太糟糕")
err = mw.getTracebackInfo()
self.debug(err)
return self.returnMsg(False, "未知错误: {}".format(err))
self.debug('认证成功,正在构建会话通道')
ssh = self.__tp.open_session()
ssh.get_pty(term='xterm', width=100, height=34)
ssh.invoke_shell()
self.__ssh_list[sid] = ssh
mw.writeLog(self.__log_type, '成功登录到SSH服务器 [{}:{}]'.format(
self.__host, self.__port))
self.debug('通道已构建')
return self.returnMsg(True, '连接成功.')
def getSshInfo(self, file):
rdata = mw.readFile(file)
destr = mw.enDoubleCrypt('mdserver-web', rdata)
return json.loads(destr)
def setAttr(self, sid, info):
self.__host = info['host'].strip()
# 外部连接获取
if not self.__host in ['127.0.0.1', 'localhost']:
dst_info = mw.getServerDir() + '/webssh/host/' + self.__host + '/info.json'
if os.path.exists(dst_info):
info = self.getSshInfo(dst_info)
if 'type' in info:
self.__type = info['type']
if 'port' in info:
self.__port = int(info['port'])
if 'username' in info:
self.__user = info['username']
if 'pkey' in info:
self.__pkey = info['pkey']
if 'password' in info:
self.__pass = info['password']
if 'pkey_passwd' in info:
self.__key_passwd = info['pkey_passwd']
# print(self.__host, self.__pass, self.__key_passwd)
try:
result = self.connect(sid)
# print(result)
except Exception as ex:
if str(ex).find("NoneType") == -1:
raise ex
return result
def send(self):
pass
def close(self):
try:
if self.__ssh:
self.__ssh.close()
if self.__tp: # 关闭宿主服务
self.__tp.close()
if self.__ps:
self.__ps.close()
except:
pass
def resize(self, sid, data):
try:
self.__ssh_list[sid].resize_pty(
width=data['cols'], height=data['rows'])
return True
except:
return False
def wsSend(self, recv):
try:
t = recv.decode("utf-8")
return emit('server_response', {'data': t})
except Exception as e:
return emit('server_response', {'data': recv})
def wsSendConnect(self):
return emit('connect', {'data': 'ok'})
def wsSendReConnect(self):
return emit('reconnect', {'data': 'ok'})
def heartbeat(self):
# limit_cos = 10
while True:
time.sleep(3)
cur_time = time.time()
for x in list(self.__ssh_list.keys()):
ssh_last_time = self.__ssh_last_request_time[x]
sid_off_cos = cur_time - ssh_last_time
# print("heartbeat off cos :", x, sid_off_cos)
if sid_off_cos > 3:
cur_ssh = self.__ssh_list[x]
if not cur_ssh:
del(self.__ssh_list[x])
del(self.__ssh_last_request_time[x])
continue
if cur_ssh.exit_status_ready():
del(self.__ssh_list[x])
del(self.__ssh_last_request_time[x])
continue
cur_ssh.send("exit\r\n")
del(self.__ssh_list[x])
del(self.__ssh_last_request_time[x])
def run(self, sid, info):
self.__sid = sid
if not self.__sid:
return self.wsSend('WebSocketIO无效')
self.__ssh_last_request_time[sid] = time.time()
if not sid in self.__ssh_list:
if type(info) == dict and 'host' in info:
result = self.setAttr(sid, info)
if result['status']:
return self.wsSendConnect()
else:
return self.wsSend(result['msg'])
else:
return self.wsSendReConnect()
result = self.returnMsg(False, '')
if sid in self.__ssh_list:
if 'resize' in info:
self.resize(sid, info)
result = self.returnMsg(True, '已连接')
if result['status']:
if type(info) == str:
time.sleep(0.1)
cur_ssh = self.__ssh_list[sid]
if cur_ssh.exit_status_ready():
self.wsSend("logout\r\n")
del(self.__ssh_list[sid])
return
cur_ssh.send(info)
try:
time.sleep(0.005)
recv = cur_ssh.recv(8192)
return self.wsSend(recv)
except Exception as ex:
return self.wsSend('')
else:
return self.wsSend(result['msg'])
+158 -441
View File
@@ -1,44 +1,16 @@
# coding: utf-8
# ---------------------------------------------------------------------------------
# MW-Linux面板
# ---------------------------------------------------------------------------------
# copyright (c) 2018-∞(https://github.com/midoks/mdserver-web) All rights reserved.
# ---------------------------------------------------------------------------------
# Author: midoks <midoks@163.com>
# ---------------------------------------------------------------------------------
# ---------------------------------------------------------------------------------
# 系统信息操作
# ---------------------------------------------------------------------------------
import psutil
import time
import os
import re
import math
import sys
import json
from flask import Flask, session
from flask import request
import db
import mw
import config_api
import requests
from threading import Thread
from time import sleep
def mw_async(f):
def wrapper(*args, **kwargs):
thr = Thread(target=f, args=args, kwargs=kwargs)
thr.start()
return wrapper
import public
class system_api:
@@ -46,93 +18,9 @@ class system_api:
pids = None
def __init__(self):
self.setupPath = mw.getServerDir()
self.setupPath = '/www/server'
##### ----- start ----- ###
def networkApi(self):
data = self.getNetWork()
return mw.getJson(data)
def updateServerApi(self):
stype = request.args.get('type', 'check')
version = request.args.get('version', '')
return self.updateServer(stype, version)
def systemTotalApi(self):
data = self.getSystemTotal()
return mw.getJson(data)
def diskInfoApi(self):
diskInfo = self.getDiskInfo()
return mw.getJson(diskInfo)
def setControlApi(self):
stype = request.form.get('type', '')
day = request.form.get('day', '')
data = self.setControl(stype, day)
return data
def getLoadAverageApi(self):
start = request.args.get('start', '')
end = request.args.get('end', '')
data = self.getLoadAverageData(start, end)
return mw.getJson(data)
def getCpuIoApi(self):
start = request.args.get('start', '')
end = request.args.get('end', '')
data = self.getCpuIoData(start, end)
return mw.getJson(data)
def getDiskIoApi(self):
start = request.args.get('start', '')
end = request.args.get('end', '')
data = self.getDiskIoData(start, end)
return mw.getJson(data)
def getNetworkIoApi(self):
start = request.args.get('start', '')
end = request.args.get('end', '')
data = self.getNetWorkIoData(start, end)
return mw.getJson(data)
def rememoryApi(self):
os.system('sync')
scriptFile = mw.getRunDir() + '/script/rememory.sh'
mw.execShell("/bin/bash " + scriptFile)
data = self.getMemInfo()
return mw.getJson(data)
# 重启面板
def restartApi(self):
self.restartMw()
return mw.returnJson(True, '面板已重启!')
def restartServerApi(self):
if mw.isAppleSystem():
return mw.returnJson(False, "开发环境不可重起")
self.restartServer()
return mw.returnJson(True, '正在重启服务器!')
##### ----- end ----- ###
def restartTask(self):
initd = mw.getRunDir() + '/scripts/init.d/mw'
if os.path.exists(initd):
os.system(initd + ' ' + 'restart_task')
return True
def restartMw(self):
mw.writeFile('data/restart.pl', 'True')
return True
@mw_async
def restartServer(self):
if not mw.isRestart():
return mw.returnJson(False, '请等待所有安装任务完成再执行!')
mw.execShell("sync && init 6 &")
return mw.returnJson(True, '命令发送成功!')
# 名取PID
# 名取PID
def getPid(self, pname):
try:
if not self.pids:
@@ -166,59 +54,32 @@ class system_api:
except:
return False
def getEnvInfoApi(self, get=None):
serverInfo = {}
serverInfo['status'] = True
sdir = mw.getServerDir()
serverInfo['webserver'] = '未安装'
if os.path.exists(sdir + '/openresty/nginx/sbin/nginx'):
serverInfo['webserver'] = 'OpenResty'
serverInfo['php'] = []
phpversions = ['52', '53', '54', '55', '56', '70', '71',
'72', '73', '74', '80', '81', '82', '83', '84']
phpPath = sdir + '/php/'
for pv in phpversions:
if not os.path.exists(phpPath + pv + '/bin/php'):
continue
serverInfo['php'].append(pv)
serverInfo['mysql'] = False
if os.path.exists(sdir + '/mysql/bin/mysql'):
serverInfo['mysql'] = True
import psutil
try:
diskInfo = psutil.disk_usage('/www')
except:
diskInfo = psutil.disk_usage('/')
serverInfo['disk'] = diskInfo[2]
return mw.returnJson(True, 'ok', serverInfo)
def getPanelInfo(self, get=None):
# 取面板配置
address = mw.GetLocalIp()
address = public.GetLocalIp()
try:
try:
port = web.ctx.host.split(':')[1]
except:
port = mw.readFile('data/port.pl')
port = public.readFile('data/port.pl')
except:
port = '7200'
port = '8888'
domain = ''
if os.path.exists('data/domain.conf'):
domain = mw.readFile('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 = mw.readFile('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 = mw.readFile('data/templates.pl')
template = public.readFile('data/templates.pl')
check502 = ''
if os.path.exists('data/502Task.pl'):
@@ -233,17 +94,17 @@ class system_api:
data['cpuRealUsed'] = cpu[0]
data['time'] = self.getBootTime()
data['system'] = self.getSystemVersion()
data['isuser'] = mw.M('users').where(
data['isuser'] = public.M('users').where(
'username=?', ('admin',)).count()
data['version'] = '0.0.1'
return data
def getLoadAverage(self):
def getLoadAverage(self, get=None):
c = os.getloadavg()
data = {}
data['one'] = round(float(c[0]), 2)
data['five'] = round(float(c[1]), 2)
data['fifteen'] = round(float(c[2]), 2)
data['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
@@ -255,7 +116,7 @@ class system_api:
data['title'] = self.GetTitle()
data['network'] = self.GetNetWorkApi(get)
data['panel_status'] = not os.path.exists(
'/www/server/mdserver-web/data/close.pl')
'/www/server/panel/data/close.pl')
import firewalls
ssh_info = firewalls.firewalls().GetSshInfo(None)
data['enable_ssh_status'] = ssh_info['status']
@@ -268,114 +129,63 @@ class system_api:
def getTitle(self):
titlePl = 'data/title.pl'
title = 'Linux面板'
title = '宝塔Linux面板'
if os.path.exists(titlePl):
title = mw.readFile(titlePl).strip()
title = public.readFile(titlePl).strip()
return title
def getSystemDeviceTemperature(self):
if not hasattr(psutil, "sensors_temperatures"):
return False, "platform not supported"
temps = psutil.sensors_temperatures()
if not temps:
return False, "can't read any temperature"
for name, entries in temps.items():
for entry in entries:
return True, entry.label
# print("%-20s %s °C (high = %s °C, critical = %s °C)" % (
# entry.label or name, entry.current, entry.high,
# entry.critical))
return False, ""
def getSystemVersion(self):
#
# 取操作系统版本
current_os = mw.getOs()
# sys_temper = self.getSystemDeviceTemperature()
# print(sys_temper)
# mac
if current_os == 'darwin':
data = mw.execShell('sw_vers')[0]
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:
xlist = x.split("\t")
mac_version += xlist[len(xlist)-1] + ' '
mac_version += x.split("\t")[1] + ' '
return mac_version
arch_ver = mw.execShell("arch")
return mac_version + " (" + arch_ver[0].strip() + ")"
# freebsd
if current_os.startswith('freebsd'):
version = mw.execShell(
"cat /etc/*-release | grep PRETTY_NAME | awk -F = '{print $2}' | awk -F '\"' '{print $2}'")
arch_ver = mw.execShell(
"sysctl -a | egrep -i 'hw.machine_arch' | awk -F ':' '{print $2}'")
return version[0].strip() + " (" + arch_ver[0].strip() + ")"
redhat_series = '/etc/redhat-release'
if os.path.exists(redhat_series):
version = mw.readFile('/etc/redhat-release')
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()
arch_ver = mw.execShell("arch")
return version + " (" + arch_ver[0].strip() + ")"
os_series = '/etc/os-release'
if os.path.exists(os_series):
version = mw.execShell(
"cat /etc/*-release | grep PRETTY_NAME | awk -F = '{print $2}' | awk -F '\"' '{print $2}'")
arch_ver = mw.execShell("arch")
return version[0].strip() + " (" + arch_ver[0].strip() + ")"
return '未识别系统信息'
return version
def getBootTime(self):
# 取系统启动时间
uptime = mw.readFile('/proc/uptime')
if uptime == False:
start_time = psutil.boot_time()
run_time = time.time() - start_time
else:
run_time = uptime.split()[0]
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 mw.getInfo('已不间断运行: {1}天{2}小时{3}分钟', (str(int(days)), str(int(hours)), str(int(min))))
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()
cpuLogicalNum = psutil.cpu_count(logical=False)
used = psutil.cpu_percent(interval=interval)
cpuLogicalNum = 0
if os.path.exists('/proc/cpuinfo'):
c_tmp = mw.readFile('/proc/cpuinfo')
d_tmp = re.findall("physical id.+", c_tmp)
cpuLogicalNum = len(set(d_tmp))
return used, cpuCount
used_all = psutil.cpu_percent(percpu=True)
cpu_name = mw.getCpuType() + " * {}".format(cpuLogicalNum)
return used, cpuCount, used_all, cpu_name, cpuCount, cpuLogicalNum
def getMemInfo(self):
def getMemInfo(self, get=None):
# 取内存信息
mem = psutil.virtual_memory()
if mw.getOs() == 'darwin':
if public.getOs() == 'darwin':
memInfo = {
'memTotal': mem.total,
'memTotal': mem.total / 1024 / 1024
}
memInfo['memRealUsed'] = memInfo['memTotal'] * (mem.percent / 100)
else:
memInfo = {
'memTotal': mem.total,
'memFree': mem.free,
'memBuffers': mem.buffers,
'memCached': mem.cached
'memTotal': mem.total / 1024 / 1024,
'memFree': mem.free / 1024 / 1024,
'memBuffers': mem.buffers / 1024 / 1024,
'memCached': mem.cached / 1024 / 1024
}
memInfo['memRealUsed'] = memInfo['memTotal'] - \
@@ -389,7 +199,7 @@ class system_api:
import psutil
mem = psutil.virtual_memory()
if mw.getOs() == 'darwin':
if public.getOs() == 'darwin':
return mem.percent
memInfo = {'memTotal': mem.total / 1024 / 1024, 'memFree': mem.free / 1024 / 1024,
@@ -398,14 +208,11 @@ class system_api:
memInfo['memBuffers'] - memInfo['memCached']
tmp1 = memInfo['memTotal'] / 100
return (tmp / tmp1)
except Exception as ex:
except Exception, ex:
return 1
def getDiskInfo(self):
info = self.getDiskInfo2()
if len(info) != 0:
return info
def getDiskInfo(self, get=None):
return self.getDiskInfo2()
# 取磁盘分区信息
diskIo = psutil.disk_partitions()
diskInfo = []
@@ -417,24 +224,22 @@ class system_api:
continue
tmp = {}
tmp['path'] = disk[1]
size_tmp = psutil.disk_usage(disk[1])
tmp['size'] = [mw.toSize(size_tmp[0]), mw.toSize(
size_tmp[1]), mw.toSize(size_tmp[2]), str(size_tmp[3]) + '%']
tmp['size'] = psutil.disk_usage(disk[1])
diskInfo.append(tmp)
return diskInfo
def getDiskInfo2(self):
# 取磁盘分区信息
temp = mw.execShell(
temp = public.execShell(
"df -h -P|grep '/'|grep -v tmpfs | grep -v devfs")[0]
tempInodes = mw.execShell(
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', '/zroot', '/run/lock', '/run', '/run/shm', '/run/user']
'/dev/shm', '/run/lock', '/run', '/run/shm', '/run/user']
for tmp in temp1:
n += 1
inodes = tempInodes1[n - 1].split()
@@ -454,7 +259,16 @@ class system_api:
tmp1 = [disk[1], disk[2], disk[3], disk[4]]
arr['size'] = tmp1
arr['inodes'] = [inodes[1], inodes[2], inodes[3], inodes[4]]
diskInfo.append(arr)
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
# 清理系统垃圾
@@ -488,7 +302,7 @@ class system_api:
shutil.rmtree(filename)
else:
os.remove(filename)
print('mail clear ok')
print '\t\033[1;32m[OK]\033[0m'
num += 1
total += size
count += num
@@ -497,10 +311,10 @@ class system_api:
# 清理其它
def clearOther(self):
clearPath = [
{'path': '/www/server/mdserver-web', 'find': 'testDisk_'},
{'path': '/www/server/panel', 'find': 'testDisk_'},
{'path': '/www/wwwlogs', 'find': 'log'},
{'path': '/tmp', 'find': 'panelBoot.pl'},
{'path': '/www/server/mdserver-web/install', 'find': '.rpm'}
{'path': '/www/server/panel/install', 'find': '.rpm'}
]
total = count = 0
@@ -516,39 +330,16 @@ class system_api:
else:
os.remove(filename)
count += 1
mw.restartWeb()
public.serviceReload()
os.system('echo > /tmp/panelBoot.pl')
return total, count
def psutilNetIoCounters(self):
'''
统计网卡流量
'''
stat_pl = 'data/only_netio_counters.pl'
if os.path.exists(stat_pl):
local_lo = (0, 0, 0, 0)
ioName = psutil.net_io_counters(pernic=True).keys()
for x in ioName:
if x.find("lo") > -1:
local_lo = psutil.net_io_counters(
pernic=True).get(x)[:4]
all_io = psutil.net_io_counters()[:4]
result_io = tuple([all_io[i] - local_lo[i]
for i in range(0, len(all_io))])
# print(local_lo)
# print(all_io)
# print(result_io)
return result_io
return psutil.net_io_counters()[:4]
def getNetWork(self):
def getNetWork(self, get=None):
# return self.GetNetWorkApi(get);
# 取网络流量信息
try:
# 取网络流量信息
networkIo = self.psutilNetIoCounters()
networkIo = psutil.net_io_counters()[:4]
if not "otime" in session:
session['up'] = networkIo[0]
session['down'] = networkIo[1]
@@ -559,9 +350,9 @@ class system_api:
networkInfo['upTotal'] = networkIo[0]
networkInfo['downTotal'] = networkIo[1]
networkInfo['up'] = round(float(
networkIo[0] - session['up']) / (ntime - session['otime']), 2)
networkIo[0] - session['up']) / 1024 / (ntime - session['otime']), 2)
networkInfo['down'] = round(
float(networkIo[1] - session['down']) / (ntime - session['otime']), 2)
float(networkIo[1] - session['down']) / 1024 / (ntime - session['otime']), 2)
networkInfo['downPackets'] = networkIo[3]
networkInfo['upPackets'] = networkIo[2]
@@ -571,34 +362,82 @@ class system_api:
session['otime'] = time.time()
networkInfo['cpu'] = self.getCpuInfo()
networkInfo['load'] = self.getLoadAverage()
networkInfo['mem'] = self.getMemInfo()
networkInfo['load'] = self.getLoadAverage(get)
networkInfo['mem'] = self.getMemInfo(get)
return networkInfo
except Exception as e:
print("getNetWork error:", e)
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 = mw.M('network').dbfile('system').where("addtime>=? AND addtime<=?", (start, end)).field(
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 = mw.M('diskio').dbfile('system').where("addtime>=? AND addtime<=?", (start, end)).field(
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 = mw.M('cpuio').dbfile('system').where("addtime>=? AND addtime<=?",
(start, end)).field('id,pro,mem,addtime').order('id asc').select()
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 = mw.M('load_average').dbfile('system').where("addtime>=? AND addtime<=?", (
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)
@@ -642,194 +481,72 @@ class system_api:
def setControl(self, stype, day):
filename = 'data/control.conf'
stat_pl = 'data/only_netio_counters.pl'
if stype == '0':
mw.execShell("rm -rf " + filename)
public.execShell("rm -f " + filename)
elif stype == '1':
_day = int(day)
if _day < 1:
return mw.returnJson(False, "设置失败!")
mw.writeFile(filename, day)
elif stype == '2':
mw.execShell("rm -rf " + stat_pl)
elif stype == '3':
mw.execShell("echo 'True' > " + stat_pl)
return public.returnJson(False, "设置失败!")
public.writeFile(filename, day)
elif stype == 'del':
if not mw.isRestart():
return mw.returnJson(False, '请等待所有安装任务完成再执行')
if not public.isRestart():
return public.returnJson(False, '请等待所有安装任务完成再执行')
os.remove("data/system.db")
sql = db.Sql().dbfile('system')
csql = mw.readFile('data/sql/system.sql')
csql = public.readFile('data/sql/system.sql')
csql_list = csql.split(';')
for index in range(len(csql_list)):
sql.execute(csql_list[index], ())
return mw.returnJson(True, "监控服务已关闭")
return public.returnJson(True, "监控服务已关闭")
else:
data = {}
if os.path.exists(filename):
try:
data['day'] = int(mw.readFile(filename))
data['day'] = int(public.readFile(filename))
except:
data['day'] = 30
data['status'] = True
else:
data['day'] = 30
data['status'] = False
return public.getJson(data)
if os.path.exists(stat_pl):
data['stat_all_status'] = True
else:
data['stat_all_status'] = False
return public.returnJson(True, "设置成功!")
return mw.getJson(data)
return mw.returnJson(True, "设置成功!")
def versionDiff(self, old, new):
'''
test 测试
new 有新版本
none 没有新版本
'''
new_list = new.split('.')
if len(new_list) > 3:
return 'test'
old_list = old.split('.')
ret = 'none'
isHasNew = True
if int(new_list[0]) == int(old_list[0]) and int(new_list[1]) == int(old_list[1]) and int(new_list[2]) == int(old_list[2]):
isHasNew = False
if isHasNew:
return 'new'
return ret
def getServerInfo(self):
import urllib.request
import ssl
upAddr = 'https://api.github.com/repos/midoks/mdserver-web/releases/latest'
# 更新服务
def updateServer(self, stype):
try:
context = ssl._create_unverified_context()
req = urllib.request.urlopen(upAddr, context=context, timeout=3)
result = req.read().decode('utf-8')
version = json.loads(result)
return version
except Exception as e:
print('getServerInfo', e)
return {}
def updateServer(self, stype, version=''):
# 更新服务
try:
if not mw.isRestart():
return mw.returnJson(False, '请等待所有安装任务完成再执行!')
version_new_info = self.getServerInfo()
version_now = config_api.config_api().getVersion()
new_ver = version_new_info['name']
if not public.isRestart():
return public.returnMsg(False, '请等待所有安装任务完成再执行!')
if stype == 'check':
if not 'name' in version_new_info:
return mw.returnJson(False, '服务器数据或网络有问题!')
diff = self.versionDiff(version_now, new_ver)
if diff == 'new':
return mw.returnJson(True, '有新版本!', new_ver)
elif diff == 'test':
return mw.returnJson(True, '有测试版本!', new_ver)
else:
return mw.returnJson(False, '已经是最新,无需更新!')
if stype == 'info':
if not 'name' in version_new_info:
return mw.returnJson(False, '服务器数据有问题!')
diff = self.versionDiff(version_now, new_ver)
data = {}
data['version'] = new_ver
data['content'] = version_new_info[
'body'].replace("\n", "<br/>")
return mw.returnJson(True, '更新信息!', data)
if stype == 'update':
if version == '':
return mw.returnJson(False, '缺少版本信息!')
if new_ver != version:
return mw.returnJson(False, '更新失败,请重试!')
toPath = mw.getRootDir() + '/temp'
if not os.path.exists(toPath):
mw.execShell('mkdir -p ' + toPath)
newUrl = "https://github.com/midoks/mdserver-web/archive/refs/tags/" + version + ".zip"
dist_mw = toPath + '/mw.zip'
if not os.path.exists(dist_mw):
mw.execShell(
'wget --no-check-certificate -O ' + dist_mw + ' ' + newUrl)
dist_to = toPath + "/mdserver-web-" + version
if not os.path.exists(dist_to):
os.system('unzip -o ' + toPath +
'/mw.zip' + ' -d ' + toPath)
cmd_cp = 'cp -rf ' + toPath + '/mdserver-web-' + \
version + '/* ' + mw.getServerDir() + '/mdserver-web'
mw.execShell(cmd_cp)
mw.execShell('rm -rf ' + toPath + '/mdserver-web-' + version)
mw.execShell('rm -rf ' + toPath + '/mw.zip')
update_env = '''
#!/bin/bash
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
P_VER=`python3 -V | awk '{print $2}'`
if [ ! -f /www/server/mdserver-web/bin/activate ];then
cd /www/server/mdserver-web && python3 -m venv .
cd /www/server/mdserver-web && source /www/server/mdserver-web/bin/activate
else
cd /www/server/mdserver-web && source /www/server/mdserver-web/bin/activate
fi
cn=$(curl -fsSL -m 10 http://ipinfo.io/json | grep "\"country\": \"CN\"")
PIPSRC="https://pypi.python.org/simple"
if [ ! -z "$cn" ];then
PIPSRC="https://pypi.tuna.tsinghua.edu.cn/simple"
fi
cd /www/server/mdserver-web && pip3 install -r /www/server/mdserver-web/requirements.txt -i $PIPSRC
P_VER_D=`echo "$P_VER"|awk -F '.' '{print $1}'`
P_VER_M=`echo "$P_VER"|awk -F '.' '{print $2}'`
NEW_P_VER=${P_VER_D}.${P_VER_M}
if [ -f /www/server/mdserver-web/version/r${NEW_P_VER}.txt ];then
cd /www/server/mdserver-web && pip3 install -r /www/server/mdserver-web/version/r${NEW_P_VER}.txt -i $PIPSRC
fi
'''
os.system(update_env)
self.restartMw()
return mw.returnJson(True, '安装更新成功!')
return mw.returnJson(False, '已经是最新,无需更新!')
public.httpGet('')
except Exception as ex:
# print('updateServer', ex)
return mw.returnJson(False, "连接服务器失败!" + str(ex))
return public.returnJson(False, "连接服务器失败!")
# 重启面板
def reWeb(self, get):
# if not public.IsRestart(): return
# public.returnMsg(False,'EXEC_ERR_TASK');
# public.ExecShell('/etc/init.d/bt restart &')
return True
# 修复面板
def repPanel(self, get):
vp = ''
if mw.readFile('/www/server/mdserver-web/class/common.py').find('checkSafe') != -1:
if public.readFile('/www/server/panel/class/common.py').find('checkSafe') != -1:
vp = '_pro'
mw.ExecShell("wget -O update.sh " + mw.get_url() +
"/install/update" + vp + ".sh && bash update.sh")
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
# 升级到专业版
def updatePro(self, get):
public.ExecShell("wget -O update.sh " + public.get_url() +
"/install/update_pro.sh && bash update.sh pro")
if hasattr(web.ctx.session, 'getCloudPlugin'):
del(web.ctx.session['getCloudPlugin'])
return True
-103
View File
@@ -1,103 +0,0 @@
# coding: utf-8
# ---------------------------------------------------------------------------------
# MW-Linux面板
# ---------------------------------------------------------------------------------
# copyright (c) 2018-∞(https://github.com/midoks/mdserver-web) All rights reserved.
# ---------------------------------------------------------------------------------
# Author: midoks <midoks@163.com>
# ---------------------------------------------------------------------------------
# ---------------------------------------------------------------------------------
# 任务页操作
# ---------------------------------------------------------------------------------
import psutil
import time
import os
import sys
import mw
import re
import json
import pwd
from flask import request
class task_api:
def __init__(self):
pass
def countApi(self):
c = mw.M('tasks').where("status!=?", ('1',)).count()
return str(c)
def listApi(self):
p = request.form.get('p', '1')
limit = request.form.get('limit', '10').strip()
search = request.form.get('search', '').strip()
start = (int(p) - 1) * int(limit)
limit_str = str(start) + ',' + str(limit)
_list = mw.M('tasks').where('', ()).field(
'id,name,type,status,addtime,start,end').limit(limit_str).order('id desc').select()
_ret = {}
_ret['data'] = _list
count = mw.M('tasks').where('', ()).count()
_page = {}
_page['count'] = count
_page['tojs'] = 'remind'
_page['p'] = p
# return data
_ret['count'] = count
_ret['page'] = mw.getPage(_page)
return mw.getJson(_ret)
def getExecLogApi(self):
file = os.getcwd() + "/tmp/panelExec.log"
v = mw.getLastLine(file, 100)
return v
def getTaskSpeedApi(self):
tempFile = mw.getRunDir() + '/tmp/panelExec.log'
freshFile = mw.getRunDir() + '/tmp/panelFresh'
find = mw.M('tasks').where('status=? OR status=?',
('-1', '0')).field('id,type,name,execstr').find()
if not len(find):
return mw.returnJson(False, '当前没有任务队列在执行-2!')
mw.triggerTask()
data = {}
data['name'] = find['name']
data['execstr'] = find['execstr']
if find['type'] == 'download':
readLine = ""
for i in range(3):
try:
readLine = mw.readFile(tempFile)
if len(readLine) > 10:
data['msg'] = json.loads(readLine)
data['isDownload'] = True
break
except Exception as e:
if i == 2:
mw.M('tasks').where("id=?", (find['id'],)).save(
'status', ('0',))
return mw.returnJson(False, '当前没有任务队列在执行-4:' + str(e))
time.sleep(0.5)
else:
data['msg'] = mw.getLastLine(tempFile, 10)
data['isDownload'] = False
data['task'] = mw.M('tasks').where("status!=?", ('1',)).field(
'id,status,name,type').order("id asc").select()
return mw.getJson(data)
+5 -16
View File
@@ -1,17 +1,6 @@
#!/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
@@ -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))
-48
View File
@@ -1,48 +0,0 @@
# coding:utf-8
# ---------------------------------------------------------------------------------
# MW-Linux面板
# ---------------------------------------------------------------------------------
# copyright (c) 2018-∞(https://github.com/midoks/mdserver-web) All rights reserved.
# ---------------------------------------------------------------------------------
# Author: midoks <midoks@163.com>
# ---------------------------------------------------------------------------------
# ---------------------------------------------------------------------------------
# VIP用户管理
# ---------------------------------------------------------------------------------
import mw
from flask import request
import requests
class vip_api:
api_url = 'https://wo.midoks.icu/api/wp-json/vip'
def __init__(self):
pass
def loginApi(self):
username = request.form.get('username', '')
password = request.form.get('password', '')
password = mw.aesEncrypt(password)
headers = {
"Content-Type": "application/x-www-form-urlencoded",
}
print("name:", str(username))
print("pwd:", str(password))
args = {
'name': username,
'pass': password
}
data = requests.post(self.api_url + '/v1/login',
data=args, headers=headers)
print(data.text)
return mw.returnJson(False, "测试中!")
-5
View File
@@ -1,5 +0,0 @@
- fcgi-client.py
```
https://pypi.org/project/flup6/1.1.1/#files
```
-408
View File
@@ -1,408 +0,0 @@
# Copyright (c) 2006 Allan Saddi <allan@saddi.com>
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
# 1. Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
# SUCH DAMAGE.
#
# $Id$
#
# Copyright (c) 2011 Vladimir Rusinov <vladimir@greenmice.info>
__author__ = 'Allan Saddi <allan@saddi.com>'
__version__ = '$Revision$'
import sys
import select
import struct
import socket
import errno
import types
__all__ = ['FCGIApp']
# Constants from the spec.
FCGI_LISTENSOCK_FILENO = 0
FCGI_HEADER_LEN = 8
FCGI_VERSION_1 = 1
FCGI_BEGIN_REQUEST = 1
FCGI_ABORT_REQUEST = 2
FCGI_END_REQUEST = 3
FCGI_PARAMS = 4
FCGI_STDIN = 5
FCGI_STDOUT = 6
FCGI_STDERR = 7
FCGI_DATA = 8
FCGI_GET_VALUES = 9
FCGI_GET_VALUES_RESULT = 10
FCGI_UNKNOWN_TYPE = 11
FCGI_MAXTYPE = FCGI_UNKNOWN_TYPE
FCGI_NULL_REQUEST_ID = 0
FCGI_KEEP_CONN = 1
FCGI_RESPONDER = 1
FCGI_AUTHORIZER = 2
FCGI_FILTER = 3
FCGI_REQUEST_COMPLETE = 0
FCGI_CANT_MPX_CONN = 1
FCGI_OVERLOADED = 2
FCGI_UNKNOWN_ROLE = 3
FCGI_MAX_CONNS = 'FCGI_MAX_CONNS'
FCGI_MAX_REQS = 'FCGI_MAX_REQS'
FCGI_MPXS_CONNS = 'FCGI_MPXS_CONNS'
FCGI_Header = '!BBHHBx'
FCGI_BeginRequestBody = '!HB5x'
FCGI_EndRequestBody = '!LB3x'
FCGI_UnknownTypeBody = '!B7x'
FCGI_BeginRequestBody_LEN = struct.calcsize(FCGI_BeginRequestBody)
FCGI_EndRequestBody_LEN = struct.calcsize(FCGI_EndRequestBody)
FCGI_UnknownTypeBody_LEN = struct.calcsize(FCGI_UnknownTypeBody)
if __debug__:
import time
# Set non-zero to write debug output to a file.
DEBUG = 0
DEBUGLOG = '/www/server/mdserver-web/logs/fastcgi.log'
def _debug(level, msg):
if DEBUG < level:
return
try:
f = open(DEBUGLOG, 'a')
f.write('%sfcgi: %s\n' % (time.ctime()[4:-4], msg))
f.close()
except:
pass
def decode_pair(s, pos=0):
"""
Decodes a name/value pair.
The number of bytes decoded as well as the name/value pair
are returned.
"""
nameLength = ord(s[pos])
if nameLength & 128:
nameLength = struct.unpack('!L', s[pos:pos + 4])[0] & 0x7fffffff
pos += 4
else:
pos += 1
valueLength = ord(s[pos])
if valueLength & 128:
valueLength = struct.unpack('!L', s[pos:pos + 4])[0] & 0x7fffffff
pos += 4
else:
pos += 1
name = s[pos:pos + nameLength]
pos += nameLength
value = s[pos:pos + valueLength]
pos += valueLength
return (pos, (name, value))
def encode_pair(name, value):
"""
Encodes a name/value pair.
The encoded string is returned.
"""
nameLength = len(name)
if nameLength < 128:
s = chr(nameLength).encode()
else:
s = struct.pack('!L', nameLength | 0x80000000)
valueLength = len(value)
if valueLength < 128:
s += chr(valueLength).encode()
else:
s += struct.pack('!L', valueLength | 0x80000000)
return s + name + value
class Record(object):
"""
A FastCGI Record.
Used for encoding/decoding records.
"""
def __init__(self, type=FCGI_UNKNOWN_TYPE, requestId=FCGI_NULL_REQUEST_ID):
self.version = FCGI_VERSION_1
self.type = type
self.requestId = requestId
self.contentLength = 0
self.paddingLength = 0
self.contentData = ''
def _recvall(sock, length):
"""
Attempts to receive length bytes from a socket, blocking if necessary.
(Socket may be blocking or non-blocking.)
"""
dataList = []
recvLen = 0
while length:
try:
data = sock.recv(length)
except socket.error as e:
if e[0] == errno.EAGAIN:
select.select([sock], [], [])
continue
else:
raise
if not data: # EOF
break
dataList.append(data)
dataLen = len(data)
recvLen += dataLen
length -= dataLen
return b''.join(dataList), recvLen
_recvall = staticmethod(_recvall)
def read(self, sock):
"""Read and decode a Record from a socket."""
try:
header, length = self._recvall(sock, FCGI_HEADER_LEN)
except:
raise EOFError
if length < FCGI_HEADER_LEN:
raise EOFError
self.version, self.type, self.requestId, self.contentLength, \
self.paddingLength = struct.unpack(FCGI_Header, header)
if __debug__:
_debug(9, 'read: fd = %d, type = %d, requestId = %d, '
'contentLength = %d' %
(sock.fileno(), self.type, self.requestId,
self.contentLength))
if self.contentLength:
try:
self.contentData, length = self._recvall(sock,
self.contentLength)
except:
raise EOFError
if length < self.contentLength:
raise EOFError
if self.paddingLength:
try:
self._recvall(sock, self.paddingLength)
except:
raise EOFError
def _sendall(sock, data):
"""
Writes data to a socket and does not return until all the data is sent.
"""
length = len(data)
while length:
try:
sent = sock.send(data)
except socket.error as e:
if e[0] == errno.EAGAIN:
select.select([], [sock], [])
continue
else:
raise
data = data[sent:]
length -= sent
_sendall = staticmethod(_sendall)
def write(self, sock):
"""Encode and write a Record to a socket."""
self.paddingLength = -self.contentLength & 7
if __debug__:
_debug(9, 'write: fd = %d, type = %d, requestId = %d, '
'contentLength = %d' %
(sock.fileno(), self.type, self.requestId,
self.contentLength))
header = struct.pack(FCGI_Header, self.version, self.type,
self.requestId, self.contentLength,
self.paddingLength)
self._sendall(sock, header)
if self.contentLength:
self._sendall(sock, self.contentData)
if self.paddingLength:
self._sendall(sock, b'\x00' * self.paddingLength)
class FCGIApp(object):
def __init__(self, connect=None, host=None, port=None, filterEnviron=True):
if host is not None:
assert port is not None
connect = (host, port)
self._connect = connect
self._filterEnviron = filterEnviron
def __call__(self, environ, io, start_response=None):
# For sanity's sake, we don't care about FCGI_MPXS_CONN
# (connection multiplexing). For every request, we obtain a new
# transport socket, perform the request, then discard the socket.
# This is, I believe, how mod_fastcgi does things...
sock = self._getConnection()
# Since this is going to be the only request on this connection,
# set the request ID to 1.
requestId = 1
# Begin the request
rec = Record(FCGI_BEGIN_REQUEST, requestId)
rec.contentData = struct.pack(FCGI_BeginRequestBody, FCGI_RESPONDER, 0)
rec.contentLength = FCGI_BeginRequestBody_LEN
rec.write(sock)
# Filter WSGI environ and send it as FCGI_PARAMS
if self._filterEnviron:
params = self._defaultFilterEnviron(environ)
else:
params = self._lightFilterEnviron(environ)
# TODO: Anything not from environ that needs to be sent also?
# return '200 OK',[],str(params),''
self._fcgiParams(sock, requestId, params)
self._fcgiParams(sock, requestId, {})
# Transfer wsgi.input to FCGI_STDIN
content_length = int(environ.get('CONTENT_LENGTH') or 0)
s = ''
#io = StringIO(stdin)
while True:
if not io:
break
chunk_size = min(content_length, 4096)
s = io.read(chunk_size)
content_length -= len(s)
rec = Record(FCGI_STDIN, requestId)
rec.contentData = s
rec.contentLength = len(s)
rec.write(sock)
if not s:
break
# Empty FCGI_DATA stream
rec = Record(FCGI_DATA, requestId)
rec.write(sock)
return sock
def _getConnection(self):
if self._connect is not None:
# The simple case. Create a socket and connect to the
# application.
if isinstance(self._connect, str):
sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
sock.connect(self._connect)
elif hasattr(socket, 'create_connection'):
sock = socket.create_connection(self._connect)
else:
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect(self._connect)
return sock
# To be done when I have more time...
# , 'Launching and managing FastCGI programs not yet implemented'
raise NotImplementedError
def _fcgiGetValues(self, sock, vars):
# Construct FCGI_GET_VALUES record
outrec = Record(FCGI_GET_VALUES)
data = []
for name in vars:
data.append(encode_pair(name, ''))
data = ''.join(data)
outrec.contentData = data
outrec.contentLength = len(data)
outrec.write(sock)
# Await response
inrec = Record()
inrec.read(sock)
result = {}
if inrec.type == FCGI_GET_VALUES_RESULT:
pos = 0
while pos < inrec.contentLength:
pos, (name, value) = decode_pair(inrec.contentData, pos)
result[name] = value
return result
def _fcgiParams(self, sock, requestId, params):
rec = Record(FCGI_PARAMS, requestId)
data = []
for name, value in params.items():
data.append(encode_pair(name.encode(
'latin-1'), value.encode('latin-1')))
data = b''.join(data)
rec.contentData = data
rec.contentLength = len(data)
rec.write(sock)
_environPrefixes = ['SERVER_', 'HTTP_', 'REQUEST_', 'REMOTE_', 'PATH_',
'CONTENT_', 'DOCUMENT_', 'SCRIPT_']
_environCopies = ['SCRIPT_NAME', 'QUERY_STRING', 'AUTH_TYPE']
_environRenames = []
def _defaultFilterEnviron(self, environ):
result = {}
for n in environ.keys():
iv = False
for p in self._environPrefixes:
if n.startswith(p):
result[n] = environ[n]
iv = True
if n in self._environCopies:
result[n] = environ[n]
iv = True
if n in self._environRenames:
result[self._environRenames[n]] = environ[n]
iv = True
if not iv:
result[n] = environ[n]
return result
def _lightFilterEnviron(self, environ):
result = {}
for n in environ.keys():
if n.upper() == n:
result[n] = environ[n]
return result
-208
View File
@@ -1,208 +0,0 @@
# coding:utf-8
import json
import os
import time
import re
import sys
import time
import struct
import fcgi_client
FCGI_Header = '!BBHHBx'
if sys.version_info[0] == 2:
try:
from cStringIO import StringIO
except:
from StringIO import StringIO
else:
from io import BytesIO as StringIO
def get_header_data(sock):
'''
@name 获取头部32KB数据
@param sock socketobject(fastcgi套接字对象)
@return bytes
'''
headers_data = b''
total_len = 0
header_len = 1024 * 128
while True:
fastcgi_header = sock.recv(8)
if not fastcgi_header:
break
if len(fastcgi_header) != 8:
headers_data += fastcgi_header
break
fast_pack = struct.unpack(FCGI_Header, fastcgi_header)
if fast_pack[1] == 3:
break
tlen = fast_pack[3]
while tlen > 0:
sd = sock.recv(tlen)
if not sd:
break
headers_data += sd
tlen -= len(sd)
total_len += fast_pack[3]
if fast_pack[4]:
sock.recv(fast_pack[4])
if total_len > header_len:
break
return headers_data
def format_header_data(headers_data):
'''
@name 格式化响应头
@param headers_data bytes(fastcgi头部32KB数据)
@return status int(响应状态), headers dict(响应头), bdata bytes(格式化响应头后的多余数据)
'''
status = '200 OK'
headers = {}
pos = 0
while True:
eolpos = headers_data.find(b'\n', pos)
if eolpos < 0:
break
line = headers_data[pos:eolpos - 1]
pos = eolpos + 1
line = line.strip()
if len(line) < 2:
break
if line.find(b':') == -1:
continue
header, value = line.split(b':', 1)
header = header.strip()
value = value.strip()
if isinstance(header, bytes):
header = header.decode()
value = value.decode()
if header == 'Status':
status = value
if status.find(' ') < 0:
status += ' BTPanel'
else:
headers[header] = value
bdata = headers_data[pos:]
status = int(status.split(' ')[0])
return status, headers, bdata
def resp_sock(sock, bdata):
'''
@name 以流的方式发送剩余数据
@param sock socketobject(fastcgi套接字对象)
@param bdata bytes(格式化响应头后的多余数据)
@return yield bytes
'''
# 发送除响应头以外的多余头部数据
yield bdata
while True:
fastcgi_header = sock.recv(8)
if not fastcgi_header:
break
if len(fastcgi_header) != 8:
yield fastcgi_header
break
fast_pack = struct.unpack(FCGI_Header, fastcgi_header)
if fast_pack[1] == 3:
break
tlen = fast_pack[3]
while tlen > 0:
sd = sock.recv(tlen)
if not sd:
break
tlen -= len(sd)
if sd:
yield sd
if fast_pack[4]:
sock.recv(fast_pack[4])
sock.close()
class fpm(object):
def __init__(self, sock=None, document_root='', last_path=''):
'''
@name 实例化FPM对象
@param sock string(unixsocket路径)
@param document_root string(PHP文档根目录)
@return FPM
'''
if sock:
self.fcgi_sock = sock
if document_root[-1:] != '/':
document_root += '/'
self.document_root = document_root
self.last_path = last_path
def load_url_public(self, url, content=b'', method='GET', content_type='application/x-www-form-urlencoded'):
'''
@name 转发URL到PHP-FPM 公共
@param url string(URI地址)
@param content stream(POST数据io对象)
@return fastcgi-socket
'''
fcgi = fcgi_client.FCGIApp(connect=self.fcgi_sock)
try:
script_name, query_string = url.split('?')
except ValueError:
script_name = url
query_string = ''
content_length = len(content)
if content:
content = StringIO(content)
env = {
'SCRIPT_FILENAME': '%s%s' % (self.document_root, script_name),
'QUERY_STRING': query_string,
'REQUEST_METHOD': method,
'SCRIPT_NAME': self.last_path + script_name,
'REQUEST_URI': url,
'GATEWAY_INTERFACE': 'CGI/1.1',
'SERVER_SOFTWARE': 'MW-Panel',
'REDIRECT_STATUS': '200',
'CONTENT_TYPE': content_type,
'CONTENT_LENGTH': str(content_length),
'DOCUMENT_URI': script_name,
'DOCUMENT_ROOT': self.document_root,
'SERVER_PROTOCOL': 'HTTP/1.1',
'REMOTE_ADDR': '127.0.0.1',
'REMOTE_PORT': '7200',
'SERVER_ADDR': '127.0.0.1',
'SERVER_PORT': '80',
'SERVER_NAME': 'MW-Panel'
}
fpm_sock = fcgi(env, content)
_data = b''
while True:
fastcgi_header = fpm_sock.recv(8)
if not fastcgi_header:
break
if len(fastcgi_header) != 8:
_data += fastcgi_header
break
fast_pack = struct.unpack(FCGI_Header, fastcgi_header)
if fast_pack[1] == 3:
break
tlen = fast_pack[3]
while tlen > 0:
sd = fpm_sock.recv(tlen)
if not sd:
break
tlen -= len(sd)
_data += sd
if fast_pack[4]:
fpm_sock.recv(fast_pack[4])
status, headers, data = format_header_data(_data)
return data
-43
View File
@@ -1,43 +0,0 @@
# coding: utf-8
import smtplib
from email import encoders
from email.header import Header
from email.mime.text import MIMEText
from email.utils import parseaddr, formataddr
def _format_addr(s):
name, addr = parseaddr(s)
return formataddr((Header(name, 'utf-8').encode(), addr))
def send(smtp_host, smtp_port, username, password, to_mail, subject, content):
smtp = smtplib.SMTP()
smtp.connect(smtp_host, port=smtp_port)
smtp.login(user=username, password=password)
msg = MIMEText(content, 'plain', 'utf-8')
msg['From'] = _format_addr(username)
msg['To'] = _format_addr(to_mail)
msg['Subject'] = Header(subject, 'utf-8').encode()
smtp.sendmail(from_addr=username, to_addrs=to_mail, msg=msg.as_string())
return True
def sendSSL(smtp_host, smtp_port, username, password, to_mail, subject, content):
smtp = smtplib.SMTP_SSL(smtp_host, port=smtp_port)
smtp.login(user=username, password=password)
msg = MIMEText(content, 'plain', 'utf-8')
msg['From'] = _format_addr(username)
msg['To'] = _format_addr(to_mail)
msg['Subject'] = Header(subject, 'utf-8').encode()
smtp.sendmail(from_addr=username, to_addrs=to_mail, msg=msg.as_string())
smtp.quit()
return True
-116
View File
@@ -1,116 +0,0 @@
# coding: utf-8
import re
import os
import sys
import pymysql.cursors
class ORM:
__DB_PASS = None
__DB_USER = 'root'
__DB_PORT = 3306
__DB_NAME = ''
__DB_HOST = 'localhost'
__DB_CONN = None
__DB_CUR = None
__DB_ERR = None
__DB_CNF = '/etc/my.cnf'
__DB_SOCKET = '/www/server/mysql/mysql.sock'
__DB_CHARSET = "utf8"
def __Conn(self):
'''连接数据库'''
try:
if os.path.exists(self.__DB_SOCKET):
try:
self.__DB_CONN = pymysql.connect(host=self.__DB_HOST, user=self.__DB_USER, passwd=self.__DB_PASS,
database=self.__DB_NAME,
port=int(self.__DB_PORT), charset=self.__DB_CHARSET, connect_timeout=1,
unix_socket=self.__DB_SOCKET, cursorclass=pymysql.cursors.DictCursor)
except Exception as e:
self.__DB_HOST = '127.0.0.1'
self.__DB_CONN = pymysql.connect(host=self.__DB_HOST, user=self.__DB_USER, passwd=self.__DB_PASS,
database=self.__DB_NAME,
port=int(self.__DB_PORT), charset=self.__DB_CHARSET, connect_timeout=1,
unix_socket=self.__DB_SOCKET, cursorclass=pymysql.cursors.DictCursor)
else:
try:
self.__DB_CONN = pymysql.connect(host=self.__DB_HOST, user=self.__DB_USER, passwd=self.__DB_PASS,
database=self.__DB_NAME,
port=int(self.__DB_PORT), charset=self.__DB_CHARSET, connect_timeout=1,
cursorclass=pymysql.cursors.DictCursor)
except Exception as e:
self.__DB_HOST = '127.0.0.1'
self.__DB_CONN = pymysql.connect(host=self.__DB_HOST, user=self.__DB_USER, passwd=self.__DB_PASS,
database=self.__DB_NAME,
port=int(self.__DB_PORT), charset=self.__DB_CHARSET, connect_timeout=1,
cursorclass=pymysql.cursors.DictCursor)
self.__DB_CUR = self.__DB_CONN.cursor()
return True
except Exception as e:
self.__DB_ERR = e
return False
def setDbConf(self, conf):
self.__DB_CNF = conf
def setSocket(self, sock):
self.__DB_SOCKET = sock
def setCharset(self, charset):
self.__DB_CHARSET = charset
def setHost(self, host):
self.__DB_HOST = host
def setPort(self, port):
self.__DB_PORT = port
def setUser(self, user):
self.__DB_USER = user
def setPwd(self, pwd):
self.__DB_PASS = pwd
def getPwd(self):
return self.__DB_PASS
def setDbName(self, name):
self.__DB_NAME = name
def execute(self, sql):
# 执行SQL语句返回受影响行
if not self.__Conn():
return self.__DB_ERR
try:
result = self.__DB_CUR.execute(sql)
self.__DB_CONN.commit()
self.__Close()
return result
except Exception as ex:
return ex
def query(self, sql):
# 执行SQL语句返回数据集
if not self.__Conn():
return self.__DB_ERR
try:
self.__DB_CUR.execute(sql)
result = self.__DB_CUR.fetchall()
# print(result)
# 将元组转换成列表
# data = map(list, result)
self.__Close()
return result
except Exception as ex:
return ex
def __Close(self):
# 关闭连接
self.__DB_CUR.close()
self.__DB_CONN.close()
-115
View File
@@ -1,115 +0,0 @@
# coding: utf-8
import re
import os
import sys
class ORM:
__DB_PASS = None
__DB_USER = 'root'
__DB_PORT = 3306
__DB_HOST = 'localhost'
__DB_CONN = None
__DB_CUR = None
__DB_ERR = None
__DB_CNF = '/etc/my.cnf'
__DB_SOCKET = '/www/server/mysql/mysql.sock'
__DB_CHARSET = 'utf8'
def __Conn(self):
'''连接MYSQL数据库'''
try:
try:
import MySQLdb
except Exception as ex:
self.__DB_ERR = ex
return False
# print(self.__DB_HOST)
# print(self.__DB_USER)
# print(self.__DB_PASS)
# print(self.__DB_PORT)
# print(self.__DB_CHARSET)
# print(self.__DB_SOCKET)
if os.path.exists(self.__DB_SOCKET):
try:
self.__DB_CONN = MySQLdb.connect(host=self.__DB_HOST, user=self.__DB_USER, passwd=self.__DB_PASS,
port=int(self.__DB_PORT), charset=self.__DB_CHARSET, connect_timeout=1, unix_socket=self.__DB_SOCKET)
except Exception as e:
print(e)
self.__DB_HOST = '127.0.0.1'
self.__DB_CONN = MySQLdb.connect(host=self.__DB_HOST, user=self.__DB_USER, passwd=self.__DB_PASS,
port=int(self.__DB_PORT), charset=self.__DB_CHARSET, connect_timeout=1, unix_socket=self.__DB_SOCKET)
else:
try:
self.__DB_CONN = MySQLdb.connect(host=self.__DB_HOST, user=self.__DB_USER, passwd=self.__DB_PASS,
port=int(self.__DB_PORT), charset=self.__DB_CHARSET, connect_timeout=1)
except Exception as e:
self.__DB_HOST = '127.0.0.1'
self.__DB_CONN = MySQLdb.connect(host=self.__DB_HOST, user=self.__DB_USER, passwd=self.__DB_PASS,
port=int(self.__DB_PORT), charset=self.__DB_CHARSET, connect_timeout=1)
self.__DB_CUR = self.__DB_CONN.cursor()
return True
except MySQLdb.Error as e:
self.__DB_ERR = e
return False
def setDbConf(self, conf):
self.__DB_CNF = conf
def setSocket(self, sock):
self.__DB_SOCKET = sock
def setCharset(self, charset):
self.__DB_CHARSET = charset
def setPort(self, port):
self.__DB_PORT = port
def setPwd(self, pwd):
self.__DB_PASS = pwd
def getPwd(self):
return self.__DB_PASS
def setDbName(self, name):
self.__DB_NAME = name
def setUser(self, user):
self.__DB_USER = user
def execute(self, sql):
# 执行SQL语句返回受影响行
if not self.__Conn():
return self.__DB_ERR
try:
result = self.__DB_CUR.execute(sql)
self.__DB_CONN.commit()
self.__Close()
return result
except Exception as ex:
return ex
def query(self, sql):
# 执行SQL语句返回数据集
if not self.__Conn():
return self.__DB_ERR
try:
self.__DB_CUR.execute(sql)
result = self.__DB_CUR.fetchall()
# 将元组转换成列表
data = map(list, result)
self.__Close()
return data
except Exception as ex:
return ex
# 关闭连接
def __Close(self):
self.__DB_CUR.close()
self.__DB_CONN.close()
-7
View File
@@ -1,7 +0,0 @@
#!/bin/bash
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin:/usr/local/lib/python2.7/bin
find . -name .DS_Store | xargs rm -rf
find . -type d -name "*.pyc" | xargs rm -rf
-99
View File
@@ -1,99 +0,0 @@
#!/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=$PATH:$DIR/bin
if [ -f bin/activate ];then
source bin/activate
if [ "$?" != "0" ];then
echo "load local python env fail!"
fi
fi
export LC_ALL="en_US.UTF-8"
mw_start_task()
{
isStart=$(ps aux |grep 'task.py'|grep -v grep|awk '{print $2}')
if [ "$isStart" == '' ];then
echo -e "starting mw-tasks... \c"
cd $DIR && python3 task.py >> ${DIR}/logs/task.log 2>&1 &
sleep 0.3
isStart=$(ps aux |grep 'task.py'|grep -v grep|awk '{print $2}')
if [ "$isStart" == '' ];then
echo -e "\033[31mfailed\033[0m"
echo '------------------------------------------------------'
tail -n 20 $DIR/logs/task.log
echo '------------------------------------------------------'
echo -e "\033[31mError: mw-tasks service startup failed.\033[0m"
return;
fi
echo -e "\033[32mdone\033[0m"
else
echo "starting mw-tasks... mw-tasks (pid $(echo $isStart)) already running"
fi
}
mw_start(){
gunicorn -c setting.py app:app
#安全启动
mw_start_task
}
mw_start_debug(){
if [ ! -f $DIR/logs/task.log ];then
echo '' > $DIR/logs/task.log
fi
python3 task.py >> $DIR/logs/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
# gunicorn -b :${port} -k gevent -w 1 app:app
gunicorn -b :${port} -k geventwebsocket.gunicorn.workers.GeventWebSocketWorker -w 1 app:app
}
mw_start_debug2(){
python3 task.py >> $DIR/logs/task.log 2>&1 &
gunicorn -b :7200 -k geventwebsocket.gunicorn.workers.GeventWebSocketWorker -w 1 app:app
}
mw_stop()
{
PLIST=`ps -ef|grep app:app |grep -v grep|awk '{print $2}'`
for i in $PLIST
do
kill -9 $i > /dev/null 2>&1
done
pids=`ps -ef|grep task.py | grep -v grep |awk '{print $2}'`
arr=($pids)
for p in ${arr[@]}
do
kill -9 $p > /dev/null 2>&1
done
}
case "$1" in
'start') mw_start;;
'stop') mw_stop;;
'restart')
mw_stop
mw_start
;;
'debug')
mw_stop
mw_start_debug
;;
'debug2')
mw_stop
mw_start_debug2
;;
esac
-79
View File
@@ -1,79 +0,0 @@
### 常用命令说明
- 面板相关命令
```
/etc/init.d/mw default | 显示登录信息
/etc/init.d/mw db | 快捷连接数据库
----------------------------------------
mw open | 开启面板
mw close | 关闭面板
mw debug | 开发测试
mw venv | 进入虚拟环境
mw mirror | 切换镜像
mw install_app | 快捷安装常用软件
mw update | 更新到正式
mw 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 |
| ----------------- |-------|-------|-------|-------|-------|-------|-------|-------|-------|-------|-------|-------|-------|
| 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 |✅ |✅ |✅ |✅ |
BIN
View File
Binary file not shown.
BIN
View File
Binary file not shown.
+1 -11
View File
@@ -4,18 +4,13 @@
"type":0,
"ps":""
},
{
"title":"已安装",
"type":-1,
"ps":""
},
{
"title":"运行环境",
"type":1,
"ps":""
},
{
"title":"数据软件",
"title":"缓存软件",
"type":2,
"ps":""
},
@@ -33,10 +28,5 @@
"title":"其他插件",
"type":5,
"ps":""
},
{
"title":"辅助插件",
"type":6,
"ps":""
}
]
-1
View File
@@ -1 +0,0 @@
True
+41 -45
View File
@@ -19,6 +19,19 @@ CREATE TABLE IF NOT EXISTS `binding` (
);
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,
@@ -27,40 +40,52 @@ CREATE TABLE IF NOT EXISTS `crontab` (
`where_hour` INTEGER,
`where_minute` INTEGER,
`echo` TEXT,
`addtime` TEXT,
`status` INTEGER DEFAULT '1',
`save` INTEGER DEFAULT '3',
`backup_to` TEXT DEFAULT 'off',
`sname` TEXT,
`sbody` TEXT,
'stype' TEXT,
`urladdress` TEXT
`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,
`protocol` TEXT DEFAULT 'tcp',
`ps` TEXT,
`addtime` TEXT
);
ALTER TABLE `firewall` ADD COLUMN `protocol` TEXT DEFAULT 'tcp';
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');
INSERT INTO `firewall` (`id`, `port`, `protocol`, `ps`, `addtime`) VALUES
(1, '80', 'tcp','网站默认端口', '0000-00-00 00:00:00'),
(2, '443', 'tcp/udp', 'HTTPS', '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,
`uid` INTEGER DEFAULT '1',
`addtime` TEXT
);
ALTER TABLE `logs` ADD COLUMN `uid` INTEGER DEFAULT '1';
CREATE TABLE IF NOT EXISTS `sites` (
`id` INTEGER PRIMARY KEY AUTOINCREMENT,
@@ -68,17 +93,10 @@ CREATE TABLE IF NOT EXISTS `sites` (
`path` TEXT,
`status` TEXT,
`index` TEXT,
`type_id` INTEGER,
`ps` TEXT,
`edate` TEXT,
`addtime` TEXT
);
CREATE TABLE IF NOT EXISTS `site_types` (
`id` INTEGER PRIMARY KEY AUTOINCREMENT,
`name` TEXT
);
CREATE TABLE IF NOT EXISTS `domain` (
`id` INTEGER PRIMARY KEY AUTOINCREMENT,
`pid` INTEGER,
@@ -98,7 +116,7 @@ CREATE TABLE IF NOT EXISTS `users` (
);
INSERT INTO `users` (`id`, `username`, `password`, `login_ip`, `login_time`, `phone`, `email`) VALUES
(1, 'admin', '21232f297a57a5a743894a0e4a801fc3', '192.168.0.10', '2022-02-02 00:00:00', 0, 'midoks@163.com');
(1, 'admin', '21232f297a57a5a743894a0e4a801fc3', '192.168.0.10', '2016-12-10 15:12:56', 0, '287962566@qq.com');
CREATE TABLE IF NOT EXISTS `tasks` (
@@ -111,25 +129,3 @@ CREATE TABLE IF NOT EXISTS `tasks` (
`end` INTEGER,
`execstr` TEXT
);
CREATE TABLE IF NOT EXISTS `temp_login` (
`id` INTEGER PRIMARY KEY AUTOINCREMENT,
`token` REAL,
`salt` REAL,
`state` INTEGER,
`login_time` INTEGER,
`login_addr` REAL,
`logout_time` INTEGER,
`expire` INTEGER,
`addtime` INTEGER
);
CREATE TABLE IF NOT EXISTS `panel` (
`id` INTEGER PRIMARY KEY AUTOINCREMENT,
`title` TEXT,
`url` TEXT,
`username` TEXT,
`password` TEXT,
`click` INTEGER,
`addtime` INTEGER
);
-52
View File
@@ -1,52 +0,0 @@
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
#error_page 404/404.html;
#SSL-END
#301-START
#PROXY-START
#ERROR-PAGE-START
#error_page 404 /404.html;
#error_page 502 /502.html;
#ERROR-PAGE-END
#PHP-INFO-START
include {$PHP_DIR}/conf/enable-php-{$PHPVER}.conf;
#PHP-INFO-END
#REWRITE-START
include {$OR_REWRITE}/{$SERVER_NAME}.conf;
#REWRITE-END
#禁止访问的文件或目录
location ~ ^/(\.user.ini|\.htaccess|\.git|\.svn|\.project|LICENSE|README.md)
{
return 404;
}
#一键申请SSL证书验证目录相关设置
location ~ \.well-known{
allow all;
}
location ~ .*\\.(gif|jpg|jpeg|png|bmp|swf|js|css)$
{
expires 30d;
error_log /dev/null;
access_log /dev/null;
}
access_log {$LOGPATH}/{$SERVER_NAME}.log main;
error_log {$LOGPATH}/{$SERVER_NAME}.error.log;
}
-48
View File
@@ -1,48 +0,0 @@
#BINDING-{$DIRBIND}-START
server
{
listen {$PORT};
server_name {$DIRBIND};
index index.php index.html index.htm default.php default.htm default.html;
root {$ROOT_DIR};
#SSL-START
#error_page 404/404.html;
#SSL-END
#ERROR-PAGE-START
#error_page 404 /404.html;
#error_page 502 /502.html;
#ERROR-PAGE-END
#PHP-INFO-START
include {$PHP_DIR}/conf/enable-php-{$PHPVER}.conf;
#PHP-INFO-END
#REWRITE-START
include {$OR_REWRITE}/{$SERVER_MAIN}.conf;
#REWRITE-END
#禁止访问的文件或目录
location ~ ^/(\.user.ini|\.htaccess|\.git|\.svn|\.project|LICENSE|README.md)
{
return 404;
}
#一键申请SSL证书验证目录相关设置
location ~ \.well-known{
allow all;
}
location ~ .*\\.(gif|jpg|jpeg|png|bmp|swf|js|css)$
{
error_log /dev/null;
access_log /dev/null;
expires 30d;
}
access_log {$LOGPATH}/{$SERVER_MAIN}_{$DIRBIND}.log;
error_log {$LOGPATH}/{$SERVER_MAIN}_{$DIRBIND}.error.log;
}
#BINDING-{$DIRBIND}-END
-23
View File
@@ -1,23 +0,0 @@
server
{
listen {$PORT};
listen [::]:{$PORT};
server_name {$SERVER_NAME};
index index.php index.html index.htm default.php default.htm default.html;
#SSL-START
#error_page 404/404.html;
#SSL-END
#PROXY-START
location ^~ / {
proxy_pass http://0.0.0.0:{$PANAL_PORT}/;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
}
#PROXY-END
error_log {$LOGPATH}/{$SERVER_NAME}.error.log;
}
-1
View File
@@ -1 +0,0 @@
存放VIP关键数据
Executable
+6
View File
@@ -0,0 +1,6 @@
#!/bin/sh
rm -rf ./*.pyc
rm -rf ./*/*.pyc
#open "http://127.0.0.1:7200"
-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.1 && \
cd /www/server/mdserver-web/plugins/mysql && \
bash install.sh install 5.6 && \
cd /www/server/mdserver-web/plugins/phpmyadmin && \
bash install.sh install 4.4.15 && \
systemctl enable openresty && \
systemctl enable php74 && \
systemctl enable mysql
RUN rm -rf /www/server/mysql/data
ADD ./start.sh /start.sh
ADD start.service /usr/lib/systemd/system/start.service
#RUN systemctl enable start
CMD [ "/lib/systemd/systemd", "log-level=info", "unit=sysinit.target" ]
EXPOSE 7200 80 443 888
VOLUME [ "/www" ]
-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
+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=0.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()
-420
View File
@@ -1,420 +0,0 @@
# coding:utf-8
'''
doc: https://docs.python.org/zh-cn/3/library/ftplib.html
'''
import sys
import io
import os
import time
import re
import json
import paramiko
import ftplib
sys.path.append(os.getcwd() + "/class/core")
import mw
DEBUG = True
BLOCK_SIZE = 1024 * 1024 * 2
# BLOCK_SIZE = 50
PROGRESS_FILE_NAME = "PROGRESS_FILE_NAME"
"""
=============自定义异常===================
"""
class OsError(Exception):
"""OS端异常"""
class ObjectNotFound(OsError):
"""对象不存在时抛出的异常"""
def __init__(self, *args, **kwargs):
message = "文件对象不存在。"
super(ObjectNotFound, self).__init__(message, *args, **kwargs)
class APIError(Exception):
"""API参数错误异常"""
def __init__(self, *args, **kwargs):
_api_error_msg = 'API资料校验失败,请核实!'
super(APIError, self).__init__(_api_error_msg, *args, **kwargs)
class FtpPSClient:
_title = "FTP"
_name = "ftp"
__host = None
__port = None
__user = None
__password = None
default_port = 21
default_backup_path = "/backup/"
config_file = "cfg.json"
def __init__(self, load_config=True, timeout=10):
self.timeout = timeout
if load_config:
data = self.get_config()
self.injection_config(data)
def get_config(self):
default_config = {
"ftp_host": '',
"ftp_user": '',
"ftp_pass": '',
"backup_path": self.default_backup_path
}
cfg = mw.getServerDir() + "/backup_ftp/" + self.config_file
if os.path.exists(cfg):
data = mw.readFile(cfg)
return json.loads(data)
else:
return default_config
def injection_config(self, data):
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) + "_(.+)_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>
-310
View File
@@ -1,310 +0,0 @@
# coding:utf-8
import sys
import io
import os
import time
import re
import json
# print(sys.platform)
if sys.platform != "darwin":
os.chdir("/www/server/mdserver-web")
sys.path.append(os.getcwd() + "/class/core")
import mw
import db
_ver = sys.version_info
is_py2 = (_ver[0] == 2)
is_py3 = (_ver[0] == 3)
DEBUG = False
if is_py2:
reload(sys)
sys.setdefaultencoding('utf-8')
app_debug = False
if mw.isAppleSystem():
app_debug = True
def getPluginName():
return 'backup_ftp'
def getPluginDir():
return mw.getPluginDir() + '/' + getPluginName()
sys.path.append(getPluginDir() + "/class")
from ftp_client import FtpPSClient
def getServerDir():
return mw.getServerDir() + '/' + getPluginName()
def getArgs():
args = sys.argv[2:]
tmp = {}
args_len = len(args)
if args_len == 1:
t = args[0].strip('{').strip('}')
t = t.split(':')
tmp[t[0]] = t[1]
elif args_len > 1:
for i in range(len(args)):
t = args[i].split(':', 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):
return mw.returnJson(False, "未配置", [])
data = mw.readFile(cfg)
data = json.loads(data)
return mw.returnJson(True, "OK", data)
def setConf():
args = getArgs()
data = checkArgs(args, ['use_sftp', 'ftp_user',
'ftp_pass', 'ftp_host', 'backup_path'])
if not data[0]:
return data[1]
cfg = getServerDir() + "/cfg.json"
values = ['ftp_user',
'ftp_pass',
'ftp_host']
for v in values:
if args[v] == '':
return mw.returnJson(False, '必填资料不能为空,请核实!', [])
if args['backup_path'] == '':
args['backup_path'] = "/backup"
try:
ftp = FtpPSClient(load_config=False)
ftp.injection_config(args)
data = ftp.getList("/")
if data:
mw.writeFile(cfg, mw.getJson(args))
return mw.returnJson(True, '设置成功', [])
except Exception as e:
return mw.returnJson(False, "FTP校验失败,请核实!\n" + str(e), [])
def getList():
cfg = getServerDir() + "/cfg.json"
if not os.path.exists(cfg):
return mw.returnJson(False, "未配置FTP,请点击`账户设置`", [])
args = getArgs()
data = checkArgs(args, ['path'])
if not data[0]:
return data[1]
try:
ftp = FtpPSClient()
flist = ftp.getList(args['path'])
return mw.returnJson(True, "ok", flist)
except Exception as e:
return mw.returnJson(False, str(e), [])
def createDir():
cfg = getServerDir() + "/cfg.json"
if not os.path.exists(cfg):
return mw.returnJson(False, "未配置FTP,请点击`账户设置`", [])
args = getArgs()
data = checkArgs(args, ['path', 'name'])
if not data[0]:
return data[1]
ftp = FtpPSClient()
isok = ftp.createDir(args['path'], args['name'])
if isok:
return mw.returnJson(True, "创建成功")
return mw.returnJson(False, "创建失败")
def deleteDir():
args = getArgs()
data = checkArgs(args, ['dir_name', 'path'])
if not data[0]:
return data[1]
ftp = FtpPSClient()
isok = ftp.deleteDir(args['path'], args['dir_name'])
if isok:
return mw.returnJson(True, "删除成功")
return mw.returnJson(False, "删除失败")
def deleteFile():
args = getArgs()
data = checkArgs(args, ['path', 'filename'])
if not data[0]:
return data[1]
ftp = FtpPSClient()
isok = ftp.deleteFile(args['path'] + "/" + args['filename'])
if isok:
return mw.returnJson(True, "删除成功")
return mw.returnJson(False, "删除失败")
def backupAllFunc(stype):
os.chdir(mw.getRunDir())
name = sys.argv[2]
num = sys.argv[3]
prefix_dict = {
"site": "web",
"database": "db",
"path": "path",
}
backups = []
sql = db.Sql()
# print("stype:", stype)
# 提前获取-清理多余备份
if stype == 'site':
pid = sql.table('sites').where('name=?', (name,)).getField('id')
backups = sql.table('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 = sql.table('backup').where(
'type=? and pid=?', ('1', pid)).field('id,filename').select()
if stype == 'path':
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 = sql.table('backup').where(
'type=? and pid=?', ('1', pid)).field('id,filename').select()
args = stype + " " + name + " " + num
cmd = 'python3 ' + mw.getRunDir() + '/scripts/backup.py ' + args
if stype.find('database_') > -1:
plugin_name = stype.replace('database_', '')
args = "database " + name + " " + num
cmd = 'python3 ' + mw.getRunDir() + '/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
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 ''
print("|-准备上传文件 {}".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')
-17
View File
@@ -1,17 +0,0 @@
{
"title":"FTP存储空间",
"hook":["backup"],
"tip":"soft",
"name":"backup_ftp",
"type":"运行环境",
"ps":"将网站或数据库打包备份到FTP存储空间",
"versions":["1.0"],
"install_pre_inspection":false,
"shell":"install.sh",
"checks":"server/backup_ftp",
"path": "server/backup_ftp",
"author":"midoks",
"home":"https://github.com/midoks/mdserver-web",
"date":"2022-10-23",
"pid": "4"
}
-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

-28
View File
@@ -1,28 +0,0 @@
<div class="bt-form">
<div class="bt-w-main">
<div class="bt-w-menu">
<p class="bgw" onclick="pluginService('clean');">服务</p>
<p onclick="pluginConfig('clean');">配置修改</p>
<p onclick="pluginLogs('clean',null,'run_log');">运行日志</p>
<p onclick="pRead()">说明</p>
</div>
<div class="bt-w-con pd15">
<div class="soft-man-con"></div>
</div>
</div>
</div>
<script type="text/javascript">
pluginService('clean');
function pRead(){
var readme = '<ul class="help-info-text c7">';
readme += '<li>启动后,加入到计划中[2:30]执行。</li>';
readme += '<li>【*】代表要删除。</li>';
readme += '<li>添加/删除清除日志,可在`配置修改`中添加/删除。</li>';
readme += '<li>如果是目录,会寻找【.log】后缀的文件清空。</li>';
readme += '</ul>';
$('.soft-man-con').html(readme);
}
</script>
-227
View File
@@ -1,227 +0,0 @@
# coding:utf-8
import sys
import io
import os
import time
sys.path.append(os.getcwd() + "/class/core")
import mw
app_debug = False
if mw.isAppleSystem():
app_debug = True
def getPluginName():
return 'clean'
def getPluginDir():
return mw.getPluginDir() + '/' + getPluginName()
def getServerDir():
return mw.getServerDir() + '/' + getPluginName()
def getInitDFile():
if app_debug:
return '/tmp/' + getPluginName()
return '/etc/init.d/' + getPluginName()
def getConf():
path = getServerDir() + "/clean.conf"
return path
def getArgs():
args = sys.argv[2:]
tmp = {}
args_len = len(args)
if args_len == 1:
t = args[0].strip('{').strip('}')
t = t.split(':')
tmp[t[0]] = t[1]
elif args_len > 1:
for i in range(len(args)):
t = args[i].split(':')
tmp[t[0]] = t[1]
return tmp
def getLockFile():
return getServerDir() + "/installed_lock.pl"
def runLog():
return getServerDir() + "/clean.log"
def status():
initConf()
if os.path.exists(getLockFile()):
return "start"
return 'stop'
def initConf():
conf = getConf()
if not os.path.exists(conf):
content = ""
clog = [
"/var/spool/clientmqueue/*",
"/var/log/cron-*",
"/var/log/maillog-*",
"/var/log/secure-*",
"/var/log/spooler-*",
"/var/log/yum.log-*",
"/var/log/messages-*",
"/var/log/btmp-*",
"/var/log/audit/audit.log.*",
"/var/log/rhsm/rhsm.log-*",
"/var/log/rhsm/rhsmcertd.log-*",
"/tmp/yum_save_*",
"/tmp/tmp.*",
]
for i in clog:
content += i + "\n"
# 常用日志
clogcom = [
"/var/log/messages",
"/var/log/btmp",
"/var/log/wtmp",
"/var/log/secure",
"/var/log/lastlog",
"/var/log/cron",
"/www/server/cron"
]
for i in clogcom:
if os.path.exists(i):
content += i + "\n"
# 清理日志
rootDir = "/var/log"
l = os.listdir(rootDir)
for x in range(len(l)):
abspath = rootDir + "/" + l[x]
content += abspath + "\n"
mw.writeFile(conf, content)
def start():
initConf()
lock_file = getLockFile()
if not os.path.exists(lock_file):
mw.writeFile(lock_file, "")
import tool_task
tool_task.createBgTask()
return 'ok'
return 'fail'
def stop():
initConf()
lock_file = getLockFile()
if os.path.exists(lock_file):
os.remove(lock_file)
import tool_task
tool_task.removeBgTask()
return 'ok'
return 'fail'
def reload():
return 'ok'
def get_filePath_fileName_fileExt(filename):
(filepath, tempfilename) = os.path.split(filename)
(shotname, extension) = os.path.splitext(tempfilename)
return filepath, shotname, extension
def cleanFileLog(path):
filepath, shotname, extension = get_filePath_fileName_fileExt(path)
if extension == ".log":
cmd = "echo \"\" > " + path
tmp = mw.execShell(cmd)
if tmp[1] != "":
cmd += " | error:" + tmp[1].strip()
print(cmd)
def cleanSelfFileLog(path):
filepath, shotname, extension = get_filePath_fileName_fileExt(path)
if extension == ".log":
cmd = "echo \"\" > " + path
tmp = mw.execShell(cmd)
if tmp[1] != "":
cmd += " | error:" + tmp[1].strip()
print(cmd)
def cleanDirLog(path):
l = os.listdir(path)
for x in range(len(l)):
abspath = path + "/" + l[x]
if os.path.isfile(abspath):
cleanFileLog(abspath)
if os.path.isdir(abspath):
cleanDirLog(abspath)
def cleanLog():
conf = getConf()
clist = mw.readFile(conf).strip()
clist = clist.split("\n")
for x in clist:
abspath = x.strip()
if abspath.find('*') > 1:
cmd = 'rm -rf ' + abspath
print(cmd)
data = mw.execShell(cmd)
# print(data)
continue
if os.path.exists(abspath):
if os.path.isfile(abspath):
cleanSelfFileLog(abspath)
continue
if os.path.isdir(abspath):
cleanDirLog(abspath)
continue
if __name__ == "__main__":
func = sys.argv[1]
if func == 'status':
print(status())
elif func == 'start':
print(start())
elif func == 'stop':
print(stop())
elif func == 'restart':
print(restart())
elif func == 'reload':
print(reload())
elif func == 'conf':
print(getConf())
elif func == 'run_log':
print(runLog())
elif func == 'clean':
cleanLog()
else:
print('error')
-16
View File
@@ -1,16 +0,0 @@
{
"title":"日志清理",
"tip":"soft",
"name":"clean",
"type":"运行环境",
"ps":"日志清理",
"versions":"1.0",
"updates":"1.0",
"shell":"install.sh",
"checks":"server/clean",
"path": "server/clean",
"author":"clean",
"home":"https://github.com/midoks",
"date":"2021-11-23",
"pid": "5"
}
-43
View File
@@ -1,43 +0,0 @@
#!/bin/bash
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH
# cd /Users/midoks/Desktop/mwdev/server/mdserver-web/plugins/mongodb && /bin/bash install.sh install 5.0.4
# cd /www/server/mdserver-web/plugins/mongodb && /bin/bash install.sh install 5.0.4
curPath=`pwd`
rootPath=$(dirname "$curPath")
rootPath=$(dirname "$rootPath")
serverPath=$(dirname "$rootPath")
install_tmp=${rootPath}/tmp/mw_install.pl
VERSION=$2
if [ -f ${rootPath}/bin/activate ];then
source ${rootPath}/bin/activate
fi
Install_app()
{
echo '正在安装脚本文件...' > $install_tmp
mkdir -p $serverPath/clean
cd ${rootPath} && python3 ${rootPath}/plugins/clean/index.py start
echo "${VERSION}" > $serverPath/clean/version.pl
echo '安装完成' > $install_tmp
}
Uninstall_app()
{
cd ${rootPath} && python3 ${rootPath}/plugins/clean/index.py stop
rm -rf $serverPath/clean
echo "Uninstall_clean" > $install_tmp
}
action=$1
if [ "${1}" == 'install' ];then
Install_app
else
Uninstall_app
fi
-156
View File
@@ -1,156 +0,0 @@
# coding:utf-8
import sys
import io
import os
import time
import json
sys.path.append(os.getcwd() + "/class/core")
import mw
app_debug = False
if mw.isAppleSystem():
app_debug = True
def getPluginName():
return 'clean'
def getPluginDir():
return mw.getPluginDir() + '/' + getPluginName()
def getServerDir():
return mw.getServerDir() + '/' + getPluginName()
def getTaskConf():
conf = getServerDir() + "/task_config.json"
return conf
def getConfigData():
try:
return json.loads(mw.readFile(getTaskConf()))
except:
pass
return []
def getConfigTpl():
tpl = {
"name": "",
"task_id": -1,
}
return tpl
def createBgTask():
removeBgTask()
args = {
"period": "day",
"hour": "0",
"minute": "15",
}
createBgTaskByName(getPluginName(), args)
def createBgTaskByName(name, args):
cfg = getConfigTpl()
_name = "[勿删]日志清理[" + name + "]"
res = mw.M("crontab").field("id, name").where("name=?", (_name,)).find()
if res:
return True
if "task_id" in cfg.keys() and cfg["task_id"] > 0:
res = mw.M("crontab").field("id, name").where(
"id=?", (cfg["task_id"],)).find()
if res and res["id"] == cfg["task_id"]:
print("计划任务已经存在!")
return True
import crontab_api
api = crontab_api.crontab_api()
period = args['period']
_hour = ''
_minute = ''
_where1 = ''
_type_day = "day"
if period == 'day':
_type_day = 'day'
_hour = args['hour']
_minute = args['minute']
elif period == 'minute-n':
_type_day = 'minute-n'
_where1 = args['minute-n']
_minute = ''
mw_dir = mw.getRunDir()
cmd = '''
mw_dir=%s
rname=%s
plugin_path=%s
script_path=%s
logs_file=$plugin_path/${rname}.log
''' % (mw_dir, name, getServerDir(), getPluginDir())
cmd += 'echo "★【`date +"%Y-%m-%d %H:%M:%S"`】 STSRT★" >> $logs_file' + "\n"
cmd += 'echo ">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>" >> $logs_file' + "\n"
cmd += 'echo "python3 $script_path/index.py clean >> $logs_file 2>&1"' + "\n"
cmd += 'cd $mw_dir && python3 $script_path/index.py clean >> $logs_file 2>&1' + "\n"
cmd += 'echo "【`date +"%Y-%m-%d %H:%M:%S"`】 END★" >> $logs_file' + "\n"
cmd += 'echo "<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<" >> $logs_file' + "\n"
params = {
'name': _name,
'type': _type_day,
'week': "",
'where1': _where1,
'hour': _hour,
'minute': _minute,
'save': "",
'backup_to': "",
'stype': "toShell",
'sname': '',
'sbody': cmd,
'urladdress': '',
}
task_id = api.add(params)
if task_id > 0:
cfg["task_id"] = task_id
cfg["name"] = name
_dd = getConfigData()
_dd.append(cfg)
mw.writeFile(getTaskConf(), json.dumps(_dd))
def removeBgTask():
cfg_list = getConfigData()
for x in range(len(cfg_list)):
cfg = cfg_list[x]
if "task_id" in cfg.keys() and cfg["task_id"] > 0:
res = mw.M("crontab").field("id, name").where(
"id=?", (cfg["task_id"],)).find()
if res and res["id"] == cfg["task_id"]:
import crontab_api
api = crontab_api.crontab_api()
data = api.delete(cfg["task_id"])
if data[0]:
cfg["task_id"] = -1
cfg_list[x] = cfg
mw.writeFile(getTaskConf(), '[]')
return True
return False
if __name__ == "__main__":
if len(sys.argv) > 1:
action = sys.argv[1]
if action == "remove":
removeBgTask()
elif action == "add":
createBgTask()
Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

+550
View File
@@ -0,0 +1,550 @@
<style>
/*危险按钮*/
.bts-danger:hover{
color: #fff;
background-color: #c9302c;
border-color: #ac2925;
}
#whiteList a{
color: red;
}
/*弹窗*/
.Add_view{
height: 100px;
padding-left: 35px;
padding-top: 35px;
}
.Add_view label{
margin-right: 10px;
font-size: 15px;
font-weight: 100;
}
.Add_view input{
width: 230px;
}
.Add_view .error{
border: 1px solid red;
}
.Add_view .correct{
border: 1px solid #20a53a;
}
/*日志管理*/
.btnTitle{
font-weight: 100;
font-size: 16px;
line-height: 34px;
height: 34px;
margin-right: 15px;
position: relative;
top: 2px;
}
.whiteList-table{
margin-top: 10px;
}
/*登录日志表单*/
.viewTwo .whiteList-table{
overflow-x: hidden;
height: 500px;
}
.maks{
background: rgba(39, 39, 39, 0.4);
position: absolute;
width: 700px;
height: 555px;
z-index: 999;
overflow: hidden;
}
.boxshow{
position: absolute;
height: 150px;
width: 400px;
margin-left: -200px;
margin-top: -75px;
left: 50%;
top: 50%;
z-index: 1000;
background: #fff;
padding: 25px 20px;
overflow: hidden;
}
.btPayment {
float: left;
padding: 8px 10px;
width: 190px;
height: 100px;
line-height: 33px;
text-align: center;
border-left: 1px solid #ececec;
}
.btPayment .t2{
display: block;
width: 100%;
text-align: center;
font-size: 16px;
margin-bottom: 5px;
}
.btPayment .price{
color: #20A53A;
font-size: 22px;
margin: 0 5px;
}
.bt-form{
overflow: hidden;
}
.boxtext{
line-height: 32px;
margin-top: -17px;
text-align: center;
border-bottom: #eee 1px solid;
height: 39px;
font-size: 14px;
}
</style>
<div class="maks" style="display: none"></div>
<div class="boxshow" style="display: none;">
<div>
<div class="boxtext">
<span>未开通此服务,如需使用请开通企业运维版。</span>
</div>
<div class="btvipinfo" style="width: 170px;padding-left: 20px">
<p style="height: 25px">1、一对一运维人员对接</p>
<p style="height: 25px">2、提供每月3次运维服务</p>
<p style="height: 25px">3、双重安全隔离登录</p>
</div>
<div class="btPayment">
<span class="t2">企业运维版<span class="price">98</span>元/月</span>
<button class="btn btn-success btn-sm" style="width:80%" onclick="window.open('https://www.bt.cn/admin/index.html')">立即开通</button>
</div>
</div>
</div>
<div class="bt-form">
<div class="bt-w-main">
<div class="bt-w-menu">
<p class="bgw">密钥</p>
<p>面板设置</p>
<p>面板登录日志</p>
<p>SSH设置</p>
<p>SSH日志管理</p>
</div>
<div class="bt-w-con pd15">
<div class="soft-man-con viewZoo">
<p class="status" style="margin-top:10px;margin-left:10px;height: 80px;margin-bottom:15px;">
<span style="float:left;margin-top:-5px">密钥:</span>
<textarea class="bt-input-text" name="token" readonly="readonly" style="background-color: #eee;width:400px;height:80px;padding: 8px 15px;" placeholder="密钥已隐藏..."></textarea>
</p>
<input type="button" class="btn btn-success btn-sm" name="ButtonShow" style="margin-left:52px;width:100px" onclick="sagelogin.tokenShow()" value="显示密钥" />
<ul style="margin: 15px 5px;padding-left: 70px;list-style-type: disc;line-height: 25px;">
<li>宝塔企业运维后台连接您的服务器需要此密钥,请妥善记录并保存</li>
<li>一但密钥丢失,可能导致您无法登录服务器</li>
</ul>
</div>
<div class="soft-man-con viewOne" style="display: none;">
<p class="status">当前状态:
<span>开启</span>
<span style="color: #20a53a; margin-left: 3px;" class="glyphicon glyphicon glyphicon-play"></span>
<span style="color: red; margin-left: 3px; display: none" class="glyphicon glyphicon-pause"></span>
</p>
<div class="sfm-opt">
<button class="btn btn-default btn-sm bts-danger" onclick="sagelogin.Panel_Admin()">清除面板登录限制IP</button>
<button class="btn btn-default btn-sm bts-safety" onclick="sagelogin.Add_view('面板白名单',true)">添加白名单</button>
</div>
<div class="whiteList-table divtable" style="overflow-x: hidden;height: 420px">
<table class="table table-hover" style="max-height: 380px; overflow: auto;">
<thead>
<tr>
<th>IP地址</th>
<th style="text-align: right;">操作</th>
</tr>
</thead>
<tbody id="whiteListOne"></tbody>
</table>
</div>
</div>
<div class="soft-man-con viewTwo" style="display: none">
<div class="whiteList-table divtable">
<table class="table table-hover" style=" max-height: 380px; overflow: auto;">
<thead>
<tr>
<th>操作记录</th>
<th>操作时间</th>
</tr>
</thead>
<tbody id="whiteListTwo"></tbody>
</table>
</div>
</div>
<div class="soft-man-con viewThree" style="display: none">
<p class="status">当前状态:
<span>开启</span>
<span style="color: #20a53a; margin-left: 3px;" class="glyphicon glyphicon glyphicon-play">
</span>
<span style="color: red; margin-left: 3px; display: none" class="glyphicon glyphicon-pause"></span>
</p>
<div class="sfm-opt">
<button class="btn btn-default btn-sm bts-danger" onclick="sagelogin.Panel_Admin()">清除SSH登录限制IP</button>
<button class="btn btn-default btn-sm bts-safety" onclick="sagelogin.Add_view('SSH白名单',false)">添加白名单</button>
</div>
<div class="whiteList-table divtable" style="overflow-x: hidden;height: 420px">
<table class="table table-hover" style="max-height: 380px; overflow: auto;">
<thead>
<tr>
<th>IP地址</th>
<th style="text-align: right;">操作</th>
</tr>
</thead>
<tbody id="whiteListThree"></tbody>
</table>
</div>
</div>
<div class="soft-man-con viewFour" style="display: none">
<div class="btnclick">
<label class="btnTitle">日志分类</label>
<div class="btn-group">
<input type="submit" name="btnOne" class="btn btn-success" onclick="sagelogin.SSH_Defense_List(0)" value="已防御" />
<input type="submit" name="btnTwo" class="btn btn-default" onclick="sagelogin.SSH_Defense_List(1)" value="入侵日志"/>
<input type="submit" name="btnThree" class="btn btn-default" onclick="sagelogin.SSH_Defense_List(2)" value="登录日志"/>
</div>
</div>
<div class="whiteList-table divtable" style="overflow-x: hidden;height: 470px;">
<table class="table table-hover" style="max-height: 380px; overflow: auto;">
<thead>
<tr>
<th>源IP地址</th>
<th>用户</th>
<th style="text-align: right;">登录时间</th>
</tr>
</thead>
<tbody id="whiteListFour"></tbody>
</table>
</div>
</div>
</div>
</div>
</div>
<script type="javascript/text">
//宝塔安全登录
$(function(){
$(".bt-w-menu p").click(function(){
$(this).addClass("bgw").siblings().removeClass("bgw");
$('.bt-w-con .soft-man-con').hide().eq($(this).index()).show();
switch($(this).index()) {
case 0:
break;
case 1:
sagelogin.Panel_White_List();
break;
case 2:
sagelogin.Panel_journal();
break;
case 3:
sagelogin.SSH_White_List();
break;
case 4:
sagelogin.SSH_Defense_List(0);
break;
}
});
layer.load();
$.get('/plugin?action=a&name=safelogin&s=GetServerInfo',function(data) {
layer.closeAll('loading');
if (data.status) {
$('.boxshow').hide();
$('.maks').hide();
}else{
$('.boxshow').show();
$('.maks').show();
}
});
});
var sagelogin = new Object();
sagelogin = {
Addview:'',
SSHlogList:'',
PanelLogList:'',
// SSH管理
SSH_Admin:function(){
var confirm = layer.confirm('确定清除面板登录限制IP地址?',{icon: 1}, {
btn: ['确认','取消'],
}, function(){
$.get('/plugin?action=a&name=safelogin&s=close_ssh_limit',function(data) {
if (data.status) {
layer.msg(data.msg,{icon:1});
sagelogin.SSH_White_List();
}else{
layer.msg(data.msg,{icon:2});
}
});
}, function(){
layer.close(confirm);
});
},
tokenShow:function(){
var inputPwd = layer.open({
type: 1,
title: '验证面板密码',
shadeClose: true,
shade: 0.3,
area: ['380px', '190px'],
btn:['确定','取消'],
content:'<div class="Add_view" style="height:90px"><label>面板密码:</label><input name="panel_pwd" class=" bt-input-text" type="password"></div>',
yes:function(){
var passwordStr = $("input[name='panel_pwd']").val();
var index = layer.load(0, {shade: false});
$.post('/plugin?action=a&name=safelogin&s=GetServerToken',{password:passwordStr},function(token) {
layer.close(index);
if(token.status === false){
layer.msg(token.msg,{icon:2});
return;
}
layer.close(inputPwd);
$("textarea[name='token']").val(token);
});
},
btn2:function(){
layer.close(inputPwd);
}
});
},
// SSH白名单——列表
SSH_White_List:function(){
var index = layer.load(0, {shade: false});
$.get('/plugin?action=a&name=safelogin&s=get_ssh_limit','',function(data){
$('#whiteListThree').empty();
var List = '';
if (data.length == 0){
$('.viewThree .status span:eq(0)').html('关闭');
$('.viewThree .glyphicon-pause').show();
$('.viewThree .glyphicon-play').hide();
}else {
$('.viewThree .status span:eq(0)').html('开启');
$('.viewThree .glyphicon-pause').hide();
$('.viewThree .glyphicon-play').show();
}
for (var i = 0; i < data.length; i++) {
List = List + '<tr><td>'+ data[i] +'</td><td style="text-align:right;"><a href="javascript:;" ip-val="'+ data[i] +'" onclick="sagelogin.SSH_Del_White_List(event)" style="color:red">删除</a></td></tr>'
}
$('#whiteListThree').append(List);
layer.close(index);
});
},
// SSH白名单——删除
SSH_Del_White_List:function(ev){
var ip = ev.currentTarget.getAttribute('ip-val');
var confirm = layer.confirm('确定删除IP['+ ip +']限制地址?', {
btn: ['确认','取消'] //按钮
}, function(){
$.get('/plugin?action=a&name=safelogin&s=remove_ssh_limit',{ip:ip},function(data){
if (data.status) {
sagelogin.SSH_White_List();
layer.msg(data.msg,{icon:1});
}else {
layer.msg(data.msg,{icon:2});
}
});
}, function(){
layer.close(confirm);
});
},
// SSH白名单——添加
SSH_Add_White_List:function(ip){
$.post('/plugin?action=a&name=safelogin&s=add_ssh_limit',{ip:ip},function(data){
layer.close(sagelogin.Addview);
if (data.status) {
sagelogin.SSH_White_List();
layer.msg(data.msg,{icon:1});
}else{
layer.msg(data.msg,{icon:2});
}
});
},
// SSH日志管理
SSH_Defense_List:function(status){
$('.btnclick input').removeClass('btn-success').addClass('btn-default');
$('.btnclick input').eq(status).addClass('btn-success').removeClass('btn-default');
if (sagelogin.SSHlogList == '') {
$.get('/plugin?action=a&s=get_ssh_errorlogin&name=safelogin',function(data){
sagelogin.SSHlogList = data;
sagelogin.DefenseFor(sagelogin.SSHlogList.defense);
});
}else{
switch(status) {
case 0:
sagelogin.DefenseFor(sagelogin.SSHlogList.defense);
break;
case 1:
sagelogin.DefenseFor(sagelogin.SSHlogList.intrusion);
break;
case 2:
sagelogin.DefenseFor(sagelogin.SSHlogList.success);
break;
}
}
},
// SSH日志渲染模板
DefenseFor:function(data){
var List = '';
$('#whiteListFour').empty();
for (var i = data.length - 1; i >= 0; i--) {
List = List + '<tr><td>'+data[i].address+'</td><td>' + data[i].user + '</td><td style="text-align:right">'+ data[i].date+'</td></tr>'
}
$('#whiteListFour').append(List);
},
// 面板管理
Panel_Admin:function(){
var confirm = layer.confirm('确定清除面板登录限制IP地址?', {
btn: ['确认','取消'] //按钮
}, function(){
$.get('/plugin?action=a&name=safelogin&s=close_panel_limit',function(data) {
if (data.status) {
layer.msg(data.msg,{icon:1});
sagelogin.Panel_White_List();
}else{
layer.msg(data.msg,{icon:2});
}
});
}, function(){
layer.close(confirm);
});
},
// 面板白名单——列表
Panel_White_List:function(){
$.get('/plugin?action=a&name=safelogin&s=get_panel_limit','',function(data){
$('#whiteListOne').empty();
var List = '';
if (data.length == 0){
$('.viewOne .status span:eq(0)').html('关闭');
$('.viewOne .glyphicon-pause').show();
$('.viewOne .glyphicon-play').hide();
}else {
$('.viewOne .status span:eq(0)').html('开启');
$('.viewOne .glyphicon-pause').hide();
$('.viewOne .glyphicon-play').show();
}
for (var i = 0; i < data.length; i++) {
List = List + '<tr><td>'+ data[i] +'</td><td style="text-align:right;"><a href="javascript:;" ip-val="'+ data[i] +'" onclick="sagelogin.Panel_Del_White_List(event)" style="color:red">删除</a></td></tr>'
}
$('#whiteListOne').append(List);
});
},
// 面板白名单——删除
Panel_Del_White_List:function(ev){
var ip = ev.currentTarget.getAttribute('ip-val');
var confirm = layer.confirm('确定删除IP['+ ip +']限制地址?', {
btn: ['确认','取消'] //按钮
}, function(){
$.get('/plugin?action=a&name=safelogin&s=remove_ssh_limit',{ip:ip},function(data){
if (data.status) {
sagelogin.Panel_White_List();
layer.msg(data.msg,{icon:1});
}else {
layer.msg(data.msg,{icon:2});
}
});
}, function(){
layer.close(confirm);
});
},
// 面板白名单——添加
Panel_Add_White_List:function(ip){
$.post('/plugin?action=a&name=safelogin&s=add_panel_limit', {ip: ip}, function(data) {
layer.close(sagelogin.Addview);
if (data.status) {
sagelogin.Panel_White_List();
layer.msg(data.msg,{icon:1});
}else{
layer.msg(data.msg,{icon:2});
}
});
},
// 面板日志——列表
Panel_journal:function(){
var index = layer.load(0, {shade: false});
$.get('/plugin?action=a&name=safelogin&s=get_login_log',function(data){
$('#whiteListTwo').empty();
var List = '';
sagelogin.PanelLogList = data;
for (var i = sagelogin.PanelLogList.length - 1; i >= 0; i--) {
List = List + '<tr><td>'+ sagelogin.PanelLogList[i].log +'</td><td style="text-align:right;">'+ sagelogin.PanelLogList[i].addtime +'</tr>';
}
$('#whiteListTwo').append(List);
layer.close(index);
});
},
// IP正则:192.168.1.0/24
CheckIP:function(ip) {
var reg = /^(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])(\/\d{1,2})?$/;
return reg.test(ip);
},
CheckIPTwo:function(ip) {
var reg = /^([1-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])(\.([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])){3}$/gi;
return reg.test(ip);
},
// 添加表单效验
addViewVul:function(status){
var ip = $('.addViewVul');
if (status) {
console.log(sagelogin.CheckIP(ip.val()));
if(sagelogin.CheckIPTwo(ip.val())){
ip.addClass('correct').removeClass('error');
return true;
}else {
ip.addClass('error').removeClass('correct');
return false;
}
}else{
if(sagelogin.CheckIP(ip.val())){
ip.addClass('correct').removeClass('error');
return true;
}else {
ip.addClass('error').removeClass('correct');
return false;
}
}
},
// 添加界面
Add_view:function(title,status){
var _this = this;
var text = ''
var statuss = true;
if (status) {
text = '';
statuss = true;
}else{
text = '<p style="color:red;margin: 5px 0px 0px 67px;font-size: 14px;">支持IP段,如:192.168.1.0/24</p>';
statuss = false;
}
sagelogin.Addview = layer.open({
type: 1,
title: title,
shadeClose: true,
shade: 0.1,
area: ['380px', '200px'],
btn:['确定','取消'],
content:'<div class="Add_view"><label>IP地址:</label><input class="addViewVul bt-input-text" onchange="sagelogin.addViewVul('+ statuss +')" type="text">'+ text +'</div>',
yes:function(){
if (sagelogin.addViewVul(statuss)) {
if(status){
sagelogin.Panel_Add_White_List($('.addViewVul').val());
}else{
sagelogin.SSH_Add_White_List($('.addViewVul').val());
}
}else{
layer.msg('请输入合法的IP地址',{icon:2})
}
},
btn2:function(){
layer.close(sagelogin.Addview);
}
});
$('.addViewVul').focus();
}
}
</script>
+20
View File
@@ -0,0 +1,20 @@
# coding: utf-8
import time
import psutil
import random
import os
import urllib
import binascii
import json
import public
import re
def status():
return 'start'
if __name__ == "__main__":
func = sys.argv[1]
if func == 'status':
print status()
+16
View File
@@ -0,0 +1,16 @@
{
"sort": 7,
"ps": "最流行的SVN代码共享管理软件",
"shell": "install.sh",
"name": "csvn",
"title": "CSVN",
"versions": ["5.1"],
"updates":["5.1.4"],
"tip": "soft",
"checks": "server/csvn",
"author": "midoks",
"date": "2017-04-01",
"home": "https://www.collab.net",
"type": "SVN代码共享管理软件",
"pid": "3"
}
+43
View File
@@ -0,0 +1,43 @@
#!/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
CSVN_SOURCE='https://github.com/midoks/mdserver-web/releases/download/init/CollabNetSubversionEdge-5.1.4_linux-x86_64.tar.xz'
Install_csvn()
{
mkdir -p $serverPath/tmp
echo '正在安装脚本文件...' > $install_tmp
if [ ! -f $serverPath/tmp/csvn.tar.xz ];then
wget -O $serverPath/so/csvn.tar.xz ${CSVN_SOURCE}
fi
cd $serverPath/tmp && tar -Jxf $serverPath/tmp/csvn.tar.xz
mv $serverPath/tmp/csvn $serverPath/csvn
echo '安装完成' > $install_tmp
}
Uninstall_csvn()
{
rm -rf $serverPath/csvn
}
action=$1
host=$2
if [ "${1}" == 'install' ];then
Install_csvn
else
Uninstall_csvn
fi
Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.3 KiB

-25
View File
@@ -1,25 +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('data_query');">服务</p>
</div>
<div class="bt-w-con pd15">
<div class="soft-man-con"></div>
</div>
</div>
</div>
<script type="text/javascript">
resetPluginWinHeight(300);
resetPluginWinWidth(380);
pluginService('data_query', $('.plugin_version').attr('version'));
</script>
-97
View File
@@ -1,97 +0,0 @@
# coding:utf-8
import sys
import io
import os
import time
import re
sys.path.append(os.getcwd() + "/class/core")
import mw
app_debug = False
if mw.isAppleSystem():
app_debug = True
def getPluginName():
return 'data_query'
def getPluginDir():
return mw.getPluginDir() + '/' + getPluginName()
def getServerDir():
return mw.getServerDir() + '/' + 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 status():
return 'start'
def appOp(method):
return 'ok'
def start():
return appOp('start')
def stop():
return appOp('stop')
def restart():
status = appOp('restart')
return status
def reload():
return appOp('reload')
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())
else:
print('error')
-36
View File
@@ -1,36 +0,0 @@
{
"hook":[
{
"tag":"menu",
"menu": {
"title":"数据管理",
"name":"data_query",
"path":"static/html/index.html",
"css_path":"static/css/data.css",
"js_path":"static/js/app.js"
}
},
{
"tag":"global_static",
"global_static": {
"title":"数据管理",
"name":"data_query",
"css_path":"static/css/ico.css"
}
}
],
"sort": 7,
"ps": "数据简单管理(memcached,redis,mongodb)",
"name": "data_query",
"title": "数据管理",
"shell": "install.sh",
"versions":"1.0",
"tip": "soft",
"checks": "server/data_query",
"path": "server/data_query",
"display": 1,
"author": "midoks",
"date": "2024-01-22",
"type": 0,
"pid": "2"
}
-34
View File
@@ -1,34 +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")
install_tmp=${rootPath}/tmp/mw_install.pl
VERSION=$2
Install_App()
{
echo '正在安装脚本文件...' > $install_tmp
mkdir -p $serverPath/source
mkdir -p $serverPath/data_query
echo "${VERSION}" > $serverPath/data_query/version.pl
echo '安装完成'
}
Uninstall_App()
{
rm -rf $serverPath/data_query
echo "卸载成功"
}
action=$1
if [ "${1}" == 'install' ];then
Install_App
else
Uninstall_App
fi
-253
View File
@@ -1,253 +0,0 @@
# coding:utf-8
# https://pypi.org/project/pymemcache/
# https://pymemcache.readthedocs.io/en/latest/getting_started.html#using-a-client-pool
import sys
import io
import os
import time
import re
import json
import pymemcache
sys.path.append(os.getcwd() + "/class/core")
import mw
def singleton(cls):
_instance = {}
def inner():
if cls not in _instance:
_instance[cls] = cls()
return _instance[cls]
return inner
@singleton
class nosqlMemcached():
__DB_PASS = None
__DB_USER = None
__DB_PORT = 11211
__DB_HOST = '127.0.0.1'
__DB_CONN = None
__DB_ERR = None
__DB_LOCAL = None
def __init__(self):
self.__config = self.get_options(None)
def conn(self):
if self.__DB_HOST in ['127.0.0.1', 'localhost']:
mem_path = "{}/memcached".format(mw.getServerDir())
if not os.path.exists(mem_path): return False
if not self.__DB_LOCAL:
self.__DB_PORT = int(self.__config['port'])
try:
self.__DB_CONN = pymemcache.client.base.PooledClient((self.__DB_HOST,self.__DB_PORT), max_pool_size=4)
return self.__DB_CONN
except pymemcache.exceptions.MemcacheError:
return False
except Exception:
self.__DB_ERR = mw.getTracebackInfo()
return False
# 获取配置项
def get_options(self, get=None):
result = {}
mem_content = mw.readFile("{}/memcached/memcached.env".format(mw.getServerDir()))
if not mem_content: return False
keys = ["bind", "PORT"]
rep = 'PORT\s*=\s*(.*)'
port_re = re.search(rep, mem_content)
if port_re:
result['port'] = int(port_re.groups()[0].strip())
else:
result['port'] = 11211
return result
def set_host(self, host, port, prefix=''):
self.__DB_HOST = host
self.__DB_PORT = int(port)
self.__DB_PREFIX = prefix
self.__DB_LOCAL = 1
return self
@singleton
class nosqlMemcachedCtr():
def __init__(self):
pass
def getInstanceBySid(self, sid = 0):
instance = nosqlMemcached()
return instance
def getItems(self, args):
sid = args['sid']
mem_instance = self.getInstanceBySid(sid).conn()
if mem_instance is False:
return mw.returnData(False,'无法链接')
result = {}
m_items = mem_instance.stats('items')
item_no = []
for i in m_items:
item_match = b'items:(\d*?):number'
item_match_re = re.search(item_match, i)
if item_match_re:
v = item_match_re.groups()[0].strip()
v_str = v.decode()
if not v_str in item_no:
item_no.append(v_str)
if len(item_no) == 0:
item_no = [0]
result['items'] = item_no
return mw.returnData(True,'ok', result)
def getKeyList(self, args):
sid = args['sid']
mem_instance = self.getInstanceBySid(sid).conn()
if mem_instance is False:
return mw.returnData(False,'无法链接')
p = 1
size = 10
if 'p' in args:
p = args['p']
if 'size' in args:
size = args['size']
item_id = args['item_id']
if item_id == '0':
return mw.returnData(False,'ok')
m_items = mem_instance.stats('items')
item_key = 'items:%s:number' % item_id
item_key_b = item_key.encode("utf-8")
m_items_v = m_items[item_key_b]
start = (p-1)*size
end = start+size
if end > m_items_v:
end = m_items_v
all_key = mem_instance.stats('cachedump', str(item_id) , str(0))
# print(all_key)
all_key_list = []
cur_time_t = time.time()
for k in all_key:
t = {}
t['k'] = k.decode("utf-8")
v = all_key[k].decode("utf-8")
v = v.strip('[').strip(']').split(';')
t['s'] = v[0]
cur_time = v[1].strip().split(' ')[0]
if int(cur_time) != 0 :
t['t'] = int(cur_time) - int(cur_time_t)
else:
t['t'] = 0
all_key_list.append(t)
# print(len(all_key_list))
# print(start,end)
return_all_key = all_key_list[start:end]
for x in range(len(return_all_key)):
v = mem_instance.get(return_all_key[x]['k'])
return_all_key[x]['v'] = v.decode('utf-8')
result = {}
result['list'] = return_all_key
result['p'] = p
page_args = {}
page_args['count'] = len(all_key_list)
page_args['tojs'] = 'memcachedGetKeyList'
page_args['p'] = p
page_args['row'] = size
result['page'] = mw.getPage(page_args)
return mw.returnData(True,'ok', result)
def delVal(self, args):
sid = args['sid']
mem_instance = self.getInstanceBySid(sid).conn()
if mem_instance is False:
return mw.returnData(False,'无法链接')
key = args['key']
mem_instance.delete(key)
return mw.returnData(True,'删除成功!')
def setKv(self, args):
sid = args['sid']
mem_instance = self.getInstanceBySid(sid).conn()
if mem_instance is False:
return mw.returnData(False,'无法链接')
key = args['key']
val = args['val']
endtime = args['endtime']
mem_instance.set(key, val, int(endtime))
return mw.returnData(True,'设置成功!')
def clear(self, args):
sid = args['sid']
mem_instance = self.getInstanceBySid(sid).conn()
if mem_instance is False:
return mw.returnData(False,'无法链接')
mem_instance.flush_all()
return mw.returnData(True,'清空成功!')
# ---------------------------------- run ----------------------------------
# 获取 memcached 列表
def get_items(args):
t = nosqlMemcachedCtr()
return t.getItems(args)
def get_key_list(args):
t = nosqlMemcachedCtr()
return t.getKeyList(args)
def del_val(args):
t = nosqlMemcachedCtr()
return t.delVal(args)
def set_kv(args):
t = nosqlMemcachedCtr()
return t.setKv(args)
def clear(args):
t = nosqlMemcachedCtr()
return t.clear(args)
# 测试
def test(args):
sid = args['sid']
t = nosqlMemcachedCtr()
print(t.get_options())
print("test")
return 'ok'
# ---------------------------------- run ----------------------------------
-351
View File
@@ -1,351 +0,0 @@
# coding:utf-8
import sys
import io
import os
import time
import re
import pymongo
import json
import yaml
from bson.objectid import ObjectId
from bson.json_util import dumps
sys.path.append(os.getcwd() + "/class/core")
import mw
def singleton(cls):
_instance = {}
def inner():
if cls not in _instance:
_instance[cls] = cls()
return _instance[cls]
return inner
def getPluginName():
return 'mongodb'
def getPluginDir():
return mw.getPluginDir() + '/' + getPluginName()
def getServerDir():
return mw.getServerDir() + '/' + getPluginName()
def getConf():
path = getServerDir() + "/mongodb.conf"
return path
def getConfTpl():
path = getPluginDir() + "/config/mongodb.conf"
return path
def pSqliteDb(dbname='users'):
file = getServerDir() + '/mongodb.db'
name = 'mongodb'
sql_file = getPluginDir() + '/config/mongodb.sql'
import_sql = mw.readFile(sql_file)
# print(sql_file,import_sql)
md5_sql = mw.md5(import_sql)
import_sign = False
save_md5_file = getServerDir() + '/import_mongodb.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 getConfigData():
cfg = getConf()
# print(cfg)
config_data = mw.readFile(cfg)
try:
config = yaml.safe_load(config_data)
except:
config = {
"systemLog": {
"destination": "file",
"logAppend": True,
"path": mw.getServerDir()+"/mongodb/log/mongodb.log"
},
"storage": {
"dbPath": mw.getServerDir()+"/mongodb/data",
"directoryPerDB": True,
"journal": {
"enabled": True
}
},
"processManagement": {
"fork": True,
"pidFilePath": mw.getServerDir()+"/mongodb/log/mongodb.pid"
},
"net": {
"port": 27017,
"bindIp": "0.0.0.0"
},
"security": {
"authorization": "disabled",
"javascriptEnabled": False
}
}
return config
def getConfPort():
data = getConfigData()
return data['net']['port']
def getConfAuth():
data = getConfigData()
return data['security']['authorization']
@singleton
class nosqlMongodb():
__DB_PASS = None
__DB_USER = None
__DB_PORT = 6379
__DB_HOST = '127.0.0.1'
__DB_CONN = None
__DB_ERR = None
__DB_LOCAL = None
def __init__(self):
self.__config = self.get_options(None)
def mgdb_conn(self):
if self.__DB_HOST in ['127.0.0.1', 'localhost']:
mgdb_path = "{}/mongodb".format(mw.getServerDir())
if not os.path.exists(mgdb_path): return False
if not self.__DB_LOCAL:
self.__DB_PORT = int(self.__config['port'])
auth = getConfAuth()
port = getConfPort()
mg_root = pSqliteDb('config').where('id=?', (1,)).getField('mg_root')
# print(auth,self.__DB_HOST,port, self.__DB_PASS)
try:
if auth == 'disabled':
self.__DB_CONN = pymongo.MongoClient(host=self.__DB_HOST, port=port, directConnection=True)
else:
self.__DB_CONN = pymongo.MongoClient(host=self.__DB_HOST, port=port, directConnection=True, username='root',password=mg_root)
self.__DB_CONN.admin.command('ping')
return self.__DB_CONN
except pymongo.errors.ConnectionFailure:
return False
except Exception as e:
# print(e)
self.__DB_ERR = mw.getTracebackInfo()
return False
# 获取配置项
def get_options(self, get=None):
result = {}
mgdb_content = mw.readFile("{}/mongodb/mongodb.conf".format(mw.getServerDir()))
if not mgdb_content: return False
keys = ["bind_ip", "port"]
result['host'] = '127.0.0.1'
rep = 'port\s*=\s*(.*)'
ip_re = re.search(rep, mgdb_content)
if ip_re:
result['port'] = int(ip_re.groups()[0].strip())
else:
result['port'] = 27017
return result
def set_host(self, host, port, name, username, password, prefix=''):
self.__DB_HOST = host
self.__DB_PORT = int(port)
self.__DB_NAME = name
if self.__DB_NAME: self.__DB_NAME = str(self.__DB_NAME)
self.__DB_USER = str(username)
self._USER = str(username)
self.__DB_PASS = str(password)
self.__DB_PREFIX = prefix
self.__DB_LOCAL = 1
return self
@singleton
class nosqlMongodbCtr():
def __init__(self):
pass
def getInstanceBySid(self, sid = 0):
instance = nosqlMongodb()
return instance
def getDbList(self, args):
sid = args['sid']
mgdb_instance = self.getInstanceBySid(sid).mgdb_conn()
if mgdb_instance is False:
return mw.returnData(False,'无法链接')
result = {}
doc_list = mgdb_instance.list_database_names()
rlist = []
for x in doc_list:
if not x in ['admin', 'config', 'local']:
rlist.append(x)
result['list'] = rlist
return mw.returnData(True,'ok', result)
def getCollectionsList(self, args):
sid = args['sid']
name = args['name']
mgdb_instance = self.getInstanceBySid(sid).mgdb_conn()
if mgdb_instance is False:
return mw.returnData(False,'无法链接.')
result = {}
collections = mgdb_instance[name].list_collection_names()
result['collections'] = collections
return mw.returnData(True,'ok', result)
def getDataList(self, args):
sid = args['sid']
db = args['db']
collection = args['collection']
p = 1
size = 10
if 'p' in args:
p = args['p']
if 'size' in args:
size = args['size']
mgdb_instance = self.getInstanceBySid(sid).mgdb_conn()
if mgdb_instance is False:
return mw.returnData(False,'无法链接')
db_instance = mgdb_instance[db]
collection_instance = db_instance[collection]
start_index = (p - 1) * size
end_index = p * size
args_where = args['where']
where = {}
if 'field' in args_where:
mg_field = args_where['field']
if mg_field == '_id':
mg_value = ObjectId(args_where['value'])
where[mg_field] = mg_value
else:
mg_value = args_where['value']
where[mg_field] = re.compile(mg_value)
# print(where)
result = collection_instance.find(where).skip(start_index).limit(size).sort('_id',-1)
count = collection_instance.count_documents(where)
d = []
for document in result:
d.append(document)
doc_str_json = dumps(d)
result = json.loads(doc_str_json)
page_args = {}
page_args['count'] = count
page_args['tojs'] = 'mongodbDataList'
page_args['p'] = p
page_args['row'] = size
rdata = {}
rdata['page'] = mw.getPage(page_args)
rdata['list'] = result
rdata['count'] = count
rdata['soso_field'] = ''
if 'field' in args_where:
rdata['soso_field'] = args_where['field']
return mw.returnData(True,'ok', rdata)
def delById(self,args):
sid = args['sid']
db = args['db']
collection = args['collection']
mgdb_instance = self.getInstanceBySid(sid).mgdb_conn()
if mgdb_instance is False:
return mw.returnData(False,'无法链接')
db_instance = mgdb_instance[db]
collection_instance = db_instance[collection]
_id = args['_id']
result = collection_instance.delete_one({"_id": ObjectId(_id)})
return mw.returnData(True,'文档删除【%d】个成功!' % result.deleted_count)
# ---------------------------------- run ----------------------------------
# 获取 mongodb databases 列表
def get_db_list(args):
t = nosqlMongodbCtr()
return t.getDbList(args)
# 获取 mongodb collections 列表
def get_collections_list(args):
t = nosqlMongodbCtr()
return t.getCollectionsList(args)
def get_data_list(args):
t = nosqlMongodbCtr()
return t.getDataList(args)
def set_kv(args):
t = nosqlMongodbCtr()
return t.setKv(args)
def del_val(args):
t = nosqlMongodbCtr()
return t.delVal(args)
def batch_del_val(args):
t = nosqlMongodbCtr()
return t.batchDelVal(args)
def clear_flushdb(args):
t = nosqlMongodbCtr()
return t.clearFlushDB(args)
def del_by_id(args):
t = nosqlMongodbCtr()
return t.delById(args)
# 测试
def test(args):
sid = args['sid']
t = nosqlMongodbCtr()
print(t.get_options())
print("test")
return 'ok'
-347
View File
@@ -1,347 +0,0 @@
# coding:utf-8
import sys
import io
import os
import time
import re
import redis
sys.path.append(os.getcwd() + "/class/core")
import mw
# def getPluginName():
# return 'data_query'
def singleton(cls):
_instance = {}
def inner():
if cls not in _instance:
_instance[cls] = cls()
return _instance[cls]
return inner
@singleton
class nosqlRedis():
__DB_PASS = None
__DB_USER = None
__DB_PORT = 6379
__DB_HOST = '127.0.0.1'
__DB_CONN = None
__DB_ERR = None
__DB_LOCAL = None
def __init__(self):
self.__config = self.get_options(None)
def redis_conn(self, db_idx=0):
if self.__DB_HOST in ['127.0.0.1', 'localhost']:
redis_path = "{}/redis".format(mw.getServerDir())
if not os.path.exists(redis_path): return False
if not self.__DB_LOCAL:
self.__DB_PASS = self.__config['requirepass']
self.__DB_PORT = int(self.__config['port'])
# print(self.__DB_HOST,self.__DB_PORT, self.__DB_PASS)
try:
redis_pool = redis.ConnectionPool(host=self.__DB_HOST, port=self.__DB_PORT, password=self.__DB_PASS, db=db_idx, socket_timeout=3)
self.__DB_CONN = redis.Redis(connection_pool=redis_pool)
self.__DB_CONN.ping()
return self.__DB_CONN
except redis.exceptions.ConnectionError:
return False
except Exception:
self.__DB_ERR = mw.getTracebackInfo()
return False
# 获取配置项
def get_options(self, get=None):
result = {}
redis_conf = mw.readFile("{}/redis/redis.conf".format(mw.getServerDir()))
if not redis_conf: return False
keys = ["bind", "port", "timeout", "maxclients", "databases", "requirepass", "maxmemory"]
for k in keys:
v = ""
rep = "\n%s\s+(.+)" % k
group = re.search(rep, redis_conf)
if not group:
if k == "maxmemory":
v = "0"
if k == "maxclients":
v = "10000"
if k == "requirepass":
v = ""
else:
if k == "maxmemory":
v = int(group.group(1).strip("mb"))
else:
v = group.group(1)
result[k] = v
return result
def set_host(self, host, port, name, username, password, prefix=''):
self.__DB_HOST = host
self.__DB_PORT = int(port)
self.__DB_NAME = name
if self.__DB_NAME: self.__DB_NAME = str(self.__DB_NAME)
self.__DB_USER = str(username)
self._USER = str(username)
self.__DB_PASS = str(password)
self.__DB_PREFIX = prefix
self.__DB_LOCAL = 1
return self
@singleton
class nosqlRedisCtr():
def __init__(self):
pass
def getInstanceBySid(self, sid = 0):
instance = nosqlRedis()
return instance
def getList(self, args):
sid = args['sid']
redis_instance = self.getInstanceBySid(sid).redis_conn(0)
if redis_instance is False:
return mw.returnData(False,'无法链接')
redis_info = redis_instance.info()
is_cluster = redis_info.get("cluster_enabled", 0)
if is_cluster != 0:
return mw.returnData(False, "当前不支持连接redis集群!")
db_num = 16
if sid != 0:
db_num = 1000
result = []
for x in range(0, db_num):
data = {}
data['id'] = x
data['name'] = 'DB{}'.format(x)
try:
redis_instance = self.getInstanceBySid(sid).redis_conn(x)
data['keynum'] = redis_instance.dbsize()
result.append(data)
except:
break
return mw.returnData(True,'ok', result)
def getDbKeyList(self, args):
p = 1
size = 10
if not 'sid' in args:
return mw.returnData(False, "缺少参数!sid")
if 'p' in args:
p = args['p']
if p < 1:
p = 1
if p > 10:
p = 10
if 'size' in args:
size = args['size']
sid = args['sid']
idx = args['idx']
search = '*'
if 'search' in args and args['search'] != '':
search = args['search']
redis_instance = self.getInstanceBySid(sid).redis_conn(idx)
total = redis_instance.dbsize()
if search != '*':
keylist = redis_instance.keys(search)
total = len(keylist)
else:
keys = redis_instance.scan(cursor=0, match="{}".format(search), count=p*size)
keylist = keys[1]
slist = keylist[(p - 1) * size:p*size]
items = []
for key in slist:
item = {}
try:
item['name'] = key.decode()
except Exception as e:
item['name'] = str(key)
item['endtime'] = redis_instance.ttl(key)
item['type'] = redis_instance.type(key).decode()
if item['type'] == 'string':
try:
item['val'] = redis_instance.get(key).decode()
except Exception as e:
item['val'] = str(redis_instance.get(key))
elif item['type'] == 'hash':
if redis_instance.hlen(key) > 500:
item['val'] = "数据量过大无法显示!共 {} 条".format(redis_instance.hlen(key))
else:
item['val'] = str(redis_instance.hgetall(key))
elif item['type'] == 'list':
if redis_instance.llen(key) > 500:
item['val'] = "数据量过大无法显示!共 {} 条".format(redis_instance.llen(key))
else:
item['val'] = str(redis_instance.lrange(key, 0, -1))
elif item['type'] == 'set':
if redis_instance.scard(key) > 500:
item['val'] = "数据量过大无法显示!共 {} 条".format(redis_instance.scard(key))
else:
item['val'] = str(redis_instance.smembers(key))
elif item['type'] == 'zset':
if redis_instance.zcard(key) > 500:
item['val'] = "数据量过大无法显示!共 {} 条".format(redis_instance.zcard(key))
else:
item['val'] = str(redis_instance.zrange(key, 0, -1, withscores=True))
else:
item['val'] = ''
try:
item['len'] = redis_instance.strlen(key)
except:
item['len'] = len(item['val'])
items.append(item)
page_args = {}
page_args['count'] = total
page_args['tojs'] = 'redisGetKeyList'
page_args['p'] = p
page_args['row'] = size
rdata = {}
rdata['page'] = mw.getPage(page_args)
rdata['data'] = items
return mw.returnData(True,'ok',rdata)
def setKv(self,args):
if not 'name' in args:
return mw.returnData(False, "缺少参数!name")
if not 'val' in args:
return mw.returnData(False, "缺少参数!val")
if not 'idx' in args:
return mw.returnData(False, "缺少参数!idx")
sid = args['sid']
idx = args['idx']
name = args["name"]
val = args["val"]
endtime = args["endtime"]
redis_instance = self.getInstanceBySid(sid).redis_conn(idx)
redis_info = redis_instance.info()
if redis_info['role'] == 'slave':
return mw.returnData(False,'从库不能写操作!')
if endtime != '0':
redis_instance.set(name, val, int(endtime))
else:
redis_instance.set(name, val)
return mw.returnData(True,'操作成功')
def delVal(self, args):
sid = args['sid']
idx = args['idx']
name = args["name"]
redis_instance = self.getInstanceBySid(sid).redis_conn(idx)
redis_info = redis_instance.info()
if redis_info['role'] == 'slave':
return mw.returnData(False,'从库不能删除操作!')
redis_instance.delete(name)
return mw.returnData(True,'操作成功')
def batchDelVal(self, args):
sid = args['sid']
idx = args['idx']
keys = args["keys"]
redis_instance = self.getInstanceBySid(sid).redis_conn(idx)
redis_info = redis_instance.info()
if redis_info['role'] == 'slave':
return mw.returnData(False,'从库不能删除操作!')
for k in keys:
redis_instance.delete(k)
return mw.returnData(True,'操作成功')
def clearFlushDB(self, args):
sid = args['sid']
idxs = args['idxs']
for idx in idxs:
redis_instance = self.getInstanceBySid(sid).redis_conn(idx)
redis_info = redis_instance.info()
if redis_info['role'] == 'slave':
return mw.returnData(False,'从库不能清空操作!')
redis_instance.flushdb()
return mw.returnData(True,'操作成功')
# ---------------------------------- run ----------------------------------
# 获取 redis databases 列表
def get_list(args):
t = nosqlRedisCtr()
return t.getList(args)
# 获取 redis key 列表
def get_dbkey_list(args):
t = nosqlRedisCtr()
return t.getDbKeyList(args)
def set_kv(args):
t = nosqlRedisCtr()
return t.setKv(args)
def del_val(args):
t = nosqlRedisCtr()
return t.delVal(args)
def batch_del_val(args):
t = nosqlRedisCtr()
return t.batchDelVal(args)
def clear_flushdb(args):
t = nosqlRedisCtr()
return t.clearFlushDB(args)
# 测试
def test(args):
sid = args['sid']
t = nosqlRedis()
print(t.get_options())
print("test")
return 'ok'
# ---------------------------------- run ----------------------------------
-301
View File
@@ -1,301 +0,0 @@
# coding:utf-8
import sys
import io
import os
import time
import re
import pymongo
import json
sys.path.append(os.getcwd() + "/class/core")
import mw
def singleton(cls):
_instance = {}
def inner():
if cls not in _instance:
_instance[cls] = cls()
return _instance[cls]
return inner
@singleton
class nosqlMySQL():
__DB_PASS = None
__DB_USER = None
__DB_PORT = 3306
__DB_HOST = '127.0.0.1'
__DB_CONN = None
__DB_ERR = None
__DB_SOCKET = None
__DB_LOCAL = None
def __init__(self):
self.__config = self.get_options(None)
def conn(self):
if self.__DB_HOST in ['127.0.0.1', 'localhost']:
my_path = "{}/mysql".format(mw.getServerDir())
if not os.path.exists(my_path): return False
if not self.__DB_LOCAL:
# print(self.__config)
self.__DB_PORT = int(self.__config['port'])
self.__DB_USER = self.__config['username']
self.__DB_PASS = self.__config['password']
self.__DB_SOCKET = self.__config['socket']
try:
db = mw.getMyORM()
db.setPort(self.__DB_PORT)
db.setPwd(self.__DB_PASS)
db.setUser(self.__DB_USER)
if self.__DB_SOCKET != '':
db.setSocket(self.__DB_SOCKET)
return db
except Exception:
self.__DB_ERR = mw.get_error_info()
return False
def sqliteDb(self,dbname='databases'):
my_root_path = mw.getServerDir() +'/mysql'
name = 'mysql'
conn = mw.M(dbname).dbPos(my_root_path, name)
return conn
# 获取配置项
def get_options(self, get=None):
result = {}
my_cnf_path = "{}/mysql/etc/my.cnf".format(mw.getServerDir())
my_content = mw.readFile(my_cnf_path)
if not my_content: return False
mysql_pass = self.sqliteDb('config').where('id=?', (1,)).getField('mysql_root')
result['password'] = mysql_pass
result['username'] = 'root'
keys = ["bind_ip", "port"]
result['host'] = '127.0.0.1'
rep = 'port\s*=\s*(.*)'
port_re = re.search(rep, my_content)
if port_re:
result['port'] = int(port_re.groups()[0].strip())
else:
result['port'] = 3306
socket_rep = 'socket\s*=\s*(.*)'
socket_re = re.search(socket_rep, my_content)
if socket_re:
result['socket'] = socket_re.groups()[0].strip()
else:
result['socket'] = ''
return result
def set_host(self, host, port, name, username, password, prefix=''):
self.__DB_HOST = host
self.__DB_PORT = int(port)
self.__DB_NAME = name
if self.__DB_NAME: self.__DB_NAME = str(self.__DB_NAME)
self.__DB_USER = str(username)
self._USER = str(username)
self.__DB_PASS = str(password)
self.__DB_PREFIX = prefix
self.__DB_LOCAL = 1
return self
@singleton
class nosqlMySQLCtr():
def __init__(self):
pass
def getInstanceBySid(self, sid = 0):
instance = nosqlMySQL()
return instance
def getDbList(self, args):
sid = args['sid']
my_instance = self.getInstanceBySid(sid).conn()
if my_instance is False:
return mw.returnData(False,'无法链接')
result = {}
db_list = my_instance.query('show databases')
rlist = []
for x in db_list:
if not x['Database'] in ['information_schema', 'mysql', 'performance_schema','sys']:
rlist.append(x['Database'])
result['list'] = rlist
return mw.returnData(True,'ok', result)
def getTableList(self, args):
sid = args['sid']
db = args['db']
my_instance = self.getInstanceBySid(sid).conn()
if my_instance is False:
return mw.returnData(False,'无法链接')
sql = "select * from information_schema.tables where table_schema = '"+db+"'"
table_list = my_instance.query(sql)
# print(table_list)
rlist = []
for x in table_list:
# print(x['TABLE_NAME'])
rlist.append(x['TABLE_NAME'])
result = {}
result['list'] = rlist
return mw.returnData(True,'ok', result)
def getDataList(self, args):
sid = args['sid']
db = args['db']
table = args['table']
p = 1
size = 10
if 'p' in args:
p = args['p']
if 'size' in args:
size = args['size']
start_index = (p - 1) * size
args_where = {}
where_sql = ''
if 'where' in args:
args_where = args['where']
if 'field' in args_where:
if args_where['field'] == 'id' or args_where['field'].find('id')>-1:
where_sql = ' where '+args_where['field'] + " = '"+args_where['value']+"' "
else:
where_sql = ' where '+args_where['field'] + " like '%"+args_where['value']+"%' "
my_instance = self.getInstanceBySid(sid).conn()
if my_instance is False:
return mw.returnData(False,'无法链接')
my_instance.setDbName(db)
sql = 'select count(*) as num from ' + table + where_sql
# print(sql)
count_result = my_instance.query(sql)
count = count_result[0]['num']
sql = 'select * from ' + table + where_sql + ' limit '+str(start_index)+',10';
# print(sql)
result = my_instance.query(sql)
for i in range(len(result)):
for f in result[i]:
result[i][f] = str(result[i][f])
# print(result)
page_args = {}
page_args['count'] = count
page_args['tojs'] = 'mysqlGetDataList'
page_args['p'] = p
page_args['row'] = size
rdata = {}
rdata['page'] = mw.getPage(page_args)
rdata['list'] = result
rdata['count'] = count
rdata['soso_field'] = ''
if 'field' in args_where:
rdata['soso_field'] = args_where['field']
return mw.returnData(True,'ok', rdata)
def showProcessList(self,args):
sql = 'show processlist';
sid = args['sid']
my_instance = self.getInstanceBySid(sid).conn()
if my_instance is False:
return mw.returnData(False,'无法链接')
result = my_instance.query(sql)
rdata = {}
rdata['list'] = result
return mw.returnData(True,'ok', rdata)
def showStatusList(self,args):
sql = 'show status';
sid = args['sid']
my_instance = self.getInstanceBySid(sid).conn()
if my_instance is False:
return mw.returnData(False,'无法链接')
result = my_instance.query(sql)
rdata = {}
rdata['list'] = result
return mw.returnData(True,'ok', rdata)
def showStatsList(self, args):
sql = "show status like 'Com_%'";
sid = args['sid']
my_instance = self.getInstanceBySid(sid).conn()
if my_instance is False:
return mw.returnData(False,'无法链接')
result = my_instance.query(sql)
rdata = {}
rdata['list'] = result
return mw.returnData(True,'ok', rdata)
# ---------------------------------- run ----------------------------------
# 获取 mysql 列表
def get_db_list(args):
t = nosqlMySQLCtr()
return t.getDbList(args)
# 获取 mysql 列表
def get_table_list(args):
t = nosqlMySQLCtr()
return t.getTableList(args)
def get_data_list(args):
t = nosqlMySQLCtr()
return t.getDataList(args)
def get_proccess_list(args):
t = nosqlMySQLCtr()
return t.showProcessList(args)
def get_status_list(args):
t = nosqlMySQLCtr()
return t.showStatusList(args)
def get_stats_list(args):
t = nosqlMySQLCtr()
return t.showStatsList(args)
# 测试
def test(args):
sid = args['sid']
t = nosqlMySQLCtr()
print(t.get_options())
print("test")
return 'ok'
-48
View File
@@ -1,48 +0,0 @@
.main-content .safe {
position: relative;
}
.mask_layer {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgb(255 255 255 / 60%);
z-index: 9998;
}
.mask_layer .prompt_description {
width: 350px;
height: 60px;
line-height: 60px;
text-align: center;
position: absolute;
left: 50%;
top: 50%;
margin-top: -25px;
margin-left: -175px;
background: #fff;
box-shadow: 1px 1px 50px rgb(0 0 0 / 30%);
border: 1px solid #eee;
border-radius: 2px;
font-size: 13px;
}
#mongodb_search xm-select {
min-height: 30px;
line-height: 30px;
}
#mongodb_search xm-select .xm-label .xm-label-block{
height: 20px;
line-height: 20px;
}
#mongodb td:last-child, th:last-child {
width: 44px;
position:sticky;
right:0; /* 首行永远固定在左侧 */
z-index:1;
background-color:white;
}
-11
View File
@@ -1,11 +0,0 @@
/* menu start */
.menu .current .menu_plugin_data_query:hover {
background-image: url("/plugins/file?name=data_query&f=ico.png");
}
.menu .menu_plugin_data_query {
background-image: url("/plugins/file?name=data_query&f=ico.png");
}
/* menu end */
-294
View File
@@ -1,294 +0,0 @@
<div class="main-content" style="min-height: 389px;">
<div class="container-fluid" style="padding-bottom: 50px;">
<div id="cutTab" class="pos-box bgw mtb15" style="height:45px">
<div class="tab-list">
<div class="tabs-item active" data-name="redis">Redis</div>
<div class="tabs-item" data-name="mongodb">Mongodb</div>
<div class="tabs-item" data-name="memcached">Memcached</div>
<div class="tabs-item" data-name="mysql">MySQL</div>
</div>
</div>
<div class="bgw mtb15 pd15 tab-view-box firewall-tab-view safe" style="overflow: hidden;">
<div class="mask_layer" style="display:none;">
<div class="prompt_description">当前未安装本地服务器,<a class="btlink install_server" href="/soft">点击安装</a></div>
</div>
<!-- mysql start -->
<!-- show w-full -->
<div class="tab-con hide" id="mysql" style="padding: 0px;">
<div class="col-md-12" style="padding: 0px;overflow: hidden;">
<div class="tootls_group tootls_top">
<div class="pull-left">
<div class="server_list" style="float:left;">
<select name="sid" class="bt-input-text c5 mr5">
<option value="0">本地服务器</option>
</select>
</div>
<div class="mysql_db_list" style="float:left;">
<select name="mysql_db" class="bt-input-text c5 mr5">
<option value="0">数据库空</option>
</select>
</div>
<div class="mysql_table_list" style="float:left;">
<select name="mysql_table" class="bt-input-text c5 mr5">
<option value="0">数据表空</option>
</select>
</div>
<div class="field" style="float:left;">
<select name="mysql_field_key" class="bt-input-text c5 mr5" style="width:80px;">
<option value="0">无字段</option>
</select>
</div>
<div class="value" style="float:left;">
<input name="mysql_field_value" type="text" class="bt-input-text" placeholder="请输入查找内容">
</div>
<button type="button" class="mysql_find btn btn-success btn-sm mr5 ml5" style="float:right;">
<span>查找</span>
</button>
</div>
</div>
<div style="position: relative;">
<div id="mysql_table" class="mysql_table divtable mtb10">
<table class="table table-hover">
<thead style="position: relative;z-index: 1;">
<tr>
<th><span>编号</span></th>
<th><span>操作类型</span></th>
<th><span>详情</span></th>
</tr>
</thead>
<tbody></tbody>
</table>
</div>
</div>
<div class="mysql_list_page page">
<div><span class="Pcurrent">0</span><span class="Pcount">共0条</span></div>
</div>
</div>
<div class="col-md-12" style="padding-left: 3px;">
<div id="mysql_list_tab" style="padding-top:0px;">
<div class="tab-nav">
<span data-name="proccess" class="on">进程</span>
<span data-name="status" class="">状态</span>
<span data-name="stats" class="">统计</span>
</div>
</div>
<div style="position: relative;">
<div id="mysql_ot_table" class="divtable">
<table class="table table-hover">
<thead style="position: relative;z-index: 1;">
<tr>
<th><span>编号</span></th>
<th><span>操作类型</span></th>
<th><span>详情</span></th>
</tr>
</thead>
<tbody></tbody>
</table>
</div>
</div>
</div>
</div>
<!-- mysql end -->
<!-- mongodb start -->
<!-- show w-full -->
<div class="tab-con hide" id="mongodb" style="padding: 0px;">
<div class="tootls_group tootls_top">
<div class="pull-left">
<div class="field" style="float:left;">
<select name="mongodb_field_key" class="bt-input-text c5 mr5" style="width:80px;">
<option value="0">无字段</option>
</select>
</div>
<div class="value" style="float:left;">
<input name="mongodb_field_value" type="text" class="bt-input-text" placeholder="请输入查找内容">
</div>
<button type="button" class="mongodb_find btn btn-success btn-sm mr5 ml5" style="float:right;">
<span>查找</span>
</button>
</div>
<div class="pull-right" style="padding-top: 5px;">
<div class="server_list" style="float:left;">
<select name="sid" class="bt-input-text c5 mr5">
<option value="0">本地服务器</option>
</select>
</div>
<div class="db_list" style="float:left;">
<select name="db" class="bt-input-text c5 mr5">
<option value="0">空</option>
</select>
</div>
<div class="search" id="mongodb_search"></div>
</div>
</div>
<div style="position: relative;">
<div id="mongodb_table" class="mongodb_table divtable mtb10">
<table class="table table-hover">
<thead style="position: relative;z-index: 1;">
<tr>
<th><span>编号</span></th>
<th><span>操作类型</span></th>
<th><span>详情</span></th>
<th style="text-align:right;"><span>操作</span></th>
</tr>
</thead>
<tbody></tbody>
</table>
</div>
</div>
<div class="mongodb_list_page page">
<div><span class="Pcurrent">0</span><span class="Pcount">共0条</span></div>
</div>
</div>
<!-- mongodb end -->
<!-- redis start -->
<div class="tab-con show w-full" id="redis" style="padding: 0px;">
<div id="redis_ctr" style="position: relative;">
<div class="tootls_group tootls_top">
<div class="pull-left">
<button id="redis_add_key" type="button" class="refresh btn btn-success btn-sm mr5">
<span>添加key</span>
</button>
<button id="redis_clear_all" type="button" class="clear btn btn-default btn-sm mr5" style="border-right: 1px solid #ccc;padding-right: 20px;">
<span>清空数据库</span>
</button>
</div>
</div>
<div id="redis_list_tab" style="padding-top:10px;">
<div class="tab-nav">
<!-- <span data-id="0" class="on">DB0(0)</span> -->
</div>
</div>
<div class="tootls_group tootls_top">
<div class="pull-left"></div>
<div class="pull-right" style="padding-top: 5px;">
<div class="search">
<input id="redis_ksearch" type="text" class="search_input" placeholder="请输入键名称">
<span id="redis_ksearch_span" class="glyphicon glyphicon-search" aria-hidden="true"></span>
</div>
</div>
</div>
<div class="divtable mtb10 redis_table_content">
<table class="table table-hover">
<thead style="position: relative;z-index: 1;">
<tr>
<th width="30"><input type="checkbox" onclick="checkSelect();"></th>
<th><span>键</span></th>
<th><span>值</span></th>
<th><span>数据类型</span></th>
<th><span>数据长度</span></th>
<th><span>有效期</span></th>
<th style="text-align:right;"><span>操作</span></th>
</tr>
</thead>
<tbody></tbody>
</table>
</div>
</div>
<div class="tootls_group tootls_bottom">
<div class="pull-left">
<button id="redis_batch_del" type="button" class="btn btn-sm set_batch_option bt-disabled btn-default">批量删除</button>
</div>
<div class="pull-right">
<div class="page redis_list_page">
<div><span class="Pcurrent">0</span><span class="Pcount">共0条</span></div>
</div>
</div>
</div>
</div>
<!-- redis end -->
<!-- memcached start -->
<div class="tab-con hide" id="memcached" style="padding: 0px;">
<div id="memcached_ctr" style="position: relative;">
<div class="tootls_group tootls_top">
<div class="pull-left">
<button id="memcached_add_key" type="button" class="refresh btn btn-success btn-sm mr5">
<span>添加key</span>
</button>
<button id="memcached_clear_all" type="button" class="clear btn btn-default btn-sm mr5" style="border-right: 1px solid #ccc;padding-right: 20px;">
<span>清空</span>
</button>
</div>
<div class="pull-right" style="padding-top: 5px;">
<div class="item_list" style="float:left;">
<select name="item" class="bt-input-text c5 mr5">
<option value="0">空</option>
</select>
</div>
<div class="search">
<input id="memcached_ksearch" type="text" class="search_input" placeholder="请输入键名称">
<span id="memcached_ksearch_span" class="glyphicon glyphicon-search" aria-hidden="true"></span>
</div>
</div>
</div>
<div class="divtable mtb10 memcached_table_content">
<table class="table table-hover">
<thead style="position: relative;z-index: 1;">
<tr>
<th width="30"><input type="checkbox" onclick="checkSelect();"></th>
<th><span>键</span></th>
<th><span>值</span></th>
<th><span>数据长度</span></th>
<th><span>有效期</span></th>
<th style="text-align:right;"><span>操作</span></th>
</tr>
</thead>
<tbody></tbody>
</table>
</div>
</div>
<div class="tootls_group tootls_bottom">
<div class="pull-left">
<button id="memcached_batch_del" type="button" class="btn btn-sm set_batch_option bt-disabled btn-default">批量删除</button>
</div>
<div class="pull-right">
<div class="page memcached_list_page">
<div><span class="Pcurrent">0</span><span class="Pcount">共0条</span></div>
</div>
</div>
</div>
</div>
<!-- memcached end -->
</div>
</div>
</div>
File diff suppressed because it is too large Load Diff
-58
View File
@@ -1,58 +0,0 @@
<style type="text/css">
.docker-sub {
border-bottom: 1px solid #ccc;
height: 30px;
line-height: 30px;
margin-bottom: 15px;
}
.docker-sub .on {
border-bottom: 2px solid #20a53a;
color: #20a53a;
font-weight: bold;
font-family: "宋体";
}
.docker-sub span {
display: inline-block;
font-size: 14px;
height: 30px;
padding: 0 25px;
cursor: pointer;
}
.docker-environments {
resize: none;
width: 330px;
height: 80px;
line-height: 20px;
padding: 5px 8px;
border: 1px solid #ddd;
font-size: 12px;
}
</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('docker');">服务</p>
<p onclick="pluginInitD('docker');">自启动</p>
<p onclick="dockerConList();">容器列表</p>
<p onclick="dockerImageList();">镜像列表</p>
<p onclick="dockerImageOutput();">镜像导出</p>
<p onclick="dockerIpList();">IP地址池</p>
<p onclick="repoList();">仓库</p>
</div>
<div class="bt-w-con pd15">
<div class="soft-man-con"></div>
</div>
</div>
</div>
<script type="text/javascript">
resetPluginWinWidth(800);
resetPluginWinHeight(400);
$.getScript( "/plugins/file?name=docker&f=js/docker.js", function(){
pluginService('docker', $('.plugin_version').attr('version'));
});
</script>
-860
View File
@@ -1,860 +0,0 @@
# coding:utf-8
import sys
import io
import os
import time
import re
import json
sys.path.append(os.getcwd() + "/class/core")
import mw
import docker
app_debug = False
if mw.isAppleSystem():
app_debug = True
def getDClient():
try:
client = docker.from_env()
except Exception as e:
client = docker.DockerClient(
base_url='unix:///Users/midoks/.docker/run/docker.sock')
return client
def getPluginName():
return 'docker'
def getPluginDir():
return mw.getPluginDir() + '/' + getPluginName()
def getServerDir():
return mw.getServerDir() + '/' + getPluginName()
def getConf():
path = getServerDir() + "/redis.conf"
return path
def getConfTpl():
path = getPluginDir() + "/config/redis.conf"
return path
def getInitDTpl():
path = getPluginDir() + "/init.d/" + getPluginName() + ".tpl"
return path
def getArgs():
args = sys.argv[2:]
tmp = {}
args_len = len(args)
if args_len == 1:
t = args[0].strip('{').strip('}')
if t.strip() == '':
tmp = []
else:
t = t.split(':', 1)
tmp[t[0]] = t[1]
tmp[t[0]] = t[1]
elif args_len > 1:
for i in range(len(args)):
t = args[i].split(':', 1)
tmp[t[0]] = t[1]
return tmp
def checkArgs(self, data, ck=[]):
for i in range(len(ck)):
print(data[i])
if not ck[i] in data:
return (False, mw.returnJson(False, '参数:(' + ck[i] + ')没有!'))
return (True, mw.returnJson(True, 'ok'))
def status():
data = mw.execShell(
"ps -ef|grep docker |grep -v grep | grep -v python | grep -v mdserver-web | awk '{print $2}'")
if data[0] == '':
return 'stop'
return 'start'
def initDreplace():
return ''
def dockerOp(method):
file = initDreplace()
if not mw.isAppleSystem():
data = mw.execShell('systemctl ' + method + ' docker')
if data[1] == '':
return 'ok'
return data[1]
return 'fail'
def start():
return dockerOp('start')
def stop():
return dockerOp('stop')
def restart():
status = dockerOp('restart')
log_file = runLog()
mw.execShell("echo '' > " + log_file)
return status
def reload():
return dockerOp('reload')
def initdStatus():
if mw.isAppleSystem():
return "Apple Computer does not support"
shell_cmd = 'systemctl status ' + \
getPluginName() + ' | grep loaded | grep "enabled;"'
data = mw.execShell(shell_cmd)
if data[0] == '':
return 'fail'
return 'ok'
def initdInstall():
if mw.isAppleSystem():
return "Apple Computer does not support"
mw.execShell('systemctl enable ' + getPluginName())
return 'ok'
def initdUinstall():
if mw.isAppleSystem():
return "Apple Computer does not support"
mw.execShell('systemctl disable ' + getPluginName())
return 'ok'
# UTC时间转换为时间戳
def utc_to_local(utc_time_str, utc_format='%Y-%m-%dT%H:%M:%S'):
import pytz
import datetime
import time
local_tz = pytz.timezone('Asia/Chongqing')
local_format = "%Y-%m-%d %H:%M"
utc_dt = datetime.datetime.strptime(utc_time_str, utc_format)
local_dt = utc_dt.replace(tzinfo=pytz.utc).astimezone(local_tz)
time_str = local_dt.strftime(local_format)
return int(time.mktime(time.strptime(time_str, local_format)))
def conList():
c = getDClient()
clist = c.containers.list(all=True)
conList = []
for con in clist:
tmp = con.attrs
tmp['Created'] = utc_to_local(tmp['Created'].split('.')[0])
conList.append(tmp)
return conList
def conListData():
try:
clist = conList()
except Exception as e:
return mw.returnJson(False, '未开启Docker')
return mw.returnJson(True, 'ok', clist)
def dockerRemoveCon():
args = getArgs()
data = checkArgs(args, ['Hostname'])
if not data[0]:
return data[1]
Hostname = args['Hostname']
c = getDClient()
try:
conFind = c.containers.get(Hostname)
try:
path_list = conFind.attrs['GraphDriver'][
'Data']['LowerDir'].split(':')
for i in path_list:
mw.execShell('chattr -R -i %s' % i)
except:
pass
conFind.remove(force=True)
return mw.returnJson(True, '成功删除!')
except docker.errors.APIError as ex:
return mw.returnJson(False, '删除失败!' + str(ex))
def dockerLogCon():
args = getArgs()
data = checkArgs(args, ['Hostname'])
if not data[0]:
return data[1]
Hostname = args['Hostname']
c = getDClient()
try:
conFind = c.containers.get(Hostname)
if not conFind:
return mw.returnJson(False, 'The specified container does not exist!')
log = conFind.logs()
if not isinstance(log, str):
log = log.decode()
return mw.returnJson(True, log)
except docker.errors.APIError as ex:
return mw.returnJson(False, 'Get Logs failed')
def dockerRunCon():
# 启动容器
args = getArgs()
data = checkArgs(args, ['Hostname'])
if not data[0]:
return data[1]
Hostname = args['Hostname']
c = getDClient()
try:
conFind = c.containers.get(Hostname)
if not conFind:
return mw.returnJson(False, 'The specified container does not exist!')
conFind.start()
return mw.returnJson(True, '启动成功!')
except docker.errors.APIError as ex:
return mw.returnJson(False, '启动失败!' + str(ex))
def dockerStopCon():
# 停止容器
args = getArgs()
data = checkArgs(args, ['Hostname'])
if not data[0]:
return data[1]
Hostname = args['Hostname']
c = getDClient()
try:
conFind = c.containers.get(Hostname)
if not conFind:
return mw.returnJson(False, 'The specified container does not exist!')
conFind.stop()
return mw.returnJson(True, '停止成功!')
except docker.errors.APIError as ex:
return mw.returnJson(False, '停止失败!' + str(ex))
def dockerExec():
# 容器执行命令
args = getArgs()
data = checkArgs(args, ['Hostname'])
if not data[0]:
return data[1]
Hostname = args['Hostname']
debug_path = 'data/debug.pl'
if os.path.exists(debug_path):
return mw.returnJson(False, '开发模式不能进入!')
c = getDClient()
try:
conFind = c.containers.get(Hostname)
cmd = 'docker container exec -it %s /bin/sh' % Hostname
return mw.returnJson(True, cmd)
except docker.errors.APIError as ex:
return mw.returnJson(False, '连接失败!')
def imageList():
imageList = []
c = getDClient()
ilist = c.images.list()
for image in ilist:
tmp_attrs = image.attrs
if len(tmp_attrs['RepoTags']) == 1:
tmp_image = {}
tmp_image['Id'] = tmp_attrs['Id'].split(':')[1][:12]
tmp_image['RepoTags'] = tmp_attrs['RepoTags'][0]
tmp_image['Size'] = tmp_attrs['Size']
tmp_image['Labels'] = tmp_attrs['Config']['Labels']
tmp_image['Comment'] = tmp_attrs['Comment']
tmp_image['Created'] = utc_to_local(
tmp_attrs['Created'].split('.')[0])
imageList.append(tmp_image)
else:
for i in range(len(tmp_attrs['RepoTags'])):
tmp_image = {}
tmp_image['Id'] = tmp_attrs['Id'].split(':')[1][:12]
tmp_image['RepoTags'] = tmp_attrs['RepoTags'][i]
tmp_image['Size'] = tmp_attrs['Size']
tmp_image['Labels'] = tmp_attrs['Config']['Labels']
tmp_image['Comment'] = tmp_attrs['Comment']
tmp_image['Created'] = utc_to_local(
tmp_attrs['Created'].split('.')[0])
imageList.append(tmp_image)
imageList = sorted(imageList, key=lambda x: x['Created'], reverse=True)
return imageList
def dockerPull():
# pull Dockr 官方镜像
args = getArgs()
data = checkArgs(args, ['images'])
if not data[0]:
return data[1]
images = args['images']
if ':' in images:
pass
else:
images = images + ':latest'
c = getDClient()
try:
ret = c.images.pull(images)
if ret:
return mw.returnJson(True, '拉取成功!')
else:
return mw.returnJson(False, '拉取失败,请检查镜像名称或是否需要登录docker进行下载')
except:
ret = mw.execShell('docker image pull %s' % images)
if 'invalid' in ret[-1]:
return mw.returnJson(False, '拉取失败,请检查镜像名称或是否需要登录docker进行下载')
else:
return mw.returnJson(True, '拉取成功!')
def dockerPlulPath(path):
if not path and path == '':
return mw.returnJson(False, 'Invalid address')
ret = mw.execShell('docker image pull %s' % path)
if 'invalid' in ret[-1]:
return mw.returnJson(False, '拉取失败,请检查镜像名称或是否需要登录docker进行下载')
else:
return mw.returnJson(True, '拉取成功!')
def dockerPullReg():
# pull Dockr 官方镜像
args = getArgs()
data = checkArgs(args, ['path'])
if not data[0]:
return data[1]
path = args['path']
return dockerPlulPath(path)
# 判断镜像是否存在
def checkImage(path):
image_list = imageList()
for i in image_list:
if path == i["RepoTags"]:
return mw.returnData(False, '镜像已存在!')
def dockerPullPrivateNew():
# pull Dockr 官方镜像
args = getArgs()
data = checkArgs(args, ['path'])
if not data[0]:
return data[1]
path = args['path']
check = checkImage(path)
if check:
return mw.getJson(check)
my_repo = repoList()
if not my_repo:
return mw.returnJson(False, '未登录任何私人存储库,请登录然后拉取')
return dockerPlulPath(path)
def imageListData():
try:
ilist = imageList()
except Exception as e:
return mw.returnJson(False, '未开启Docker')
return mw.returnJson(True, 'ok', ilist)
def dockerRemoveImage():
args = getArgs()
data = checkArgs(args, ['imageId', 'repoTags'])
if not data[0]:
return data[1]
repoTags = args['repoTags']
imageId = args['imageId']
c = getDClient()
try:
c.images.remove(repoTags)
return mw.returnJson(True, '成功删除')
except:
try:
c.images.remove(imageId)
return mw.returnJson(True, '成功删除!')
except docker.errors.APIError as ex:
return mw.returnJson(False, '删除失败, 当前镜像正在使用!')
def getImageListFunc(dbname=''):
bkDir = mw.getRootDir() + '/backup/docker'
blist = os.listdir(bkDir)
r = []
bname = 'db_' + dbname
blen = len(bname)
for x in blist:
fbstr = x[0:blen]
if fbstr == bname:
r.append(x)
return r
def dockerImagePickDir():
bkDir = mw.getRootDir() + '/backup/docker'
return mw.returnJson(True, 'ok', bkDir)
def dockerImagePickList():
bkDir = mw.getRootDir() + '/backup/docker'
if not os.path.exists(bkDir):
os.mkdir(bkDir)
r = os.listdir(bkDir)
rr = []
for x in range(0, len(r)):
p = bkDir + '/' + r[x]
data = {}
data['name'] = r[x]
rsize = os.path.getsize(p)
data['size'] = mw.toSize(rsize)
t = os.path.getctime(p)
t = time.localtime(t)
data['time'] = time.strftime('%Y-%m-%d %H:%M:%S', t)
rr.append(data)
data['file'] = p
return mw.returnJson(True, 'ok', rr)
def dockerImagePickSave():
# image 导出
args = getArgs()
data = checkArgs(args, ['images'])
if not data[0]:
return data[1]
bkDir = mw.getRootDir() + '/backup/docker/'
images = args['images']
try:
file_name = bkDir + \
str(time.strftime('%Y%m%d_%H%M%S', time.localtime())) + '.tar.gz'
mw.execShell('docker image save %s | gzip > %s' %
(images, file_name))
return mw.returnJson(True, '导出镜像 {} 成功!'.format(file_name))
except docker.errors.APIError as ex:
return mw.returnJson(False, '操作失败: ' + str(ex))
def dockerImagePickLoad():
# 镜像文件导入
args = getArgs()
data = checkArgs(args, ['file'])
if not data[0]:
return data[1]
try:
file_path = args['file']
if not os.path.exists(file_path):
return mw.returnJson(False, '文件不存在')
if file_path.endswith('.tar'):
mw.execShell('docker image load < %s' % file_path)
elif file_path.endswith('.tar.gz'):
mw.execShell('gunzip -c %s | docker image load' % file_path)
else:
return mw.returnJson(False, '不支持改文件类型!')
return mw.returnJson(True, '导入镜像文件成功!')
except docker.errors.APIError as ex:
return mw.returnJson(False, '操作失败: ' + str(ex))
def dockerLoginCheck(user_name, user_pass, registry):
# 登陆验证
cmd = 'docker login -u=%s -p %s %s' % (user_name, user_pass, registry)
# print(cmd)
login_test = mw.execShell(cmd)
# print(login_test)
ret = 'required$|Error'
ret2 = re.findall(ret, login_test[-1])
if len(ret2) == 0:
return True
else:
return False
def getDockerIpListData():
# 取IP列表
path = getServerDir()
ipConf = path + '/iplist.json'
if not os.path.exists(ipConf):
return []
iplist = json.loads(mw.readFile(ipConf))
return iplist
def getDockerIpList():
data = getDockerIpListData()
return mw.returnJson(True, 'ok!', data)
def dockerAddIP():
# 添加IP
args = getArgs()
data = checkArgs(args, ['address', 'netmask', 'gateway'])
if not data[0]:
return data[1]
path = getServerDir()
ipConf = path + '/iplist.json'
if not os.path.exists(ipConf):
iplist = []
mw.writeFile(ipConf, json.dumps(iplist))
iplist = json.loads(mw.readFile(ipConf))
ipInfo = {
'address': args['address'],
'netmask': args['netmask'],
'gateway': args['gateway'],
}
iplist.append(ipInfo)
mw.writeFile(ipConf, json.dumps(iplist))
return mw.returnJson(True, '添加成功!')
def dockerDelIP():
# 删除IP
args = getArgs()
data = checkArgs(args, ['address'])
if not data[0]:
return data[1]
path = getServerDir()
ipConf = path + '/iplist.json'
if not os.path.exists(ipConf):
return mw.returnJson(False, '指定的IP不存在。!')
iplist = json.loads(mw.readFile(ipConf))
newList = []
for ipInfo in iplist:
if ipInfo['address'] == args['address']:
continue
newList.append(ipInfo)
mw.writeFile(ipConf, json.dumps(newList))
return mw.returnJson(True, '成功删除!')
def getDockerCreateInfo():
# 取创建依赖
import psutil
data = {}
data['images'] = imageList()
data['memSize'] = int(psutil.virtual_memory().total / 1024 / 1024)
data['iplist'] = getDockerIpListData()
return mw.returnJson(True, 'ok!', data)
def __release_port(port):
from collections import namedtuple
try:
import firewall_api
firewall_api.firewall_api().addAcceptPortArgs(port, 'docker', 'port')
return port
except Exception as e:
return "Release failed {}".format(e)
def dockerPortCheck():
args = getArgs()
data = checkArgs(args, ['port'])
if not data[0]:
return data[1]
port = args['port']
is_ok = IsPortExists(port)
if is_ok:
return mw.returnJson(True, 'ok')
return mw.returnJson(False, 'fail')
def IsPortExists(port):
# 判断端口是否被占用
ret = __check_dst_port(ip='localhost', port=port)
ret2 = __check_dst_port(ip='0.0.0.0', port=port)
if ret:
return ret
if not ret and ret2:
return ret2
if not ret and not ret2:
return False
def __check_dst_port(ip, port, timeout=3):
# 端口检测
import socket
ok = True
try:
s = socket.socket()
s.settimeout(timeout)
s.connect((ip, port))
s.close()
except:
ok = False
return ok
def dockerCreateCon():
args = getArgs()
data = checkArgs(args, ['environments', 'command',
'entrypoint', 'image', 'mem_limit', 'ports', 'volumes'])
if not data[0]:
return data[1]
environments = args['environments']
environments = environments.strip().split()
command = args['command']
entrypoint = args['entrypoint']
image = args['image']
mem_limit = args['mem_limit']
ports = args['ports']
ports = ports.replace('[', '(').replace(']', ')')
volumes = args['volumes']
# if __name__ == "__main__":
# print(args)
try:
c = getDClient()
conObject = c.containers.run(
image=image,
mem_limit=mem_limit + 'M',
ports=eval(ports),
auto_remove=False,
command=command,
detach=True,
stdin_open=True,
tty=True,
entrypoint=entrypoint,
privileged=True,
volumes=json.loads(volumes),
cpu_shares=10,
environment=environments
)
if conObject:
__release_port(ports)
return mw.returnJson(True, '创建成功!')
return mw.returnJson(False, '创建失败!')
except docker.errors.APIError as ex:
return mw.returnJson(False, '创建失败!' + str(ex))
def dockerLogin():
args = getArgs()
# print(args)
data = checkArgs(args, ['user', 'passwd', 'hub_name',
'namespace', 'registry', 'repository_name'])
if not data[0]:
return data[1]
user_name = args['user']
user_pass = args['passwd']
registry = args['registry']
hub_name = args['hub_name']
namespace = args['namespace']
repository_name = args['repository_name']
ret_status = dockerLoginCheck(user_name, user_pass, registry)
path = getServerDir()
if ret_status:
user_file = path + '/user.json'
user_info = mw.readFile(user_file)
if not user_info:
user_info = []
else:
user_info = json.loads(user_info)
ret = {}
ret['user_name'] = user_name
ret['user_pass'] = user_pass
ret['registry'] = registry
ret['hub_name'] = hub_name
ret['namespace'] = namespace
ret['repository_name'] = repository_name
if not registry:
ret['registry'] = "docker.io"
user_info.append(ret)
mw.writeFile(user_file, json.dumps(user_info))
return mw.returnJson(True, '成功登录!')
return mw.returnJson(False, '登录失败!')
# 删除用户信息
def delete_user_info(registry):
path = getServerDir()
user_file = path + '/user.json'
user_info = mw.readFile(user_file)
if user_info:
user_info = json.loads(user_info)
for i in range(len(user_info)):
if registry in user_info[i].values():
del(user_info[i])
mw.writeFile(user_file, json.dumps(user_info))
return True
def dockerLogout():
args = getArgs()
data = checkArgs(args, ['registry'])
if not data[0]:
return data[1]
registry = args['registry']
if registry == "docker.io":
registry = ""
login_test = mw.execShell('docker logout %s' % registry)
if registry == "":
registry = "docker.io"
ret = 'required$|Error'
ret2 = re.findall(ret, login_test[-1])
delete_user_info(registry)
if len(ret2) == 0:
return mw.returnJson(True, '退出成功')
else:
return mw.returnJson(True, '退出失败')
def repoList():
path = getServerDir()
repostory_info = []
user_file = path + '/user.json'
if os.path.exists(user_file):
user_info = mw.readFile(user_file)
user_info = json.loads(user_info)
for i in user_info:
tmp = {}
tmp["hub_name"] = i["hub_name"]
tmp["registry"] = i["registry"]
tmp["namespace"] = i["namespace"]
tmp['repository_name'] = i["repository_name"]
repostory_info.append(tmp)
return mw.returnJson(True, 'ok', repostory_info)
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 == 'con_list':
print(conListData())
elif func == 'docker_con_log':
print(dockerLogCon())
elif func == 'docker_remove_con':
print(dockerRemoveCon())
elif func == 'docker_run_con':
print(dockerRunCon())
elif func == 'docker_stop_con':
print(dockerStopCon())
elif func == 'docker_exec':
print(dockerExec())
elif func == 'docker_pull':
print(dockerPull())
elif func == 'docker_pull_reg':
print(dockerPullReg())
elif func == 'image_list':
print(imageListData())
elif func == 'image_pick_dir':
print(dockerImagePickDir())
elif func == 'image_pick_save':
print(dockerImagePickSave())
elif func == 'image_pick_load':
print(dockerImagePickLoad())
elif func == 'image_pick_list':
print(dockerImagePickList())
elif func == 'docker_get_iplist':
print(getDockerIpList())
elif func == 'docker_del_ip':
print(dockerDelIP())
elif func == 'docker_add_ip':
print(dockerAddIP())
elif func == 'get_docker_create_info':
print(getDockerCreateInfo())
elif func == 'docker_create_con':
print(dockerCreateCon())
elif func == 'docker_remove_image':
print(dockerRemoveImage())
elif func == 'docker_port_check':
print(dockerPortCheck())
elif func == 'docker_login':
print(dockerLogin())
elif func == 'docker_logout':
print(dockerLogout())
elif func == 'repo_list':
print(repoList())
else:
print('error')
-19
View File
@@ -1,19 +0,0 @@
{
"sort": 7,
"ps": "Docker是一个开源的应用容器引擎",
"name": "docker",
"title": "Docker",
"shell": "install.sh",
"versions":["1.0"],
"updates":["1.0"],
"tip": "soft",
"checks": "server/docker",
"path": "server/docker",
"display": 1,
"author": "Zend",
"date": "2017-04-01",
"home": "https://docker.io",
"doc1":"https://docker-py.readthedocs.io/",
"type": 0,
"pid": "4"
}
-84
View File
@@ -1,84 +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")
# cd /www/server/mdserver-web/plugins/docker && /bin/bash install.sh uninstall 1.0
# cd /www/server/mdserver-web/plugins/docker && /bin/bash install.sh install 1.0
install_tmp=${rootPath}/tmp/mw_install.pl
VERSION=$2
if [ -f ${rootPath}/bin/activate ];then
source ${rootPath}/bin/activate
fi
Install_Docker()
{
# which docker
# if [ "$?" == "0" ];then
# echo '安装已经完成docker' > $install_tmp
# exit 0
# fi
echo '正在安装脚本文件...' > $install_tmp
mkdir -p $serverPath/source
if [ ! -d $serverPath/docker ];then
curl -fsSL https://get.docker.com | bash
mkdir -p $serverPath/docker
fi
pip install docker
pip install pytz
if [ -d $serverPath/docker ];then
echo "${VERSION}" > $serverPath/docker/version.pl
echo '安装完成' > $install_tmp
cd ${rootPath} && python3 ${rootPath}/plugins/docker/index.py start
cd ${rootPath} && python3 ${rootPath}/plugins/docker/index.py initd_install
fi
}
Uninstall_Docker()
{
CMD=yum
which apt
if [ "$?" == "0" ];then
CMD=apt
fi
if [ -f /usr/lib/systemd/system/docker.service ];then
systemctl stop docker
systemctl disable docker
rm -rf /usr/lib/systemd/system/docker.service
systemctl daemon-reload
fi
$CMD remove -y docker docker-ce-cli containerd.io
# docker-client \
# docker-client-latest \
# docker-common \
# docker-latest \
# docker-latest-logrotate \
# docker-logrotate \
# docker-selinux \
# docker-engine-selinux \
# docker-engine \
# docker-ce
rm -rf $serverPath/docker
echo "Uninstall_Docker" > $install_tmp
}
action=$1
if [ "${1}" == 'install' ];then
Install_Docker
else
Uninstall_Docker
fi
File diff suppressed because it is too large Load Diff
Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.1 KiB

-19
View File
@@ -1,19 +0,0 @@
<div class="bt-form">
<div class='plugin_version'></div>
<div class="bt-w-main">
<div class="bt-w-menu">
<p class="bgw" onclick="pluginService('dynamic-tracking');">服务</p>
<p onclick="pluginInitD('dynamic-tracking');">自启动</p>
</div>
<div class="bt-w-con pd15">
<div class="soft-man-con"></div>
</div>
</div>
</div>
<script type="text/javascript">
resetPluginWinHeight(300);
resetPluginWinWidth(500);
$.getScript( "/plugins/file?name=dynamic-tracking&f=static/js/dynamic-tracking.js", function(){
pluginService('dynamic-tracking', $('.plugin_version').attr('version'));
});
</script>

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