Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
5cbca9232c | ||
|
|
742bb6d86d | ||
|
|
b14accd83e | ||
|
|
8ae01d8d36 | ||
|
|
5db1e58e6a | ||
|
|
a2e6d2f280 | ||
|
|
83f3d224ed | ||
|
|
73693620df | ||
|
|
72eb67dfda | ||
|
|
c55b4a02c8 | ||
|
|
721efd6a16 | ||
|
|
61c8ce4b6f | ||
|
|
ce95ca6905 | ||
|
|
01dee5fe1f | ||
|
|
1a72edee9a | ||
|
|
27b6ad0280 | ||
|
|
ae9e6921c3 | ||
|
|
b51eb99027 | ||
|
|
2e584c540d | ||
|
|
bbf258e1a7 | ||
|
|
53d57716aa | ||
|
|
e9a6e5477d | ||
|
|
46ca8a6b44 | ||
|
|
d6f88caf3d | ||
|
|
abc85902a2 | ||
|
|
9edc8bbea2 | ||
|
|
443d3efd37 | ||
|
|
948bcde358 | ||
|
|
83aa3cfca6 | ||
|
|
976c88ed44 | ||
|
|
0db16797b5 | ||
|
|
b7802fc898 | ||
|
|
741dffd5db | ||
|
|
2c9f11bcfe | ||
|
|
16893e51e6 | ||
|
|
34a43a6af9 | ||
|
|
4dd5d946d9 | ||
|
|
7e63f686b9 | ||
|
|
7b57972179 | ||
|
|
a758a36bdc | ||
|
|
84123c603d | ||
|
|
33f9c09a07 |
@@ -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: 需求描述
|
||||
---
|
||||
|
||||
## 需求
|
||||
|
||||
想开发一个什么插件?
|
||||
想有什么功能?
|
||||
|
||||
|
||||
## 提供参考
|
||||
|
||||
给开发者提供参考!
|
||||
@@ -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,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: 请在讨论中提出建议。
|
||||
@@ -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
|
||||
@@ -1,3 +0,0 @@
|
||||
### 合并描述
|
||||
|
||||
- 我添加了测试用例来覆盖新代码。
|
||||
@@ -1,38 +0,0 @@
|
||||
# This workflow will install Python dependencies, run tests and lint with a single version of Python
|
||||
# For more information see: https://help.github.com/actions/language-and-framework-guides/using-python-with-github-actions
|
||||
|
||||
name: MW APP MySQL55
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ "run_test" ]
|
||||
pull_request:
|
||||
branches: [ "run_test" ]
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
build:
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-20.04, ubuntu-18.04, ubuntu-22.04]
|
||||
runs-on: ${{ matrix.os }}
|
||||
#runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Set up Python 3.10
|
||||
uses: actions/setup-python@v3
|
||||
with:
|
||||
python-version: "3.10"
|
||||
- name: Install MW
|
||||
run: |
|
||||
sudo bash scripts/install_dev.sh
|
||||
- name: Install MySQL55
|
||||
run: |
|
||||
cd /www/server/mdserver-web/plugins/mysql && sudo bash install.sh install 5.5
|
||||
- name: Start DEBUG
|
||||
run: |
|
||||
cd web && gunicorn -c setting.py app:app
|
||||
python3 panel_task.py &
|
||||
@@ -1,38 +0,0 @@
|
||||
# This workflow will install Python dependencies, run tests and lint with a single version of Python
|
||||
# For more information see: https://help.github.com/actions/language-and-framework-guides/using-python-with-github-actions
|
||||
|
||||
name: MW APP MySQL56
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ "run_test" ]
|
||||
pull_request:
|
||||
branches: [ "run_test" ]
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
build:
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-20.04, ubuntu-18.04, ubuntu-22.04]
|
||||
runs-on: ${{ matrix.os }}
|
||||
#runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Set up Python 3.10
|
||||
uses: actions/setup-python@v3
|
||||
with:
|
||||
python-version: "3.10"
|
||||
- name: Install MW
|
||||
run: |
|
||||
sudo bash scripts/install_dev.sh
|
||||
- name: Install MySQL56
|
||||
run: |
|
||||
cd /www/server/mdserver-web/plugins/mysql && sudo bash install.sh install 5.6
|
||||
- name: Start DEBUG
|
||||
run: |
|
||||
cd web && gunicorn -c setting.py app:app
|
||||
python3 panel_task.py &
|
||||
@@ -1,38 +0,0 @@
|
||||
# This workflow will install Python dependencies, run tests and lint with a single version of Python
|
||||
# For more information see: https://help.github.com/actions/language-and-framework-guides/using-python-with-github-actions
|
||||
|
||||
name: MW APP MySQL57
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ "run_test" ]
|
||||
pull_request:
|
||||
branches: [ "run_test" ]
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
build:
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-20.04, ubuntu-18.04, ubuntu-22.04]
|
||||
runs-on: ${{ matrix.os }}
|
||||
#runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Set up Python 3.10
|
||||
uses: actions/setup-python@v3
|
||||
with:
|
||||
python-version: "3.10"
|
||||
- name: Install MW
|
||||
run: |
|
||||
sudo bash scripts/install_dev.sh
|
||||
- name: Install MySQL57
|
||||
run: |
|
||||
cd /www/server/mdserver-web/plugins/mysql && sudo bash install.sh install 5.7
|
||||
- name: Start DEBUG
|
||||
run: |
|
||||
cd web && gunicorn -c setting.py app:app
|
||||
python3 panel_task.py &
|
||||
@@ -1,38 +0,0 @@
|
||||
# This workflow will install Python dependencies, run tests and lint with a single version of Python
|
||||
# For more information see: https://help.github.com/actions/language-and-framework-guides/using-python-with-github-actions
|
||||
|
||||
name: MW APP MySQL80
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ "run_test" ]
|
||||
pull_request:
|
||||
branches: [ "run_test" ]
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
build:
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-20.04, ubuntu-18.04, ubuntu-22.04]
|
||||
runs-on: ${{ matrix.os }}
|
||||
#runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Set up Python 3.10
|
||||
uses: actions/setup-python@v3
|
||||
with:
|
||||
python-version: "3.10"
|
||||
- name: Install MW
|
||||
run: |
|
||||
sudo bash scripts/install_dev.sh
|
||||
- name: Install MySQL80
|
||||
run: |
|
||||
cd /www/server/mdserver-web/plugins/mysql && sudo bash install.sh install 8.0
|
||||
- name: Start DEBUG
|
||||
run: |
|
||||
cd web && gunicorn -c setting.py app:app
|
||||
python3 panel_task.py &
|
||||
@@ -1,38 +0,0 @@
|
||||
# This workflow will install Python dependencies, run tests and lint with a single version of Python
|
||||
# For more information see: https://help.github.com/actions/language-and-framework-guides/using-python-with-github-actions
|
||||
|
||||
name: MW APP OpenResty
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ "master", "dev" ]
|
||||
pull_request:
|
||||
branches: [ "master", "dev" ]
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
build:
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-latest]
|
||||
runs-on: ${{ matrix.os }}
|
||||
#runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Set up Python 3.10
|
||||
uses: actions/setup-python@v3
|
||||
with:
|
||||
python-version: "3.10"
|
||||
- name: Install MW
|
||||
run: |
|
||||
sudo bash scripts/install_dev.sh
|
||||
- name: Install OpenResty
|
||||
run: |
|
||||
cd /www/server/mdserver-web/plugins/openresty && sudo bash install.sh install 1.27.1
|
||||
- name: Start DEBUG
|
||||
run: |
|
||||
source /www/server/mdserver-web/bin/activate && cd web && gunicorn -c setting.py app:app
|
||||
cd /www/server/mdserver-web && python3 panel_task.py &
|
||||
@@ -1,68 +0,0 @@
|
||||
# This workflow will install Python dependencies, run tests and lint with a single version of Python
|
||||
# For more information see: https://help.github.com/actions/language-and-framework-guides/using-python-with-github-actions
|
||||
|
||||
name: MW APP PHP
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ "run_test" ]
|
||||
pull_request:
|
||||
branches: [ "run_test" ]
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
build:
|
||||
strategy:
|
||||
matrix:
|
||||
os: [ubuntu-latest]
|
||||
runs-on: ${{ matrix.os }}
|
||||
#runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v3
|
||||
- name: Set up Python 3.10
|
||||
uses: actions/setup-python@v3
|
||||
with:
|
||||
python-version: "3.10"
|
||||
- name: Install MW
|
||||
run: |
|
||||
sudo bash scripts/install_dev.sh
|
||||
- name: Install PHP53
|
||||
run: |
|
||||
cd /www/server/mdserver-web/plugins/php && sudo bash install.sh install 53
|
||||
- name: Install PHP54
|
||||
run: |
|
||||
cd /www/server/mdserver-web/plugins/php && sudo bash install.sh install 54
|
||||
- name: Install PHP55
|
||||
run: |
|
||||
cd /www/server/mdserver-web/plugins/php && sudo bash install.sh install 55
|
||||
- name: Install PHP56
|
||||
run: |
|
||||
cd /www/server/mdserver-web/plugins/php && sudo bash install.sh install 56
|
||||
- name: Install PHP70
|
||||
run: |
|
||||
cd /www/server/mdserver-web/plugins/php && sudo bash install.sh install 70
|
||||
- name: Install PHP71
|
||||
run: |
|
||||
cd /www/server/mdserver-web/plugins/php && sudo bash install.sh install 71
|
||||
- name: Install PHP72
|
||||
run: |
|
||||
cd /www/server/mdserver-web/plugins/php && sudo bash install.sh install 72
|
||||
- name: Install PHP73
|
||||
run: |
|
||||
cd /www/server/mdserver-web/plugins/php && sudo bash install.sh install 73
|
||||
- name: Install PHP74
|
||||
run: |
|
||||
cd /www/server/mdserver-web/plugins/php && sudo bash install.sh install 74
|
||||
- name: Install PHP80
|
||||
run: |
|
||||
cd /www/server/mdserver-web/plugins/php && sudo bash install.sh install 80
|
||||
- name: Install PHP81
|
||||
run: |
|
||||
cd /www/server/mdserver-web/plugins/php && sudo bash install.sh install 81
|
||||
- name: Start DEBUG
|
||||
run: |
|
||||
source /www/server/mdserver-web/bin/activate && cd web && gunicorn -c setting.py app:app
|
||||
cd /www/server/mdserver-web && python3 panel_task.py &
|
||||
@@ -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
|
||||
@@ -8,11 +8,13 @@ __pycache__/
|
||||
|
||||
# Distribution / packaging
|
||||
.Python
|
||||
build/
|
||||
develop-eggs/
|
||||
dist/
|
||||
downloads/
|
||||
eggs/
|
||||
.eggs/
|
||||
lib/
|
||||
lib64/
|
||||
parts/
|
||||
sdist/
|
||||
@@ -87,7 +89,6 @@ venv/
|
||||
ENV/
|
||||
env.bak/
|
||||
venv.bak/
|
||||
lib/python*
|
||||
|
||||
# Spyder project settings
|
||||
.spyderproject
|
||||
@@ -99,97 +100,9 @@ lib/python*
|
||||
# mkdocs documentation
|
||||
/site
|
||||
|
||||
bin
|
||||
pyvenv.cfg
|
||||
include
|
||||
share
|
||||
pip-selfcheck.json
|
||||
|
||||
# mypy
|
||||
.mypy_cache/
|
||||
|
||||
.DS_Store
|
||||
.idea/*.xml
|
||||
.idea/*.iml
|
||||
tmp/*
|
||||
*.swp
|
||||
*.pem
|
||||
*.zip
|
||||
debug.out
|
||||
mdioks.session-journal
|
||||
*.session
|
||||
|
||||
temp
|
||||
|
||||
logs/panel.pid
|
||||
logs/panel.log
|
||||
logs/panel_task.log
|
||||
logs/panel_exec.log
|
||||
logs/panel_task.lock
|
||||
|
||||
ssl/local
|
||||
ssl/nginx
|
||||
ssl/choose.pl
|
||||
|
||||
scripts/init.d/mw
|
||||
scripts/mdserver-web
|
||||
|
||||
data/api_login.txt
|
||||
data/sessions
|
||||
|
||||
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
|
||||
data/ssl.pl
|
||||
|
||||
data/port.pl
|
||||
data/ipv6.pl
|
||||
data/restart.pl
|
||||
data/edate.pl
|
||||
data/osname.pl
|
||||
data/only_netio_counters.pl
|
||||
data/system.db-journal
|
||||
data/hook_*.json
|
||||
data/letsencrypt.json
|
||||
data/basic_auth.json
|
||||
data/notify.json
|
||||
data/api.json
|
||||
data/bind_domain.pl
|
||||
data/unauthorized_status.pl
|
||||
data/auth_secret.pl
|
||||
data/panelSpeed.pl
|
||||
|
||||
plugins/vip_*
|
||||
plugins/own_*
|
||||
plugins/my_*
|
||||
plugins/op_auth
|
||||
plugins/l2tp
|
||||
plugins/openlitespeed
|
||||
plugins/tamper_proof
|
||||
plugins/cryptocurrency_trade
|
||||
plugins/zimg
|
||||
plugins/bk_demo
|
||||
plugins/mail
|
||||
plugins/fastdfs
|
||||
plugins/v2ray
|
||||
plugins/frp
|
||||
plugins/file_search
|
||||
plugins/proxysql
|
||||
plugins/tidb
|
||||
plugins/goedge-admin
|
||||
plugins/goedge-node
|
||||
plugins/goedge-happy
|
||||
plugins/dztasks
|
||||
|
||||
/logs
|
||||
/data
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="VcsDirectoryMappings">
|
||||
<mapping directory="$PROJECT_DIR$" vcs="Git" />
|
||||
</component>
|
||||
</project>
|
||||
@@ -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.
|
||||
|
||||
@@ -1,225 +1,14 @@
|
||||
<p align="center">
|
||||
<img alt="logo" src="https://cdn.jsdelivr.net/gh/midoks/mdserver-web@latest/route/static/logo.png" height="140" />
|
||||
<h3 align="center">mdserver-web</h3>
|
||||
<p align="center">一款简单Linux面板服务</p>
|
||||
<p align="center">强烈推荐系统:debian</p>
|
||||
</p>
|
||||
|
||||
### 简介
|
||||
|
||||
简单的Linux面板,感谢BT.CN写出如此好的web管理软件。我一看到,就知道这是我一直想要的页面化管理方式。
|
||||
复制了后台管理界面,按照自己想要的方式写了一版。
|
||||
|
||||

|
||||

|
||||

|
||||

|
||||

|
||||
|
||||
|
||||
[](https://github.com/midoks/mdserver-web/wiki)
|
||||
[](https://www.jsdelivr.com/package/gh/midoks/mdserver-web)
|
||||
|
||||
* SSH终端工具
|
||||
* 面板收藏功能
|
||||
* 网站子目录绑定
|
||||
* 网站备份功能
|
||||
* 插件方式管理
|
||||
|
||||
基本上可以使用,后续会继续优化!欢迎提供意见!
|
||||
|
||||
- 吹水组 - https://t.me/mdserver_web
|
||||
- 交流论坛 - https://bbs.midoks.icu
|
||||
# mdweb
|
||||
简单的Linux面板
|
||||
|
||||
### 自动安装
|
||||
```
|
||||
如果出现问题,最好私给我面板信息。不要让我猜。如果不提供,不要提出问题,自行解决。 — 座右铭
|
||||
Talk is cheap, show me the code. -- linus
|
||||
curl -fsSL https://raw.githubusercontent.com/midoks/mdweb/master/install/install.sh | sh
|
||||
```
|
||||
|
||||
- [兼容性测试报告](/compatibility.md)
|
||||
- [常用命令说明](/cmd.md) [ mw default ] [ mw update_dev ]
|
||||
|
||||
### 特别赞助
|
||||
|
||||
- [找资源 - 阿里云盘资源搜索引擎](https://zhaoziyuan1.cc)
|
||||
|
||||
### 主要插件介绍
|
||||
|
||||
* OpenResty - 轻量级,占有内存少,并发能力强。
|
||||
* PHP[53-84] - 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.1]支持MySQL[8.0+]
|
||||
|
||||
PHP[53-72]支持phpMyAdmin[4.4.15]
|
||||
PHP[72-84]支持phpMyAdmin[5.2.1]
|
||||
```
|
||||
|
||||
# 郑重声明
|
||||
|
||||
不卖、不会监控(统计使用除外)、更不会注入病毒,大家使用方便(望大家不吝捐赠)。
|
||||
|
||||
- https://www.youtube.com/watch?v=2taa5K-Jmmw
|
||||
|
||||
|
||||
# AD - VPS推荐 - 🙏
|
||||
|
||||
- [ZZZ评测](https://www.zzzvps.com/)
|
||||
|
||||
| 服务商 | LOGO | 推广地址 | 优惠码 |
|
||||
| ------------- |----------|-----------|-------|
|
||||
| digitalvirt |[](https://digitalvirt.com/aff.php?aff=154) | https://digitalvirt.com/aff.php?aff=154 | mdserver-web |
|
||||
|
||||
# Docker
|
||||
|
||||
- 由[DDS-Derek](https://github.com/DDS-Derek)开发维护。
|
||||
- https://github.com/DDS-Derek/mdserver-web-Docker
|
||||
|
||||
```
|
||||
docker run -itd --name mw-server --privileged=true -p 7200:7200 -p 80:80 -p 443:443 -p 888:888 ddsderek/mw-server:latest
|
||||
```
|
||||
|
||||
|
||||
### 版本更新 0.18.0
|
||||
|
||||
- 重新架构面板。
|
||||
- PHP84正式版。
|
||||
- 加入alist插件。
|
||||
- 数据管理-常用命令。
|
||||
- 插件读取优化(加入缓存+定时检查状态)。
|
||||
- 恢复面板书签。
|
||||
- 常规更新。
|
||||
|
||||
### 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
|
||||
```
|
||||
|
||||
### 旧版安装
|
||||
```
|
||||
curl --insecure -fsSL https://raw.githubusercontent.com/midoks/mdserver-web/dev/scripts/old/install.sh | bash
|
||||
curl --insecure -fsSL https://raw.githubusercontent.com/midoks/mdserver-web/dev/scripts/old/update.sh | bash
|
||||
```
|
||||
|
||||
### 捐赠地址 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/mdw.jpg)
|
||||
|
||||
|
||||
### Stargazers over time
|
||||
|
||||
[](https://starchart.cc/midoks/mdserver-web)
|
||||
|
||||
|
||||
### 感谢开发赞助
|
||||
|
||||
[](https://digitalvirt.com/aff.php?aff=154)
|
||||
|
||||
### 授权许可
|
||||
|
||||
本项目采用 Apache 开源授权许可证,完整的授权说明已放置在 [LICENSE](https://github.com/midoks/mdserver-web/blob/master/LICENSE) 文件中。
|
||||
|
||||
### 手动操作
|
||||
- pip install pip==9.0.3
|
||||
- pip install flask
|
||||
- pip install gunicorn
|
||||
- pip install psutil
|
||||
- pip install -r requirements.txt
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
from flask import Flask
|
||||
import views
|
||||
import sys
|
||||
import io
|
||||
import os
|
||||
|
||||
sys.path.append("class/")
|
||||
|
||||
app = Flask(__name__)
|
||||
app.debug = True
|
||||
|
||||
|
||||
DEFAULT_MODULES = (
|
||||
(views.dashboard, "/"),
|
||||
(views.site, "/site"),
|
||||
(views.files, "/files"),
|
||||
(views.soft, "/soft"),
|
||||
(views.config, "/config"),
|
||||
(views.plugins, "/plugins"),
|
||||
(views.task, "/task"),
|
||||
)
|
||||
|
||||
import time
|
||||
# print "time.time(): %f " % time.time()
|
||||
app.config.version = "0.0.1" + str(time.time())
|
||||
|
||||
|
||||
def setting_modules(app, modules):
|
||||
for module, url_prefix in modules:
|
||||
app.register_blueprint(module, url_prefix=url_prefix)
|
||||
|
||||
setting_modules(app, DEFAULT_MODULES)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
app.run()
|
||||
@@ -0,0 +1,841 @@
|
||||
# coding: utf-8
|
||||
|
||||
import os
|
||||
import sys
|
||||
import time
|
||||
import string
|
||||
|
||||
|
||||
def M(table):
|
||||
import db
|
||||
sql = db.Sql()
|
||||
return sql.table(table)
|
||||
|
||||
|
||||
def md5(str):
|
||||
# 生成MD5
|
||||
try:
|
||||
import hashlib
|
||||
m = hashlib.md5()
|
||||
m.update(str)
|
||||
return m.hexdigest()
|
||||
except:
|
||||
return False
|
||||
|
||||
# 文件的MD5值
|
||||
|
||||
|
||||
def GetFileMd5(filename):
|
||||
if not os.path.isfile(filename):
|
||||
return False
|
||||
import hashlib
|
||||
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):
|
||||
from random import Random
|
||||
str = ''
|
||||
chars = 'AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz0123456789'
|
||||
chrlen = len(chars) - 1
|
||||
random = Random()
|
||||
for i in range(length):
|
||||
str += chars[random.randint(0, chrlen)]
|
||||
return str
|
||||
|
||||
|
||||
def checkCode(code, outime=120):
|
||||
# 校验验证码
|
||||
import time
|
||||
import web
|
||||
try:
|
||||
if md5(code.lower()) != web.ctx.session.codeStr:
|
||||
web.ctx.session.login_error = getMsg('CODE_ERR')
|
||||
return False
|
||||
if time.time() - web.ctx.session.codeTime > outime:
|
||||
web.ctx.session.login_error = getMsg('CODE_TIMEOUT')
|
||||
return False
|
||||
return True
|
||||
except:
|
||||
web.ctx.session.login_error = getMsg('CODE_NOT_EXISTS')
|
||||
return False
|
||||
|
||||
|
||||
def returnJson(status, msg, args=()):
|
||||
# 取通用Json返回
|
||||
return getJson(returnMsg(status, msg, args))
|
||||
|
||||
|
||||
def returnMsg(status, msg, args=()):
|
||||
import json
|
||||
# 取通用字曲返回
|
||||
logMessage = json.loads(
|
||||
readFile('static/language/' + get_language() + '/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}
|
||||
|
||||
# 取提示消息
|
||||
|
||||
|
||||
def getMsg(key, args=()):
|
||||
try:
|
||||
import json
|
||||
logMessage = json.loads(
|
||||
readFile('static/language/' + get_language() + '/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):
|
||||
import json
|
||||
logMessage = json.loads(
|
||||
readFile('static/language/' + get_language() + '/template.json'))
|
||||
keys = logMessage.keys()
|
||||
msg = None
|
||||
if key in keys:
|
||||
msg = logMessage[key]
|
||||
return msg
|
||||
|
||||
|
||||
def getJson(data):
|
||||
import json
|
||||
import web
|
||||
web.header('Content-Type', 'application/json; charset=utf-8')
|
||||
return json.dumps(data)
|
||||
|
||||
|
||||
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 get_language():
|
||||
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/' + get_language() + '/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):
|
||||
import json
|
||||
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():
|
||||
import json
|
||||
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 ExecShell(cmdstring, cwd=None, timeout=None, shell=True):
|
||||
import shlex
|
||||
import datetime
|
||||
import subprocess
|
||||
import time
|
||||
|
||||
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 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:
|
||||
try:
|
||||
url = web.ctx.session.home + '/Api/getIpAddress'
|
||||
opener = urllib2.urlopen(url)
|
||||
return opener.read()
|
||||
except:
|
||||
import web
|
||||
return web.ctx.host.split(':')[0]
|
||||
|
||||
# 搜索数据中是否存在
|
||||
|
||||
|
||||
def inArray(arrays, searchStr):
|
||||
for key in arrays:
|
||||
if key == searchStr:
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
# 检查Web服务器配置文件是否有错误
|
||||
|
||||
|
||||
def checkWebConfig():
|
||||
import 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
|
||||
|
||||
# 检查是否为IPv4地址
|
||||
|
||||
|
||||
def checkIp(ip):
|
||||
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]
|
||||
|
||||
# 取CPU类型
|
||||
|
||||
|
||||
def getCpuType():
|
||||
import re
|
||||
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)
|
||||
|
||||
|
||||
# 处理MySQL配置文件
|
||||
def CheckMyCnf():
|
||||
import os
|
||||
confFile = '/etc/my.cnf'
|
||||
if os.path.exists(confFile):
|
||||
conf = readFile(confFile)
|
||||
if len(conf) > 100:
|
||||
return True
|
||||
versionFile = '/www/server/mysql/version.pl'
|
||||
if not os.path.exists(versionFile):
|
||||
return False
|
||||
|
||||
versions = ['5.1', '5.5', '5.6', '5.7', 'AliSQL']
|
||||
version = readFile(versionFile)
|
||||
for key in versions:
|
||||
if key in version:
|
||||
version = key
|
||||
break
|
||||
|
||||
shellStr = '''
|
||||
#!/bin/bash
|
||||
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
|
||||
export PATH
|
||||
|
||||
CN='125.88.182.172'
|
||||
HK='download.bt.cn'
|
||||
HK2='103.224.251.67'
|
||||
US='174.139.221.74'
|
||||
sleep 0.5;
|
||||
CN_PING=`ping -c 1 -w 1 $CN|grep time=|awk '{print $7}'|sed "s/time=//"`
|
||||
HK_PING=`ping -c 1 -w 1 $HK|grep time=|awk '{print $7}'|sed "s/time=//"`
|
||||
HK2_PING=`ping -c 1 -w 1 $HK2|grep time=|awk '{print $7}'|sed "s/time=//"`
|
||||
US_PING=`ping -c 1 -w 1 $US|grep time=|awk '{print $7}'|sed "s/time=//"`
|
||||
|
||||
echo "$HK_PING $HK" > ping.pl
|
||||
echo "$HK2_PING $HK2" >> ping.pl
|
||||
echo "$US_PING $US" >> ping.pl
|
||||
echo "$CN_PING $CN" >> ping.pl
|
||||
nodeAddr=`sort -V ping.pl|sed -n '1p'|awk '{print $2}'`
|
||||
if [ "$nodeAddr" == "" ];then
|
||||
nodeAddr=$HK
|
||||
fi
|
||||
|
||||
Download_Url=http://$nodeAddr:5880
|
||||
|
||||
|
||||
MySQL_Opt()
|
||||
{
|
||||
MemTotal=`free -m | grep Mem | awk '{print $2}'`
|
||||
if [[ ${MemTotal} -gt 1024 && ${MemTotal} -lt 2048 ]]; then
|
||||
sed -i "s#^key_buffer_size.*#key_buffer_size = 32M#" /etc/my.cnf
|
||||
sed -i "s#^table_open_cache.*#table_open_cache = 128#" /etc/my.cnf
|
||||
sed -i "s#^sort_buffer_size.*#sort_buffer_size = 768K#" /etc/my.cnf
|
||||
sed -i "s#^read_buffer_size.*#read_buffer_size = 768K#" /etc/my.cnf
|
||||
sed -i "s#^myisam_sort_buffer_size.*#myisam_sort_buffer_size = 8M#" /etc/my.cnf
|
||||
sed -i "s#^thread_cache_size.*#thread_cache_size = 16#" /etc/my.cnf
|
||||
sed -i "s#^query_cache_size.*#query_cache_size = 16M#" /etc/my.cnf
|
||||
sed -i "s#^tmp_table_size.*#tmp_table_size = 32M#" /etc/my.cnf
|
||||
sed -i "s#^innodb_buffer_pool_size.*#innodb_buffer_pool_size = 128M#" /etc/my.cnf
|
||||
sed -i "s#^innodb_log_file_size.*#innodb_log_file_size = 32M#" /etc/my.cnf
|
||||
elif [[ ${MemTotal} -ge 2048 && ${MemTotal} -lt 4096 ]]; then
|
||||
sed -i "s#^key_buffer_size.*#key_buffer_size = 64M#" /etc/my.cnf
|
||||
sed -i "s#^table_open_cache.*#table_open_cache = 256#" /etc/my.cnf
|
||||
sed -i "s#^sort_buffer_size.*#sort_buffer_size = 1M#" /etc/my.cnf
|
||||
sed -i "s#^read_buffer_size.*#read_buffer_size = 1M#" /etc/my.cnf
|
||||
sed -i "s#^myisam_sort_buffer_size.*#myisam_sort_buffer_size = 16M#" /etc/my.cnf
|
||||
sed -i "s#^thread_cache_size.*#thread_cache_size = 32#" /etc/my.cnf
|
||||
sed -i "s#^query_cache_size.*#query_cache_size = 32M#" /etc/my.cnf
|
||||
sed -i "s#^tmp_table_size.*#tmp_table_size = 64M#" /etc/my.cnf
|
||||
sed -i "s#^innodb_buffer_pool_size.*#innodb_buffer_pool_size = 256M#" /etc/my.cnf
|
||||
sed -i "s#^innodb_log_file_size.*#innodb_log_file_size = 64M#" /etc/my.cnf
|
||||
elif [[ ${MemTotal} -ge 4096 && ${MemTotal} -lt 8192 ]]; then
|
||||
sed -i "s#^key_buffer_size.*#key_buffer_size = 128M#" /etc/my.cnf
|
||||
sed -i "s#^table_open_cache.*#table_open_cache = 512#" /etc/my.cnf
|
||||
sed -i "s#^sort_buffer_size.*#sort_buffer_size = 2M#" /etc/my.cnf
|
||||
sed -i "s#^read_buffer_size.*#read_buffer_size = 2M#" /etc/my.cnf
|
||||
sed -i "s#^myisam_sort_buffer_size.*#myisam_sort_buffer_size = 32M#" /etc/my.cnf
|
||||
sed -i "s#^thread_cache_size.*#thread_cache_size = 64#" /etc/my.cnf
|
||||
sed -i "s#^query_cache_size.*#query_cache_size = 64M#" /etc/my.cnf
|
||||
sed -i "s#^tmp_table_size.*#tmp_table_size = 64M#" /etc/my.cnf
|
||||
sed -i "s#^innodb_buffer_pool_size.*#innodb_buffer_pool_size = 512M#" /etc/my.cnf
|
||||
sed -i "s#^innodb_log_file_size.*#innodb_log_file_size = 128M#" /etc/my.cnf
|
||||
elif [[ ${MemTotal} -ge 8192 && ${MemTotal} -lt 16384 ]]; then
|
||||
sed -i "s#^key_buffer_size.*#key_buffer_size = 256M#" /etc/my.cnf
|
||||
sed -i "s#^table_open_cache.*#table_open_cache = 1024#" /etc/my.cnf
|
||||
sed -i "s#^sort_buffer_size.*#sort_buffer_size = 4M#" /etc/my.cnf
|
||||
sed -i "s#^read_buffer_size.*#read_buffer_size = 4M#" /etc/my.cnf
|
||||
sed -i "s#^myisam_sort_buffer_size.*#myisam_sort_buffer_size = 64M#" /etc/my.cnf
|
||||
sed -i "s#^thread_cache_size.*#thread_cache_size = 128#" /etc/my.cnf
|
||||
sed -i "s#^query_cache_size.*#query_cache_size = 128M#" /etc/my.cnf
|
||||
sed -i "s#^tmp_table_size.*#tmp_table_size = 128M#" /etc/my.cnf
|
||||
sed -i "s#^innodb_buffer_pool_size.*#innodb_buffer_pool_size = 1024M#" /etc/my.cnf
|
||||
sed -i "s#^innodb_log_file_size.*#innodb_log_file_size = 256M#" /etc/my.cnf
|
||||
elif [[ ${MemTotal} -ge 16384 && ${MemTotal} -lt 32768 ]]; then
|
||||
sed -i "s#^key_buffer_size.*#key_buffer_size = 512M#" /etc/my.cnf
|
||||
sed -i "s#^table_open_cache.*#table_open_cache = 2048#" /etc/my.cnf
|
||||
sed -i "s#^sort_buffer_size.*#sort_buffer_size = 8M#" /etc/my.cnf
|
||||
sed -i "s#^read_buffer_size.*#read_buffer_size = 8M#" /etc/my.cnf
|
||||
sed -i "s#^myisam_sort_buffer_size.*#myisam_sort_buffer_size = 128M#" /etc/my.cnf
|
||||
sed -i "s#^thread_cache_size.*#thread_cache_size = 256#" /etc/my.cnf
|
||||
sed -i "s#^query_cache_size.*#query_cache_size = 256M#" /etc/my.cnf
|
||||
sed -i "s#^tmp_table_size.*#tmp_table_size = 256M#" /etc/my.cnf
|
||||
sed -i "s#^innodb_buffer_pool_size.*#innodb_buffer_pool_size = 2048M#" /etc/my.cnf
|
||||
sed -i "s#^innodb_log_file_size.*#innodb_log_file_size = 512M#" /etc/my.cnf
|
||||
elif [[ ${MemTotal} -ge 32768 ]]; then
|
||||
sed -i "s#^key_buffer_size.*#key_buffer_size = 1024M#" /etc/my.cnf
|
||||
sed -i "s#^table_open_cache.*#table_open_cache = 4096#" /etc/my.cnf
|
||||
sed -i "s#^sort_buffer_size.*#sort_buffer_size = 16M#" /etc/my.cnf
|
||||
sed -i "s#^read_buffer_size.*#read_buffer_size = 16M#" /etc/my.cnf
|
||||
sed -i "s#^myisam_sort_buffer_size.*#myisam_sort_buffer_size = 256M#" /etc/my.cnf
|
||||
sed -i "s#^thread_cache_size.*#thread_cache_size = 512#" /etc/my.cnf
|
||||
sed -i "s#^query_cache_size.*#query_cache_size = 512M#" /etc/my.cnf
|
||||
sed -i "s#^tmp_table_size.*#tmp_table_size = 512M#" /etc/my.cnf
|
||||
sed -i "s#^innodb_buffer_pool_size.*#innodb_buffer_pool_size = 4096M#" /etc/my.cnf
|
||||
sed -i "s#^innodb_log_file_size.*#innodb_log_file_size = 1024M#" /etc/my.cnf
|
||||
fi
|
||||
}
|
||||
|
||||
wget -O /etc/my.cnf $Download_Url/install/conf/mysql-%s.conf -T 5
|
||||
MySQL_Opt
|
||||
''' % (version,)
|
||||
# 判断是否迁移目录
|
||||
if os.path.exists('data/datadir.pl'):
|
||||
newPath = public.readFile('data/datadir.pl')
|
||||
mycnf = public.readFile('/etc/my.cnf')
|
||||
mycnf = mycnf.replace('/www/server/data', newPath)
|
||||
public.writeFile('/etc/my.cnf', mycnf)
|
||||
|
||||
os.system(shellStr)
|
||||
WriteLog('TYPE_SOFE', 'MYSQL_CHECK_ERR')
|
||||
return True
|
||||
|
||||
|
||||
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)
|
||||
|
||||
|
||||
# 获取Token
|
||||
def GetToken():
|
||||
try:
|
||||
from json import loads
|
||||
tokenFile = 'data/token.json'
|
||||
if not os.path.exists(tokenFile):
|
||||
return False
|
||||
token = loads(public.readFile(tokenFile))
|
||||
return token
|
||||
except:
|
||||
return False
|
||||
|
||||
# 解密数据
|
||||
|
||||
|
||||
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
|
||||
|
||||
# 检查Token
|
||||
|
||||
|
||||
def checkToken(get):
|
||||
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
|
||||
|
||||
# 获取Web服务器
|
||||
|
||||
|
||||
def get_webserver():
|
||||
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
|
||||
@@ -1,8 +0,0 @@
|
||||
#!/bin/bash
|
||||
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin:/usr/local/lib/python2.7/bin
|
||||
|
||||
|
||||
|
||||
find . -name .DS_Store -print0 | xargs -0 git rm -f --ignore-unmatch
|
||||
find . -name .DS_Store | xargs rm -rf
|
||||
find . -type d -name "*.pyc" | xargs rm -rf
|
||||
@@ -1,117 +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")
|
||||
|
||||
export LC_ALL="en_US.UTF-8"
|
||||
|
||||
# echo $DIR
|
||||
|
||||
PATH=$PATH:$DIR/bin
|
||||
if [ -f ${DIR}/bin/activate ];then
|
||||
source ${DIR}/bin/activate
|
||||
|
||||
if [ "$?" != "0" ];then
|
||||
echo "load local python env fail!"
|
||||
fi
|
||||
fi
|
||||
|
||||
mw_start_task()
|
||||
{
|
||||
isStart=$(ps aux |grep 'panel_task.py'|grep -v grep|awk '{print $2}')
|
||||
if [ "$isStart" == '' ];then
|
||||
echo -e "starting mw-tasks... \c"
|
||||
cd $DIR && python3 panel_task.py >> ${DIR}/logs/panel_task.log 2>&1 &
|
||||
sleep 0.3
|
||||
isStart=$(ps aux |grep 'panel_task.py'|grep -v grep|awk '{print $2}')
|
||||
if [ "$isStart" == '' ];then
|
||||
echo -e "\033[31mfailed\033[0m"
|
||||
echo '------------------------------------------------------'
|
||||
tail -n 20 $DIR/logs/panel_task.log
|
||||
echo '------------------------------------------------------'
|
||||
echo -e "\033[31mError: mw-tasks service startup failed.\033[0m"
|
||||
return;
|
||||
fi
|
||||
echo -e "\033[32mdone\033[0m"
|
||||
else
|
||||
echo "starting mw-tasks... mw-tasks (pid $(echo $isStart)) already running"
|
||||
fi
|
||||
}
|
||||
|
||||
mw_start(){
|
||||
# 后台任务
|
||||
mw_start_task
|
||||
|
||||
cd ${DIR}/web && gunicorn -c setting.py app:app
|
||||
}
|
||||
|
||||
|
||||
mw_start_debug(){
|
||||
if [ ! -f $DIR/logs/panel_task.log ];then
|
||||
echo '' > $DIR/logs/panel_task.log
|
||||
fi
|
||||
|
||||
python3 panel_task.py >> $DIR/logs/panel_task.log 2>&1 &
|
||||
port=7200
|
||||
if [ -f /www/server/mdserver-web/data/port.pl ];then
|
||||
port=$(cat /www/server/mdserver-web/data/port.pl)
|
||||
fi
|
||||
|
||||
if [ -f ${DIR}/data/port.pl ];then
|
||||
port=$(cat ${DIR}/data/port.pl)
|
||||
fi
|
||||
cd ${DIR}/web && gunicorn -b :${port} -k eventlet -w 1 app:app
|
||||
# cd ${DIR}/web && gunicorn -b :${port} -k geventwebsocket.gunicorn.workers.GeventWebSocketWorker -w 1 app:app
|
||||
}
|
||||
|
||||
mw_start_panel(){
|
||||
port=7200
|
||||
if [ -f ${DIR}/data/port.pl ];then
|
||||
port=$(cat ${DIR}/data/port.pl)
|
||||
fi
|
||||
cd ${DIR}/web && gunicorn -b :${port} -k eventlet -w 1 app:app
|
||||
# cd ${DIR}/web && gunicorn -b :${port} -k geventwebsocket.gunicorn.workers.GeventWebSocketWorker -w 1 app:app
|
||||
|
||||
}
|
||||
|
||||
mw_start_bgtask(){
|
||||
cd ${DIR}/web && gunicorn -c setting.py app:app
|
||||
cd ${DIR} && python3 panel_task.py
|
||||
}
|
||||
|
||||
|
||||
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 panel_task.py | grep -v grep |awk '{print $2}'`
|
||||
for p in $PIDS
|
||||
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
|
||||
;;
|
||||
'panel')
|
||||
mw_stop
|
||||
mw_start_panel
|
||||
;;
|
||||
'task')
|
||||
# mw_stop
|
||||
mw_start_bgtask
|
||||
;;
|
||||
esac
|
||||
@@ -1,82 +0,0 @@
|
||||
### 常用命令说明
|
||||
|
||||
|
||||
- 面板相关命令
|
||||
|
||||
```
|
||||
mw default | 显示登录信息
|
||||
mw db | 快捷连接MySQL
|
||||
mw redis | 快捷连接Redis
|
||||
mw mongodb | 快捷连接MongoDB
|
||||
mw pgdb | 快捷连接PostgreSQL
|
||||
----------------------------------------
|
||||
mw open | 开启面板
|
||||
mw close | 关闭面板
|
||||
|
||||
mw debug | 开发测试
|
||||
mw venv | 进入虚拟环境
|
||||
mw mirror | 切换镜像
|
||||
mw install_app | 快捷安装常用软件
|
||||
mw update | 更新到正式
|
||||
mw dev/update_dev | 更新到开发
|
||||
|
||||
service mw [start|stop|reload|restart|status]
|
||||
```
|
||||
|
||||
- OpenResty
|
||||
|
||||
```
|
||||
|
||||
systemctl [start|stop|reload|restart|status] openresty
|
||||
|
||||
```
|
||||
|
||||
- MySQL
|
||||
|
||||
```
|
||||
|
||||
systemctl [start|stop|reload|restart|status] mysql
|
||||
|
||||
```
|
||||
|
||||
- MariaDB
|
||||
|
||||
```
|
||||
|
||||
systemctl [start|stop|reload|restart|status] mariadb
|
||||
|
||||
```
|
||||
|
||||
- PHP
|
||||
|
||||
```
|
||||
|
||||
systemctl [start|stop|reload|restart|status] php[54-81]
|
||||
|
||||
systemctl start php71
|
||||
```
|
||||
|
||||
- Redis
|
||||
|
||||
```
|
||||
|
||||
systemctl [start|stop|reload|restart|status] redis
|
||||
|
||||
```
|
||||
|
||||
- Memcached
|
||||
|
||||
```
|
||||
|
||||
systemctl [start|stop|reload|restart|status] memcached
|
||||
|
||||
```
|
||||
|
||||
|
||||
- sphinx
|
||||
|
||||
```
|
||||
|
||||
systemctl [start|stop|reload|restart|status] sphinx
|
||||
|
||||
```
|
||||
@@ -1,71 +0,0 @@
|
||||
### 兼容性测试报告
|
||||
|
||||
快速测试命令
|
||||
|
||||
```
|
||||
cd /www/server/mdserver-web/scripts/quick && bash debug.sh
|
||||
cd /www/server/mdserver-web/plugins/php/versions && bash all_test.sh
|
||||
```
|
||||
|
||||
|
||||
| 系统名称 | 面板 | OpenResty |
|
||||
| ----------------- |---------------|---------------|
|
||||
| CentOS 7.9 |✅ |✅ |
|
||||
| CentOS 8.4 |✅ |✅ |
|
||||
| CentOS 8 Stream |✅ |✅ |
|
||||
| CentOS 9 Stream |✅ |✅ |
|
||||
| Debian 10.3 |✅ |✅ |
|
||||
| Debian 11.3 |✅ |✅ |
|
||||
| Debian 12.0 |✅ |✅ |
|
||||
| Ubuntu 18.04 |✅ |✅ |
|
||||
| Ubuntu 20.04 |✅ |✅ |
|
||||
| Ubuntu 22.04 |✅ |✅ |
|
||||
| Fedora 31 |✅ |✅ |
|
||||
| Fedora 32 |✅ |✅ |
|
||||
| AlmaLinux 9 |✅ |✅ |
|
||||
| RockyLinux 8.6 |✅ |✅ |
|
||||
| Arch Linux |✅ |✅ |
|
||||
| openSUSE 15.4 |✅ |✅ |
|
||||
|
||||
|
||||
|
||||
| 系统名称 |PHP53 |PHP54 |PHP55 |PHP56 |PHP70 |PHP71 |PHP72 |PHP73 |PHP74 |PHP80 |PHP81 |PHP82 |PHP83 |PHP84 |
|
||||
| ----------------- |-------|-------|-------|-------|-------|-------|-------|-------|-------|-------|-------|-------|-------|-------|
|
||||
| CentOS 7.9 |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |
|
||||
| CentOS 8.4 |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |
|
||||
| CentOS 8 Stream |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |
|
||||
| CentOS 9 Stream |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |
|
||||
| Debian 10.3 |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |
|
||||
| Debian 11.3 |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |
|
||||
| Debian 12 |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |
|
||||
| Ubuntu 18.04 |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |
|
||||
| Ubuntu 20.04 |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |
|
||||
| Ubuntu 22.04 |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |
|
||||
| Fedora 31 |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |
|
||||
| Fedora 32 |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |
|
||||
| AlmaLinux 9 |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |
|
||||
| RockyLinux 8.6 |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |
|
||||
| Arch Linux |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |
|
||||
| openSUSE 15.4 |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |
|
||||
|
||||
|
||||
|
||||
| 系统名称 | MySQL55|MySQL56|MySQL57|MySQL80|
|
||||
| ----------------- |-------|--------|-------|-------|
|
||||
| CentOS 7.9 |✅ |✅ |✅ |✅ |
|
||||
| CentOS 8.4 |✅ |✅ |✅ |✅ |
|
||||
| CentOS 8 Stream |✅ |✅ |✅ |✅ |
|
||||
| CentOS 9 Stream |✅ |✅ |✅ |✅ |
|
||||
| Debian 10.3 |✅ |✅ |✅ |✅ |
|
||||
| Debian 11.3 |✅ |✅ |✅ |✅ |
|
||||
| Debian 12 |✅ |✅ |✅ |✅ |
|
||||
| Ubuntu 18.04 |✅ |✅ |✅ |✅ |
|
||||
| Ubuntu 20.04 |✅ |✅ |✅ |✅ |
|
||||
| Ubuntu 22.04 |✅ |✅ |✅ |✅ |
|
||||
| Fedora 31 |✅ |✅ |✅ |✅ |
|
||||
| Fedora 32 |✅ |✅ |✅ |✅ |
|
||||
| AlmaLinux 9 |✅ |✅ |✅ |✅ |
|
||||
| RockyLinux 8.6 |✅ |✅ |✅ |✅ |
|
||||
| Arch Linux |✅ |✅ |✅ |✅ |
|
||||
| openSUSE 15.4 |✅ |✅ |✅ |✅ |
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
# 自动生成
|
||||
|
||||
certificate.pem - 证书信息
|
||||
privateKey.pem - 私钥
|
||||
@@ -0,0 +1,22 @@
|
||||
[
|
||||
{
|
||||
"title":"全部",
|
||||
"type":0,
|
||||
"ps":""
|
||||
},
|
||||
{
|
||||
"title":"运行环境",
|
||||
"type":1,
|
||||
"ps":""
|
||||
},
|
||||
{
|
||||
"title":"系统工具",
|
||||
"type":2,
|
||||
"ps":""
|
||||
},
|
||||
{
|
||||
"title":"其他插件",
|
||||
"type":3,
|
||||
"ps":""
|
||||
}
|
||||
]
|
||||
@@ -1,62 +0,0 @@
|
||||
FROM debian:10.12-slim
|
||||
|
||||
STOPSIGNAL SIGRTMIN+3
|
||||
|
||||
RUN rm -f /lib/systemd/system/multi-user.target.wants/* \
|
||||
/etc/systemd/system/*.wants/* \
|
||||
/lib/systemd/system/local-fs.target.wants/* \
|
||||
/lib/systemd/system/sockets.target.wants/*udev* \
|
||||
/lib/systemd/system/sockets.target.wants/*initctl* \
|
||||
/lib/systemd/system/sysinit.target.wants/systemd-tmpfiles-setup* \
|
||||
/lib/systemd/system/systemd-update-utmp*
|
||||
|
||||
RUN apt update -y && \
|
||||
apt install -y devscripts && \
|
||||
apt install -y wget zip unzip && \
|
||||
apt-get install -y locales && \
|
||||
localedef -i en_US -c -f UTF-8 -A /usr/share/locale/locale.alias en_US.UTF-8 && \
|
||||
locale-gen en_US.UTF-8 && \
|
||||
export LC_ALL=en_US.UTF-8 && \
|
||||
export LANG=en_US.UTF-8 && \
|
||||
export LANGUAGE=en_US.UTF-8 && \
|
||||
localedef -i en_US -f UTF-8 en_US.UTF-8 && \
|
||||
mkdir -p /www/server && \
|
||||
mkdir -p /www/wwwroot && \
|
||||
mkdir -p /www/wwwlogs && \
|
||||
mkdir -p /www/backup/database && \
|
||||
mkdir -p /www/backup/site && \
|
||||
wget -O /tmp/dev.zip https://github.com/midoks/mdserver-web/archive/refs/heads/dev.zip && \
|
||||
cd /tmp && unzip /tmp/dev.zip && \
|
||||
mv -f /tmp/mdserver-web-dev /www/server/mdserver-web && \
|
||||
rm -rf /tmp/dev.zip && \
|
||||
rm -rf /tmp/mdserver-web-dev && \
|
||||
cd /www/server/mdserver-web && \
|
||||
bash scripts/install/debian.sh && \
|
||||
cd /www/server/mdserver-web/ && \
|
||||
/www/server/mdserver-web/bin/python tools.py username username && \
|
||||
cd /www/server/mdserver-web/ && \
|
||||
/www/server/mdserver-web/bin/python tools.py panel password
|
||||
|
||||
RUN cd /www/server/mdserver-web/plugins/php && \
|
||||
bash install.sh install 74 && \
|
||||
cd /www/server/mdserver-web/plugins/openresty && \
|
||||
bash install.sh install 1.21.4 && \
|
||||
cd /www/server/mdserver-web/plugins/mysql && \
|
||||
bash install.sh install 5.6 && \
|
||||
cd /www/server/mdserver-web/plugins/phpmyadmin && \
|
||||
bash install.sh install 4.4.15 && \
|
||||
systemctl enable openresty && \
|
||||
systemctl enable php74 && \
|
||||
systemctl enable mysql
|
||||
|
||||
RUN rm -rf /www/server/mysql/data
|
||||
|
||||
ADD ./start.sh /start.sh
|
||||
ADD start.service /usr/lib/systemd/system/start.service
|
||||
#RUN systemctl enable start
|
||||
|
||||
CMD [ "/lib/systemd/systemd", "log-level=info", "unit=sysinit.target" ]
|
||||
|
||||
EXPOSE 7200 80 443 888
|
||||
|
||||
VOLUME [ "/www" ]
|
||||
@@ -1,9 +0,0 @@
|
||||
[Unit]
|
||||
Description=Customize the script
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
ExecStart=/start.sh
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
@@ -1 +0,0 @@
|
||||
#!/bin/bash
|
||||
@@ -0,0 +1,13 @@
|
||||
#!/bin/bash
|
||||
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
|
||||
export PATH
|
||||
LANG=en_US.UTF-8
|
||||
is64bit=`getconf LONG_BIT`
|
||||
|
||||
startTime=`date +%s`
|
||||
|
||||
echo "hello"
|
||||
|
||||
endTime=`date +%s`
|
||||
((outTime=($endTime-$startTime)/60))
|
||||
echo -e "Time consumed:\033[32m $outTime \033[0mMinute!"
|
||||
@@ -1 +0,0 @@
|
||||
logs
|
||||
@@ -1,359 +0,0 @@
|
||||
# coding: utf-8
|
||||
|
||||
# ---------------------------------------------------------------------------------
|
||||
# MW-Linux面板
|
||||
# ---------------------------------------------------------------------------------
|
||||
# copyright (c) 2018-∞(https://github.com/midoks/mdserver-web) All rights reserved.
|
||||
# ---------------------------------------------------------------------------------
|
||||
# Author: midoks <midoks@163.com>
|
||||
# ---------------------------------------------------------------------------------
|
||||
|
||||
# ---------------------------------------------------------------------------------
|
||||
# 计划任务
|
||||
# ---------------------------------------------------------------------------------
|
||||
|
||||
import sys
|
||||
import os
|
||||
import json
|
||||
import time
|
||||
import threading
|
||||
import psutil
|
||||
|
||||
|
||||
web_dir = os.getcwd() + "/web"
|
||||
os.chdir(web_dir)
|
||||
sys.path.append(web_dir)
|
||||
|
||||
import core.mw as mw
|
||||
import thisdb
|
||||
|
||||
from admin import setup
|
||||
setup.init()
|
||||
|
||||
g_log_file = mw.getPanelTaskExecLog()
|
||||
if not os.path.exists(g_log_file):
|
||||
os.system("touch " + g_log_file)
|
||||
|
||||
def execShell(cmdstring, cwd=None, timeout=None, shell=True):
|
||||
cmd = cmdstring + ' > ' + g_log_file + ' 2>&1'
|
||||
return mw.execShell(cmd)
|
||||
|
||||
def writeLogs(data):
|
||||
# 写输出日志
|
||||
try:
|
||||
fp = open(g_log_file, 'w+')
|
||||
fp.write(data)
|
||||
fp.close()
|
||||
except:
|
||||
pass
|
||||
|
||||
def downloadFile(url, filename):
|
||||
# 下载文件
|
||||
try:
|
||||
import urllib
|
||||
import socket
|
||||
socket.setdefaulttimeout(300)
|
||||
|
||||
headers = ('User-Agent', 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36')
|
||||
opener = urllib.request.build_opener()
|
||||
opener.addheaders = [headers]
|
||||
urllib.request.install_opener(opener)
|
||||
|
||||
urllib.request.urlretrieve(url, filename=filename, reporthook=downloadHook)
|
||||
|
||||
if not mw.isAppleSystem():
|
||||
os.system('chown www.www ' + filename)
|
||||
|
||||
writeLogs(filename + ' download success!')
|
||||
except Exception as e:
|
||||
writeLogs(str(e))
|
||||
return True
|
||||
|
||||
def downloadHook(count, blockSize, totalSize):
|
||||
# 下载文件进度回调
|
||||
used = count * blockSize
|
||||
pre = int((100.0 * used / totalSize))
|
||||
speed = {'total': totalSize, 'used': used, 'pre': pre}
|
||||
writeLogs(json.dumps(speed))
|
||||
|
||||
def runPanelTask():
|
||||
# 站点过期检查
|
||||
siteEdateCheck()
|
||||
|
||||
lock_file = mw.getTriggerTaskLockFile()
|
||||
try:
|
||||
if os.path.exists(lock_file):
|
||||
bash_list = thisdb.getTaskList(status=-1)
|
||||
for task in bash_list:
|
||||
thisdb.setTaskStatus(task['id'], 0)
|
||||
|
||||
run_list = thisdb.getTaskList(status=0)
|
||||
for run_task in run_list:
|
||||
start = int(time.time())
|
||||
thisdb.setTaskData(run_task['id'], start=start)
|
||||
thisdb.setTaskStatus(run_task['id'], -1)
|
||||
|
||||
if run_task['type'] == 'download':
|
||||
argv = run_task['cmd'].split('|mw|')
|
||||
downloadFile(argv[0], argv[1])
|
||||
elif run_task['type'] == 'execshell':
|
||||
execShell(run_task['cmd'])
|
||||
|
||||
end = int(time.time())
|
||||
thisdb.setTaskData(run_task['id'], end=end)
|
||||
thisdb.setTaskStatus(run_task['id'], 1)
|
||||
|
||||
if thisdb.getTaskUnexecutedCount() < 1:
|
||||
os.remove(lock_file)
|
||||
except Exception as e:
|
||||
print('runPanelTask:',mw.getTracebackInfo())
|
||||
|
||||
# 网站到期处理
|
||||
def siteEdateCheck():
|
||||
try:
|
||||
from utils.site import sites as MwSites
|
||||
website_edate = thisdb.getOption('website_edate', default='0000-00-00')
|
||||
now_time_ymd = time.strftime('%Y-%m-%d', time.localtime())
|
||||
|
||||
if website_edate == now_time_ymd:
|
||||
return False
|
||||
site_list = thisdb.getSitesEdateList(now_time_ymd)
|
||||
for site in site_list:
|
||||
MwSites.instance().stop(site['id'])
|
||||
thisdb.setOption('website_edate', now_time_ymd)
|
||||
except Exception as e:
|
||||
print('siteEdateCheck:',mw.getTracebackInfo())
|
||||
|
||||
# 任务队列
|
||||
def startPanelTask():
|
||||
try:
|
||||
while True:
|
||||
runPanelTask()
|
||||
time.sleep(5)
|
||||
except Exception as e:
|
||||
print('startPanelTask:',mw.getTracebackInfo())
|
||||
time.sleep(30)
|
||||
startPanelTask()
|
||||
|
||||
def systemTask():
|
||||
# 系统监控任务
|
||||
from utils.system import monitor
|
||||
try:
|
||||
while True:
|
||||
monitor.instance().run()
|
||||
time.sleep(5)
|
||||
except Exception as ex:
|
||||
print('systemTask:',mw.getTracebackInfo())
|
||||
time.sleep(30)
|
||||
systemTask()
|
||||
|
||||
|
||||
def panelPluginStatusCheck():
|
||||
# 系统监控任务
|
||||
from utils.plugin import plugin
|
||||
try:
|
||||
while True:
|
||||
# start_t = time.time()
|
||||
plugin.instance().autoCachePluginStatus()
|
||||
# end_t = time.time()
|
||||
time.sleep(60)
|
||||
except Exception as ex:
|
||||
print('panelPluginStatusCheck:',mw.getTracebackInfo())
|
||||
time.sleep(120)
|
||||
panelPluginStatusCheck()
|
||||
|
||||
# -------------------------------------- PHP监控 start --------------------------------------------- #
|
||||
# 502错误检查线程
|
||||
def check502Task():
|
||||
try:
|
||||
check_file = mw.getPanelDir() + '/data/502Task.pl'
|
||||
while True:
|
||||
if os.path.exists(check_file):
|
||||
check502()
|
||||
time.sleep(10)
|
||||
except:
|
||||
time.sleep(30)
|
||||
check502Task()
|
||||
|
||||
|
||||
def check502():
|
||||
try:
|
||||
verlist = [
|
||||
'52', '53', '54', '55', '56', '70',
|
||||
'71', '72', '73', '74', '80', '81',
|
||||
'82', '83', '84'
|
||||
]
|
||||
for ver in verlist:
|
||||
server_dir = mw.getServerDir()
|
||||
php_path = server_dir + '/php/' + ver + '/sbin/php-fpm'
|
||||
if not os.path.exists(php_path):
|
||||
continue
|
||||
if checkPHPVersion(ver):
|
||||
continue
|
||||
if startPHPVersion(ver):
|
||||
print('检测到PHP-' + ver + '处理异常,已自动修复!')
|
||||
mw.writeLog('PHP守护程序', '检测到PHP-' + ver + '处理异常,已自动修复!')
|
||||
|
||||
except Exception as e:
|
||||
mw.writeLog('PHP守护程序', '自动修复异常:'+str(e))
|
||||
|
||||
|
||||
# 处理指定PHP版本
|
||||
def startPHPVersion(version):
|
||||
server_dir = mw.getServerDir()
|
||||
try:
|
||||
# system
|
||||
phpService = mw.systemdCfgDir() + '/php' + version + '.service'
|
||||
if os.path.exists(phpService):
|
||||
mw.execShell("systemctl restart php" + version)
|
||||
if checkPHPVersion(version):
|
||||
return True
|
||||
|
||||
# initd
|
||||
fpm = server_dir + '/php/init.d/php' + version
|
||||
php_path = server_dir + '/php/' + version + '/sbin/php-fpm'
|
||||
if not os.path.exists(php_path):
|
||||
if os.path.exists(fpm):
|
||||
os.remove(fpm)
|
||||
return False
|
||||
|
||||
if not os.path.exists(fpm):
|
||||
return False
|
||||
|
||||
# 尝试重载服务
|
||||
os.system(fpm + ' reload')
|
||||
if checkPHPVersion(version):
|
||||
return True
|
||||
# 尝试重启服务
|
||||
cgi = '/tmp/php-cgi-' + version + '.sock'
|
||||
pid = server_dir + '/php/' + version + '/var/run/php-fpm.pid'
|
||||
data = mw.execShell("ps -ef | grep php/" + version +" | grep -v grep|grep -v python |awk '{print $2}'")
|
||||
if data[0] != '':
|
||||
os.system("ps -ef | grep php/" + version + " | grep -v grep|grep -v python |awk '{print $2}' | xargs kill ")
|
||||
time.sleep(0.5)
|
||||
if not os.path.exists(cgi):
|
||||
os.system('rm -f ' + cgi)
|
||||
if not os.path.exists(pid):
|
||||
os.system('rm -f ' + pid)
|
||||
os.system(fpm + ' start')
|
||||
if checkPHPVersion(version):
|
||||
return True
|
||||
|
||||
# 检查是否正确启动
|
||||
if os.path.exists(cgi):
|
||||
return True
|
||||
except Exception as e:
|
||||
print('startPHPVersion:',mw.getTracebackInfo())
|
||||
mw.writeLog('PHP守护程序', '自动修复异常:'+str(e))
|
||||
return True
|
||||
|
||||
|
||||
def checkPHPVersion(version):
|
||||
# 检查指定PHP版本
|
||||
try:
|
||||
sock = mw.getFpmAddress(version)
|
||||
data = mw.requestFcgiPHP(sock, '/phpfpm_status_' + version + '?json')
|
||||
result = str(data, encoding='utf-8')
|
||||
except Exception as e:
|
||||
result = 'Bad Gateway'
|
||||
# 检查openresty
|
||||
if result.find('Bad Gateway') != -1:
|
||||
return False
|
||||
if result.find('HTTP Error 404: Not Found') != -1:
|
||||
return False
|
||||
|
||||
# 检查Web服务是否启动
|
||||
if result.find('Connection refused') != -1:
|
||||
return False
|
||||
return True
|
||||
|
||||
# -------------------------------------- PHP监控 end --------------------------------------------- #
|
||||
|
||||
|
||||
# --------------------------------------OpenResty Auto Restart Start --------------------------------------------- #
|
||||
# 解决acme.sh续签后,未起效。
|
||||
def openrestyAutoRestart():
|
||||
try:
|
||||
while True:
|
||||
# 检查是否安装
|
||||
odir = mw.getServerDir() + '/openresty'
|
||||
if not os.path.exists(odir):
|
||||
time.sleep(86400)
|
||||
continue
|
||||
mw.opWeb('reload')
|
||||
time.sleep(86400)
|
||||
except Exception as e:
|
||||
mw.writeLog('OpenResty检测', '自动修复异常:'+str(e))
|
||||
time.sleep(86400)
|
||||
|
||||
# --------------------------------------OpenResty Auto Restart End --------------------------------------------- #
|
||||
|
||||
# ------------------------------------ OpenResty Restart At Once Start ------------------------------------------ #
|
||||
|
||||
|
||||
def openrestyRestartAtOnce():
|
||||
restart_nginx_tip = mw.getPanelDir()+'/data/restart_nginx.pl'
|
||||
while True:
|
||||
if os.path.exists(restart_nginx_tip):
|
||||
os.remove(restart_nginx_tip)
|
||||
mw.opWeb('reload')
|
||||
time.sleep(1)
|
||||
# ----------------------------------- OpenResty Restart At Once End ------------------------------------------ #
|
||||
|
||||
|
||||
# --------------------------------------Panel Restart Start --------------------------------------------- #
|
||||
def restartPanelService():
|
||||
restart_tip = mw.getPanelDir()+'/data/restart.pl'
|
||||
while True:
|
||||
if os.path.exists(restart_tip):
|
||||
print("restart panel")
|
||||
os.remove(restart_tip)
|
||||
mw.panelCmd('restart_panel')
|
||||
time.sleep(1)
|
||||
# --------------------------------------Panel Restart End --------------------------------------------- #
|
||||
|
||||
|
||||
def setDaemon(t):
|
||||
if sys.version_info.major == 3 and sys.version_info.minor >= 10:
|
||||
t.daemon = True
|
||||
else:
|
||||
t.setDaemon(True)
|
||||
return t
|
||||
|
||||
def run():
|
||||
# 系统监控
|
||||
sysTask = threading.Thread(target=systemTask)
|
||||
sysTask = setDaemon(sysTask)
|
||||
sysTask.start()
|
||||
|
||||
# PHP 502错误检查线程
|
||||
php502 = threading.Thread(target=check502Task)
|
||||
php502 = setDaemon(php502)
|
||||
php502.start()
|
||||
|
||||
# OpenResty Restart At Once Start
|
||||
oraos = threading.Thread(target=openrestyRestartAtOnce)
|
||||
oraos = setDaemon(oraos)
|
||||
oraos.start()
|
||||
|
||||
# OpenResty Auto Restart Start
|
||||
oar = threading.Thread(target=openrestyAutoRestart)
|
||||
oar = setDaemon(oar)
|
||||
oar.start()
|
||||
|
||||
# Panel Plugin Status Check
|
||||
pps = threading.Thread(target=panelPluginStatusCheck)
|
||||
pps = setDaemon(pps)
|
||||
pps.start()
|
||||
|
||||
# Panel Restart Start
|
||||
rps = threading.Thread(target=restartPanelService)
|
||||
rps = setDaemon(rps)
|
||||
rps.start()
|
||||
|
||||
# 面板后台任务
|
||||
startPanelTask()
|
||||
|
||||
if __name__ == "__main__":
|
||||
run()
|
||||
|
||||
@@ -1,367 +0,0 @@
|
||||
# coding=utf-8
|
||||
|
||||
# ---------------------------------------------------------------------------------
|
||||
# MW-Linux面板
|
||||
# ---------------------------------------------------------------------------------
|
||||
# copyright (c) 2018-∞(https://github.com/midoks/mdserver-web) All rights reserved.
|
||||
# ---------------------------------------------------------------------------------
|
||||
# Author: midoks <midoks@163.com>
|
||||
# ---------------------------------------------------------------------------------
|
||||
|
||||
# ---------------------------------------------------------------------------------
|
||||
# 工具箱
|
||||
# ---------------------------------------------------------------------------------
|
||||
|
||||
|
||||
import sys
|
||||
import os
|
||||
import json
|
||||
import time
|
||||
import re
|
||||
|
||||
web_dir = os.getcwd() + "/web"
|
||||
os.chdir(web_dir)
|
||||
sys.path.append(web_dir)
|
||||
|
||||
from utils.firewall import Firewall as MwFirewall
|
||||
import core.mw as mw
|
||||
import thisdb
|
||||
|
||||
INIT_DIR = "/etc/rc.d/init.d"
|
||||
if mw.isAppleSystem():
|
||||
INIT_DIR = mw.getPanelDir() + "/scripts/init.d"
|
||||
|
||||
INIT_CMD = INIT_DIR + "/mw"
|
||||
|
||||
|
||||
def mw_input_cmd(msg):
|
||||
if sys.version_info[0] == 2:
|
||||
in_val = raw_input(msg)
|
||||
else:
|
||||
in_val = input(msg)
|
||||
return in_val
|
||||
|
||||
def getRemainLen(cmd, max_length=100):
|
||||
cmd_len = len(cmd)
|
||||
cmd_u8_len = len(cmd.encode('utf-8'))
|
||||
return max_length-int((cmd_u8_len - cmd_len)/2+cmd_len)
|
||||
|
||||
def mwcli(mw_input=0):
|
||||
panel_dir = mw.getPanelDir()
|
||||
|
||||
raw_tip = "========================================================================"
|
||||
if not mw_input:
|
||||
print("========================mdserver-web cli tools==========================")
|
||||
cmd_list = [
|
||||
'(1) 重启面板服务',
|
||||
'(2) 停止面板服务',
|
||||
'(3) 启动面板服务',
|
||||
'(4) 重载面板服务',
|
||||
'(5) 修改面板端口',
|
||||
'(10) 查看面板默认信息',
|
||||
'(11) 修改面板密码',
|
||||
'(12) 修改面板用户名',
|
||||
'(13) 显示面板错误日志',
|
||||
'(14) 关闭面板访问',
|
||||
'(15) 开启面板访问',
|
||||
'(20) 关闭BasicAuth认证',
|
||||
'(21) 解除域名绑定',
|
||||
'(22) 解除面板SSL绑定',
|
||||
'(23) 开启IPV6支持',
|
||||
'(24) 关闭IPV6支持',
|
||||
'(25) 开启防火墙SSH端口',
|
||||
'(26) 关闭二次验证',
|
||||
'(27) 查看防火墙信息',
|
||||
'(28) 自动识别防火墙端口到面板',
|
||||
'(100) 开启PHP52显示',
|
||||
'(101) 关闭PHP52显示',
|
||||
'(200) 切换Linux系统软件源',
|
||||
'(201) 简单速度测试',
|
||||
'(0) 取消'
|
||||
]
|
||||
cmd_list_num = len(cmd_list)
|
||||
for index in range(cmd_list_num):
|
||||
cmd = cmd_list[index]
|
||||
if index % 2 == 0:
|
||||
if index == (cmd_list_num-1):
|
||||
print(cmd)
|
||||
else:
|
||||
print(cmd + " " * getRemainLen(cmd, 40), end="")
|
||||
if index % 2 == 1:
|
||||
print(cmd)
|
||||
print(raw_tip)
|
||||
try:
|
||||
mw_input = input("请输入命令编号:")
|
||||
if sys.version_info[0] == 3:
|
||||
mw_input = int(mw_input)
|
||||
except:
|
||||
mw_input = 0
|
||||
|
||||
nums = [
|
||||
1, 2, 3, 4, 5, 10, 11, 12, 13, 14, 15,
|
||||
20, 21, 22, 23, 24, 25, 26, 27, 28,
|
||||
100, 101,
|
||||
200, 201
|
||||
]
|
||||
if not mw_input in nums:
|
||||
print(raw_tip)
|
||||
print("已取消!")
|
||||
exit()
|
||||
|
||||
if mw_input == 1:
|
||||
os.system(INIT_CMD + " restart")
|
||||
elif mw_input == 2:
|
||||
os.system(INIT_CMD + " stop")
|
||||
elif mw_input == 3:
|
||||
os.system(INIT_CMD + " start")
|
||||
elif mw_input == 4:
|
||||
os.system(INIT_CMD + " reload")
|
||||
elif mw_input == 5:
|
||||
in_port = mw_input_cmd("请输入新的面板端口:")
|
||||
in_port_int = int(in_port.strip())
|
||||
if in_port_int < 65536 and in_port_int > 0:
|
||||
MwFirewall.instance().addAcceptPort(in_port, 'WEB面板[TOOLS修改]', 'port')
|
||||
panel_port = panel_dir + '/data/port.pl'
|
||||
mw.writeFile(panel_port, in_port)
|
||||
os.system(INIT_CMD + " restart_panel")
|
||||
os.system(INIT_CMD + " default")
|
||||
else:
|
||||
mw.echoInfo("端口范围在0-65536之间")
|
||||
return
|
||||
elif mw_input == 10:
|
||||
os.system(INIT_CMD + " default")
|
||||
elif mw_input == 11:
|
||||
input_pwd = mw_input_cmd("请输入新的面板密码:")
|
||||
if len(input_pwd.strip()) < 5:
|
||||
mw.echoInfo("错误,密码长度不能小于5位")
|
||||
return
|
||||
set_panel_pwd(input_pwd.strip(), True)
|
||||
elif mw_input == 12:
|
||||
input_user = mw_input_cmd("请输入新的面板用户名(>=5位):")
|
||||
set_panel_username(input_user.strip())
|
||||
elif mw_input == 13:
|
||||
os.system('tail -100 ' + panel_dir + '/logs/panel_error.log')
|
||||
elif mw_input == 14:
|
||||
admin_close = thisdb.getOption('admin_close')
|
||||
if admin_close == 'no':
|
||||
thisdb.setOption('admin_close', 'yes')
|
||||
mw.echoInfo("关闭面板访问成功!")
|
||||
else:
|
||||
mw.echoInfo("已关闭面板访问!")
|
||||
elif mw_input == 15:
|
||||
admin_close = thisdb.getOption('admin_close')
|
||||
if admin_close == 'yes':
|
||||
thisdb.setOption('admin_close', 'no')
|
||||
mw.echoInfo("开启面板访问成功!")
|
||||
else:
|
||||
mw.echoInfo("已开启面板访问!")
|
||||
elif mw_input == 20:
|
||||
basic_auth = thisdb.getOptionByJson('basic_auth', default={'open':False})
|
||||
if basic_auth['open']:
|
||||
basic_auth['open'] = False
|
||||
thisdb.setOption('basic_auth', json.dumps(basic_auth))
|
||||
os.system(INIT_CMD + " restart")
|
||||
mw.echoInfo("关闭basic_auth成功")
|
||||
elif mw_input == 21:
|
||||
panel_domain = thisdb.getOption('panel_domain', default='')
|
||||
if panel_domain != '':
|
||||
thisdb.setOption('panel_domain', '')
|
||||
os.system(INIT_CMD + " unbind_domain")
|
||||
mw.echoInfo("解除域名绑定成功")
|
||||
else:
|
||||
mw.echoInfo("面板未绑定域名!")
|
||||
elif mw_input == 22:
|
||||
panel_ssl = thisdb.getOptionByJson('panel_ssl', default={'open':False})
|
||||
if panel_ssl['open']:
|
||||
panel_ssl['open'] = False
|
||||
thisdb.setOption('panel_ssl', json.dumps(panel_ssl))
|
||||
os.system(INIT_CMD + " unbind_ssl")
|
||||
mw.echoInfo("解除面板SSL绑定成功")
|
||||
elif mw_input == 23:
|
||||
listen_ipv6 = panel_dir + '/data/ipv6.pl'
|
||||
if not os.path.exists(listen_ipv6):
|
||||
mw.writeFile(listen_ipv6, 'True')
|
||||
os.system(INIT_CMD + " restart")
|
||||
mw.echoInfo("开启IPv6支持了")
|
||||
else:
|
||||
mw.echoInfo("已开启IPv6支持!")
|
||||
elif mw_input == 24:
|
||||
listen_ipv6 = panel_dir + '/data/ipv6.pl'
|
||||
if not os.path.exists(listen_ipv6):
|
||||
mw.echoInfo("已关闭IPv6支持!")
|
||||
else:
|
||||
os.remove(listen_ipv6)
|
||||
os.system(INIT_CMD + " restart")
|
||||
mw.echoInfo("关闭IPv6支持了")
|
||||
elif mw_input == 25:
|
||||
open_ssh_port()
|
||||
mw.echoInfo("已开启!")
|
||||
elif mw_input == 26:
|
||||
two_step_verification = thisdb.getOptionByJson('two_step_verification', default={'open':False})
|
||||
if two_step_verification['open']:
|
||||
two_step_verification['open'] = False
|
||||
thisdb.setOption('two_step_verification', json.dumps(two_step_verification))
|
||||
mw.echoInfo("关闭二次验证成功!")
|
||||
else:
|
||||
mw.echoInfo("二次验证已关闭!")
|
||||
elif mw_input == 27:
|
||||
cmd = 'which ufw'
|
||||
run_cmd = False
|
||||
find_cmd = mw.execShell(cmd)
|
||||
if find_cmd[0].strip() != '':
|
||||
run_cmd = True
|
||||
os.system('ufw status')
|
||||
|
||||
cmd = 'which firewall-cmd'
|
||||
find_cmd = mw.execShell(cmd)
|
||||
if find_cmd[0].strip() != '':
|
||||
run_cmd = True
|
||||
os.system('firewall-cmd --list-all')
|
||||
if not run_cmd:
|
||||
mw.echoInfo("未检测到防火墙!")
|
||||
elif mw_input == 28:
|
||||
from utils.firewall import Firewall as MwFirewall
|
||||
MwFirewall.instance().aIF()
|
||||
mw.echoInfo("执行自动识别防火墙端口到面板成功!")
|
||||
elif mw_input == 100:
|
||||
php_conf = panel_dir + '/plugins/php/info.json'
|
||||
if os.path.exists(php_conf):
|
||||
cont = mw.readFile(php_conf)
|
||||
cont = re.sub("\"53\"", "\"52\",\"53\"", cont)
|
||||
cont = re.sub("\"5.3.29\"", "\"5.2.17\",\"5.3.29\"", cont)
|
||||
mw.writeFile(php_conf, cont)
|
||||
mw.echoInfo("执行PHP52显示成功!")
|
||||
elif mw_input == 101:
|
||||
php_conf = panel_dir + '/plugins/php/info.json'
|
||||
if os.path.exists(php_conf):
|
||||
cont = mw.readFile(php_conf)
|
||||
cont = re.sub("\"52\",", "", cont)
|
||||
cont = re.sub("\"5.2.17\",", cont)
|
||||
mw.writeFile(php_conf, cont)
|
||||
mw.echoInfo("执行PHP52隐藏成功!")
|
||||
elif mw_input == 200:
|
||||
os.system(INIT_CMD + " mirror")
|
||||
elif mw_input == 201:
|
||||
os.system('curl -Lso- bench.sh | bash')
|
||||
|
||||
|
||||
def open_ssh_port():
|
||||
|
||||
find_ssh_port_cmd = "cat /etc/ssh/sshd_config | grep '^Port \\d*' | tail -1"
|
||||
cmd_data = mw.execShell(find_ssh_port_cmd)
|
||||
ssh_port = cmd_data[0].replace("Port ", '').strip()
|
||||
if ssh_port == '':
|
||||
ssh_port = '22'
|
||||
|
||||
mw.echoInfo("SSH端口: "+ str(ssh_port))
|
||||
MwFirewall.instance().addAcceptPort(ssh_port, 'SSH远程管理服务', 'port')
|
||||
return True
|
||||
|
||||
|
||||
def set_panel_pwd(password, ncli=False):
|
||||
info = thisdb.getUserByRoot()
|
||||
thisdb.setUserByRoot(password=password)
|
||||
if ncli:
|
||||
mw.echoInfo("username: " + info['name'])
|
||||
mw.echoInfo("password: " + password)
|
||||
else:
|
||||
print(username)
|
||||
|
||||
|
||||
def show_panel_pwd():
|
||||
# 面板密码展示
|
||||
info = thisdb.getUserByRoot()
|
||||
defailt_pwd_file = mw.getPanelDir()+'/data/default.pl'
|
||||
pwd = ''
|
||||
if os.path.exists(defailt_pwd_file):
|
||||
pwd = mw.readFile(defailt_pwd_file).strip()
|
||||
|
||||
if mw.md5(pwd) == info['password']:
|
||||
mw.echoInfo('password: ' + pwd)
|
||||
return
|
||||
print("*-password has been changed!")
|
||||
|
||||
def show_panel_adminpath():
|
||||
admin_path = thisdb.getOption('admin_path')
|
||||
print('/'+admin_path)
|
||||
|
||||
|
||||
def set_panel_username(username=None):
|
||||
# 随机面板用户名
|
||||
if username:
|
||||
if len(username) < 5:
|
||||
mw.echoInfo("错误,用户名长度不能少于5位")
|
||||
return
|
||||
if username in ['admin', 'root']:
|
||||
mw.echoInfo("错误,不能使用过于简单的用户名")
|
||||
return
|
||||
|
||||
thisdb.setUserByRoot(name=username)
|
||||
mw.echoInfo("username: %s" % username)
|
||||
return
|
||||
|
||||
info = thisdb.getUserByRoot()
|
||||
if info['name'] == 'admin':
|
||||
username = mw.getRandomString(8).lower()
|
||||
thisdb.setUserByRoot(name=username)
|
||||
mw.echoInfo('username: ' + info['name'])
|
||||
|
||||
|
||||
def getServerIp():
|
||||
version = sys.argv[2]
|
||||
# ip = mw.execShell(
|
||||
# "curl --insecure -{} -sS --connect-timeout 5 -m 60 https://v6r.ipip.net/?format=text".format(version))
|
||||
ip = mw.execShell(
|
||||
"curl --insecure -{} -sS --connect-timeout 5 -m 60 https://ip.cachecha.com/?format=text".format(version))
|
||||
# print(ip[0])
|
||||
return ip[0]
|
||||
|
||||
|
||||
def getPanelSslType():
|
||||
scheme = 'http'
|
||||
panel_ssl = thisdb.getOptionByJson('panel_ssl', default={'open':False})
|
||||
if panel_ssl['open']:
|
||||
scheme = 'https'
|
||||
return scheme
|
||||
|
||||
def getPanelBindDomain():
|
||||
return thisdb.getOption('panel_domain', default='')
|
||||
|
||||
|
||||
def main():
|
||||
if len(sys.argv) == 1:
|
||||
print('ERROR: Parameter error!')
|
||||
exit(-2)
|
||||
method = sys.argv[1]
|
||||
if method == 'panel':
|
||||
set_panel_pwd(sys.argv[2])
|
||||
elif method == 'username':
|
||||
if len(sys.argv) > 2:
|
||||
set_panel_username(sys.argv[2])
|
||||
else:
|
||||
set_panel_username()
|
||||
elif method == 'password':
|
||||
show_panel_pwd()
|
||||
elif method == 'test':
|
||||
thisdb.getOption('admin_path')
|
||||
elif method == 'admin_path':
|
||||
show_panel_adminpath()
|
||||
elif method == 'getServerIp':
|
||||
print(getServerIp())
|
||||
elif method == 'panel_ssl_type':
|
||||
print(getPanelSslType())
|
||||
elif method == 'panel_bind_domain':
|
||||
print(getPanelBindDomain())
|
||||
elif method == "cli":
|
||||
clinum = 0
|
||||
try:
|
||||
if len(sys.argv) > 2:
|
||||
clinum = int(sys.argv[2]) if sys.argv[2][:6] else sys.argv[2]
|
||||
except:
|
||||
clinum = sys.argv[2]
|
||||
mwcli(clinum)
|
||||
else:
|
||||
print('ERROR: Parameter error')
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -1,34 +0,0 @@
|
||||
CREATE TABLE IF NOT EXISTS `dnsapi` (
|
||||
`id` INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
`name` TEXT,
|
||||
`type` TEXT,
|
||||
`val` TEXT,
|
||||
`remark` TEXT,
|
||||
`addtime` TEXT
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `email` (
|
||||
`id` INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
`addr` TEXT,
|
||||
`remark` TEXT,
|
||||
`addtime` TEXT
|
||||
);
|
||||
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `domain` (
|
||||
`id` INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
`domain` TEXT,
|
||||
`dnsapi_id` TEXT,
|
||||
`email` TEXT,
|
||||
`remark` TEXT,
|
||||
`effective_date` TEXT,
|
||||
`expiration_date` TEXT,
|
||||
`error` TEXT,
|
||||
`status` INTEGER default '0',
|
||||
`addtime` TEXT
|
||||
);
|
||||
|
||||
-- ALTER TABLE `domain` ADD COLUMN `effective_date` TEXT DEFAULT '';
|
||||
-- ALTER TABLE `domain` ADD COLUMN `expiration_date` TEXT DEFAULT '';
|
||||
-- ALTER TABLE `domain` ADD COLUMN `error` TEXT DEFAULT '';
|
||||
-- ALTER TABLE `domain` ADD COLUMN `status` INTEGER DEFAULT '0';
|
||||
@@ -1,128 +0,0 @@
|
||||
# coding:utf-8
|
||||
|
||||
import sys
|
||||
import io
|
||||
import os
|
||||
import time
|
||||
import re
|
||||
import requests
|
||||
import base64
|
||||
|
||||
|
||||
# 8001 / 7788
|
||||
goedge_addr = 'http://127.0.0.2:8009'
|
||||
access_keyid = "xxx"
|
||||
access_key = "xxx"
|
||||
|
||||
# 指定用户
|
||||
userId = 1
|
||||
|
||||
sys.path.append(os.getcwd() + "/class/core")
|
||||
import mw
|
||||
|
||||
domain = sys.argv[1]
|
||||
ssl_path = sys.argv[2]
|
||||
|
||||
|
||||
def getToken():
|
||||
api_url = goedge_addr+'/APIAccessTokenService/getAPIAccessToken'
|
||||
post_data = {
|
||||
"type": "admin",
|
||||
"accessKeyId": access_keyid,
|
||||
"accessKey": access_key
|
||||
}
|
||||
data = requests.post(api_url,json=post_data)
|
||||
data_obj = data.json()
|
||||
|
||||
return data_obj['data']['token']
|
||||
|
||||
token = getToken()
|
||||
|
||||
def commonReq(url, data):
|
||||
headers = {
|
||||
'X-Edge-Access-Token': token
|
||||
}
|
||||
api_url = goedge_addr+url
|
||||
resp_data = requests.post(api_url,json=data, headers=headers)
|
||||
return resp_data.json()
|
||||
|
||||
def listSSLCerts(domain):
|
||||
request_data = {
|
||||
"userId":userId,
|
||||
"isCA":False,
|
||||
"keyword": "ACME泛域名自动上传",
|
||||
"domains":[domain,"*."+domain],
|
||||
"size":1
|
||||
}
|
||||
response_data = commonReq('/SSLCertService/listSSLCerts', request_data)
|
||||
|
||||
data = response_data['data']['sslCertsJSON']
|
||||
data = mw.base64StrDecode(data)
|
||||
data = mw.getObjectByJson(data)
|
||||
# print(data)
|
||||
return data
|
||||
|
||||
|
||||
|
||||
# createSSLCert(domain)
|
||||
def createSSLCert(domain, did=0):
|
||||
|
||||
ssl_cer_file = ssl_path + '/'+domain+'.cer'
|
||||
|
||||
if not os.path.exists(ssl_cer_file):
|
||||
print("没有有效证书!")
|
||||
return ''
|
||||
# print(ssl_cer_file)
|
||||
ssl_info = mw.getCertName(ssl_cer_file)
|
||||
cer_data = mw.readFile(ssl_cer_file)
|
||||
cer_data = mw.base64StrEncode(cer_data)
|
||||
# print('cer',cer_data)
|
||||
|
||||
ssl_key_file = ssl_path + '/'+domain+'.key'
|
||||
key_data = mw.readFile(ssl_key_file)
|
||||
key_data = mw.base64StrEncode(key_data)
|
||||
# print('ssl_info',ssl_info)
|
||||
|
||||
timeBeginAt = int(time.mktime(time.strptime(ssl_info['notBefore'], "%Y-%m-%d")))
|
||||
timeEndAt = int(time.mktime(time.strptime(ssl_info['notAfter'], "%Y-%m-%d")))
|
||||
|
||||
request_data = {
|
||||
"isOn":True,
|
||||
"userId":userId,
|
||||
"name": "ACME泛域名自动上传",
|
||||
"isCA":False,
|
||||
"description":domain,
|
||||
"serverName":domain,
|
||||
"certData":cer_data,
|
||||
"keyData":key_data,
|
||||
"timeBeginAt":timeBeginAt,
|
||||
"timeEndAt": timeEndAt,
|
||||
"dnsNames":[domain,"*."+domain],
|
||||
"commonNames":[ssl_info['issuer']]
|
||||
}
|
||||
|
||||
if did>0:
|
||||
request_data['sslCertId'] = did
|
||||
# print(request_data)
|
||||
response_data = commonReq('/SSLCertService/updateSSLCert', request_data)
|
||||
print('更新成功',domain,response_data)
|
||||
return response_data
|
||||
else:
|
||||
# print(request_data)
|
||||
response_data = commonReq('/SSLCertService/createSSLCert', request_data)
|
||||
print('创建成功',domain,response_data)
|
||||
return response_data
|
||||
return response_data
|
||||
|
||||
def autoSyncDomain(domain):
|
||||
data = listSSLCerts(domain)
|
||||
if len(data) > 0 :
|
||||
did = data[0]['id']
|
||||
createSSLCert(domain,did)
|
||||
else:
|
||||
createSSLCert(domain)
|
||||
print(data)
|
||||
|
||||
|
||||
autoSyncDomain(domain)
|
||||
print(domain,ssl_path)
|
||||
@@ -1,12 +0,0 @@
|
||||
# coding:utf-8
|
||||
|
||||
import sys
|
||||
import io
|
||||
import os
|
||||
import time
|
||||
import re
|
||||
|
||||
domain = sys.argv[1]
|
||||
path = sys.argv[2]
|
||||
|
||||
print(domain,path)
|
||||
|
Before Width: | Height: | Size: 4.0 KiB |
@@ -1,34 +0,0 @@
|
||||
<style>
|
||||
.overflow_hide {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
vertical-align: middle;
|
||||
}
|
||||
</style>
|
||||
|
||||
<div class="bt-form">
|
||||
<div class='plugin_version'></div>
|
||||
<div class="bt-w-main">
|
||||
<div class="bt-w-menu">
|
||||
<p class="bgw" onclick="pluginService('acme_pandominassl_apply');">服务</p>
|
||||
<p onclick="dnsapiList();">DNSAPI *</p>
|
||||
<p onclick="emailList();">邮件地址 </p>
|
||||
<p onclick="domainList()">域名SSL *</p>
|
||||
<p onclick="pluginConfigTpl('acme_pandominassl_apply',$('.plugin_version').attr('version'));">HOOK</p>
|
||||
<p onclick="pluginLogs('acme_pandominassl_apply','','run_log');">日志</p>
|
||||
<p onclick="apaReadme();">相关说明</p>
|
||||
|
||||
</div>
|
||||
<div class="bt-w-con pd15">
|
||||
<div class="soft-man-con"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script type="text/javascript">
|
||||
resetPluginWinWidth(1000);
|
||||
resetPluginWinHeight(550);
|
||||
$.getScript( "/plugins/file?name=acme_pandominassl_apply&f=js/common.js", function(){
|
||||
pluginService('acme_pandominassl_apply', $('.plugin_version').attr('version'));
|
||||
});
|
||||
</script>
|
||||
@@ -1,860 +0,0 @@
|
||||
# coding:utf-8
|
||||
|
||||
import sys
|
||||
import io
|
||||
import os
|
||||
import time
|
||||
import re
|
||||
import requests
|
||||
import json
|
||||
|
||||
web_dir = os.getcwd() + "/web"
|
||||
if os.path.exists(web_dir):
|
||||
sys.path.append(web_dir)
|
||||
os.chdir(web_dir)
|
||||
|
||||
import core.mw as mw
|
||||
|
||||
app_debug = False
|
||||
if mw.isAppleSystem():
|
||||
app_debug = True
|
||||
|
||||
|
||||
def getPluginName():
|
||||
return 'acme_pandominassl_apply'
|
||||
|
||||
|
||||
def getPluginDir():
|
||||
return mw.getPluginDir() + '/' + getPluginName()
|
||||
|
||||
|
||||
def getServerDir():
|
||||
return mw.getServerDir() + '/' + getPluginName()
|
||||
|
||||
|
||||
def getInitDFile():
|
||||
current_os = mw.getOs()
|
||||
if current_os == 'darwin':
|
||||
return '/tmp/' + getPluginName()
|
||||
|
||||
if current_os.startswith('freebsd'):
|
||||
return '/etc/rc.d/' + getPluginName()
|
||||
|
||||
return '/etc/init.d/' + getPluginName()
|
||||
|
||||
|
||||
def getConf():
|
||||
path = getServerDir() + "/hook.py"
|
||||
return path
|
||||
|
||||
|
||||
def getInitDTpl():
|
||||
path = getPluginDir() + "/init.d/" + getPluginName() + ".tpl"
|
||||
return path
|
||||
|
||||
def runLog():
|
||||
return getServerDir() + '/hook.log'
|
||||
|
||||
def getArgs():
|
||||
args = sys.argv[3:]
|
||||
# print(args)
|
||||
tmp = {}
|
||||
args_len = len(args)
|
||||
|
||||
if args_len == 1:
|
||||
t = args[0].strip('{').strip('}')
|
||||
if t.strip() == '':
|
||||
tmp = []
|
||||
else:
|
||||
t = t.split(':')
|
||||
tmp[t[0]] = t[1]
|
||||
tmp[t[0]] = t[1]
|
||||
elif args_len > 1:
|
||||
for i in range(len(args)):
|
||||
t = args[i].split(':')
|
||||
tmp[t[0]] = t[1]
|
||||
return tmp
|
||||
|
||||
def checkArgs(data, ck=[]):
|
||||
for i in range(len(ck)):
|
||||
if not ck[i] in data:
|
||||
return (False, mw.returnJson(False, '参数:(' + ck[i] + ')没有!'))
|
||||
return (True, mw.returnJson(True, 'ok'))
|
||||
|
||||
|
||||
def getHomeDir():
|
||||
if mw.isAppleSystem():
|
||||
user = mw.execShell(
|
||||
"who | sed -n '2, 1p' |awk '{print $1}'")[0].strip()
|
||||
return '/Users/' + user
|
||||
else:
|
||||
return '/root'
|
||||
|
||||
|
||||
def getRunUser():
|
||||
if mw.isAppleSystem():
|
||||
user = mw.execShell(
|
||||
"who | sed -n '2, 1p' |awk '{print $1}'")[0].strip()
|
||||
return user
|
||||
else:
|
||||
return 'root'
|
||||
|
||||
def configTpl():
|
||||
path = getPluginDir() + '/hooks'
|
||||
pathFile = os.listdir(path)
|
||||
tmp = []
|
||||
for one in pathFile:
|
||||
file = path + '/' + one
|
||||
tmp.append(file)
|
||||
return mw.getJson(tmp)
|
||||
|
||||
|
||||
def readConfigTpl():
|
||||
args = getArgs()
|
||||
data = checkArgs(args, ['file'])
|
||||
if not data[0]:
|
||||
return data[1]
|
||||
|
||||
content = mw.readFile(args['file'])
|
||||
content = contentReplace(content)
|
||||
return mw.returnJson(True, 'ok', content)
|
||||
|
||||
def getPidFile():
|
||||
file = getConf()
|
||||
content = mw.readFile(file)
|
||||
rep = r'pidfile\s*(.*)'
|
||||
tmp = re.search(rep, content)
|
||||
return tmp.groups()[0].strip()
|
||||
|
||||
def status():
|
||||
return 'start'
|
||||
|
||||
def contentReplace(content):
|
||||
service_path = mw.getServerDir()
|
||||
content = content.replace('{$ROOT_PATH}', mw.getFatherDir())
|
||||
content = content.replace('{$SERVER_PATH}', service_path)
|
||||
content = content.replace('{$SERVER_APP}', service_path + '/redis')
|
||||
content = content.replace('{$REDIS_PASS}', mw.getRandomString(10))
|
||||
return content
|
||||
|
||||
|
||||
def pSqliteDb(dbname='dnsapi'):
|
||||
file = getServerDir() + '/acme.db'
|
||||
name = 'acme'
|
||||
|
||||
import_sql = mw.readFile(getPluginDir() + '/conf/acme.sql')
|
||||
md5_sql = mw.md5(import_sql)
|
||||
|
||||
import_sign = False
|
||||
save_md5_file = getServerDir() + '/acme.md5'
|
||||
if os.path.exists(save_md5_file):
|
||||
save_md5_sql = mw.readFile(save_md5_file)
|
||||
if save_md5_sql != md5_sql:
|
||||
import_sign = True
|
||||
mw.writeFile(save_md5_file, md5_sql)
|
||||
else:
|
||||
mw.writeFile(save_md5_file, md5_sql)
|
||||
|
||||
if not os.path.exists(file) or import_sql:
|
||||
conn = mw.M(dbname).dbPos(getServerDir(), name)
|
||||
csql_list = import_sql.split(';')
|
||||
for index in range(len(csql_list)):
|
||||
conn.execute(csql_list[index], ())
|
||||
|
||||
conn = mw.M(dbname).dbPos(getServerDir(), name)
|
||||
return conn
|
||||
|
||||
def initDreplace():
|
||||
|
||||
file_tpl = getInitDTpl()
|
||||
|
||||
initD_path = getServerDir() + '/init.d'
|
||||
if not os.path.exists(initD_path):
|
||||
os.mkdir(initD_path)
|
||||
file_bin = initD_path + '/' + getPluginName()
|
||||
|
||||
run_log_file = runLog()
|
||||
if not os.path.exists(run_log_file):
|
||||
mw.writeFile(run_log_file,'')
|
||||
|
||||
hook_file = getConf()
|
||||
if not os.path.exists(hook_file):
|
||||
mw.writeFile(hook_file,'')
|
||||
|
||||
return file_bin
|
||||
|
||||
|
||||
def apaOp(method):
|
||||
file = initDreplace()
|
||||
|
||||
current_os = mw.getOs()
|
||||
if current_os == "darwin":
|
||||
data = mw.execShell(file + ' ' + method)
|
||||
if data[1] == '':
|
||||
return 'ok'
|
||||
return data[1]
|
||||
|
||||
if current_os.startswith("freebsd"):
|
||||
data = mw.execShell('service ' + getPluginName() + ' ' + method)
|
||||
if data[1] == '':
|
||||
return 'ok'
|
||||
return data[1]
|
||||
|
||||
data = mw.execShell('systemctl ' + method + ' ' + getPluginName())
|
||||
if data[1] == '':
|
||||
return 'ok'
|
||||
return data[1]
|
||||
|
||||
|
||||
def start():
|
||||
import tool_cron
|
||||
tool_cron.createBgTask()
|
||||
|
||||
return apaOp('start')
|
||||
|
||||
|
||||
def stop():
|
||||
import tool_cron
|
||||
tool_cron.removeBgTask()
|
||||
return apaOp('stop')
|
||||
|
||||
|
||||
def restart():
|
||||
status = apaOp('restart')
|
||||
return status
|
||||
|
||||
|
||||
def reload():
|
||||
return apaOp('reload')
|
||||
|
||||
|
||||
def dnsapiAdd():
|
||||
conn = pSqliteDb('dnsapi')
|
||||
args = getArgs()
|
||||
data = checkArgs(args,['id','name', 'type', 'remark'])
|
||||
if not data[0]:
|
||||
return data[1]
|
||||
|
||||
name = args['name'].strip()
|
||||
remark = args['remark'].strip()
|
||||
stype = args['type'].strip()
|
||||
val = args['val'].strip()
|
||||
sid = args['id'].strip()
|
||||
|
||||
if name == '':
|
||||
return mw.returnJson(False, '名称不能为空!')
|
||||
|
||||
if sid != '0' : #修改操作
|
||||
conn.where("id=?", (sid,)).update({
|
||||
'name':name,
|
||||
'type':stype,
|
||||
'val':val,
|
||||
'remark':remark,
|
||||
})
|
||||
return mw.returnJson(True, '修改成功!')
|
||||
|
||||
if conn.where("name=?", (name,)).count():
|
||||
return mw.returnJson(False, name+'已存在!')
|
||||
|
||||
addTime = time.strftime('%Y-%m-%d %X', time.localtime())
|
||||
err = conn.add('name,type,val,remark,addtime', (name, stype, val,remark, addTime))
|
||||
# print(err)
|
||||
return mw.returnJson(True, '添加成功!')
|
||||
|
||||
def dnsapiDel():
|
||||
args = getArgs()
|
||||
data = checkArgs(args, ['id', 'name'])
|
||||
if not data[0]:
|
||||
return data[1]
|
||||
|
||||
conn = pSqliteDb('dnsapi')
|
||||
try:
|
||||
sid = args['id']
|
||||
name = args['name']
|
||||
conn.where("id=?", (sid,)).delete()
|
||||
return mw.returnJson(True, '删除成功!')
|
||||
except Exception as ex:
|
||||
return mw.returnJson(False, '删除失败!' + str(ex))
|
||||
|
||||
def dnsapiList():
|
||||
args = getArgs()
|
||||
page = 1
|
||||
page_size = 10
|
||||
search = ''
|
||||
data = {}
|
||||
if 'page' in args:
|
||||
page = int(args['page'])
|
||||
|
||||
if 'page_size' in args:
|
||||
page_size = int(args['page_size'])
|
||||
|
||||
if 'search' in args:
|
||||
search = args['search']
|
||||
|
||||
conn = pSqliteDb('dnsapi')
|
||||
limit = str((page - 1) * page_size) + ',' + str(page_size)
|
||||
condition = ''
|
||||
if not search == '':
|
||||
condition = "name like '%" + search + "%'"
|
||||
field = 'id,name,type,val,remark,addtime'
|
||||
clist = conn.where(condition, ()).field(
|
||||
field).limit(limit).order('id desc').select()
|
||||
|
||||
count = conn.where(condition, ()).count()
|
||||
_page = {}
|
||||
_page['count'] = count
|
||||
_page['p'] = page
|
||||
_page['row'] = page_size
|
||||
_page['tojs'] = 'dbList'
|
||||
data['page'] = mw.getPage(_page)
|
||||
data['data'] = clist
|
||||
|
||||
return mw.getJson(data)
|
||||
|
||||
def dnsapiListAll():
|
||||
conn = pSqliteDb('dnsapi')
|
||||
field = 'id,name,type,val,remark,addtime'
|
||||
data = conn.field(field).limit('1000').select()
|
||||
return mw.getJson(data)
|
||||
|
||||
def emailList():
|
||||
args = getArgs()
|
||||
page = 1
|
||||
page_size = 10
|
||||
search = ''
|
||||
data = {}
|
||||
if 'page' in args:
|
||||
page = int(args['page'])
|
||||
|
||||
if 'page_size' in args:
|
||||
page_size = int(args['page_size'])
|
||||
|
||||
if 'search' in args:
|
||||
search = args['search']
|
||||
|
||||
conn = pSqliteDb('email')
|
||||
limit = str((page - 1) * page_size) + ',' + str(page_size)
|
||||
condition = ''
|
||||
if not search == '':
|
||||
condition = "addr like '%" + search + "%'"
|
||||
field = 'id,addr,remark,addtime'
|
||||
clist = conn.where(condition, ()).field(
|
||||
field).limit(limit).order('id desc').select()
|
||||
|
||||
count = conn.where(condition, ()).count()
|
||||
_page = {}
|
||||
_page['count'] = count
|
||||
_page['p'] = page
|
||||
_page['row'] = page_size
|
||||
_page['tojs'] = 'dbList'
|
||||
data['page'] = mw.getPage(_page)
|
||||
data['data'] = clist
|
||||
|
||||
return mw.getJson(data)
|
||||
|
||||
def emailAdd():
|
||||
args = getArgs()
|
||||
data = checkArgs(args,['addr', 'remark'])
|
||||
if not data[0]:
|
||||
return data[1]
|
||||
|
||||
addr = args['addr'].strip()
|
||||
remark = args['remark'].strip()
|
||||
|
||||
if addr == '':
|
||||
return mw.returnJson(False, '邮件地址不能为空!')
|
||||
|
||||
conn = pSqliteDb('email')
|
||||
|
||||
addTime = time.strftime('%Y-%m-%d %X', time.localtime())
|
||||
conn.add('addr,remark,addtime', (addr, remark, addTime))
|
||||
return mw.returnJson(True, '添加成功!')
|
||||
|
||||
def emailDel():
|
||||
args = getArgs()
|
||||
data = checkArgs(args, ['id', 'name'])
|
||||
if not data[0]:
|
||||
return data[1]
|
||||
|
||||
conn = pSqliteDb('email')
|
||||
try:
|
||||
sid = args['id']
|
||||
name = args['name']
|
||||
conn.where("id=?", (sid,)).delete()
|
||||
return mw.returnJson(True, '删除成功!')
|
||||
except Exception as ex:
|
||||
return mw.returnJson(False, '删除失败!' + str(ex))
|
||||
|
||||
def domainAdd():
|
||||
args = getArgs()
|
||||
data = checkArgs(args,['id','domain', 'dnsapi_id','email','remark'])
|
||||
if not data[0]:
|
||||
return data[1]
|
||||
|
||||
sid = args['id'].strip()
|
||||
domain = args['domain'].strip()
|
||||
remark = args['remark'].strip()
|
||||
email = args['email'].strip()
|
||||
dnsapi_id = args['dnsapi_id'].strip()
|
||||
|
||||
if domain == '':
|
||||
return mw.returnJson(False, '域名不能为空!')
|
||||
if email == '':
|
||||
return mw.returnJson(False, '邮件不能为空!')
|
||||
|
||||
conn = pSqliteDb('domain')
|
||||
|
||||
if sid != '0' : #修改操作
|
||||
conn.where("id=?", (sid,)).update({
|
||||
'domain':domain,
|
||||
'dnsapi_id':dnsapi_id,
|
||||
'email':email,
|
||||
'remark':remark,
|
||||
})
|
||||
return mw.returnJson(True, '修改成功!')
|
||||
|
||||
if conn.where("domain=?", (domain,)).count():
|
||||
return mw.returnJson(False, domain+'已存在!')
|
||||
|
||||
|
||||
|
||||
addTime = time.strftime('%Y-%m-%d %X', time.localtime())
|
||||
conn.add('domain,dnsapi_id,email,remark,addtime', (domain, dnsapi_id,email,remark, addTime))
|
||||
return mw.returnJson(True, '添加成功!')
|
||||
|
||||
def domainDel():
|
||||
args = getArgs()
|
||||
data = checkArgs(args, ['id', 'name'])
|
||||
if not data[0]:
|
||||
return data[1]
|
||||
|
||||
conn = pSqliteDb('domain')
|
||||
try:
|
||||
sid = args['id']
|
||||
name = args['name']
|
||||
conn.where("id=?", (sid,)).delete()
|
||||
return mw.returnJson(True, '删除成功!')
|
||||
except Exception as ex:
|
||||
return mw.returnJson(False, '删除失败!' + str(ex))
|
||||
|
||||
def domainStatusToggle():
|
||||
args = getArgs()
|
||||
data = checkArgs(args, ['id'])
|
||||
if not data[0]:
|
||||
return data[1]
|
||||
|
||||
conn = pSqliteDb('domain')
|
||||
|
||||
field = 'id,status'
|
||||
fdata = conn.field(field).where('id=?',(args['id'],)).find()
|
||||
|
||||
fstatus = fdata['status']
|
||||
if fstatus == 0:
|
||||
fstatus = 1
|
||||
else:
|
||||
fstatus = 0
|
||||
# print(fdata['status'],fstatus)
|
||||
conn.where("id=?", (args['id'],)).update({
|
||||
'status':fstatus,
|
||||
})
|
||||
return mw.returnJson(True, '切换成功!')
|
||||
|
||||
def domainList():
|
||||
args = getArgs()
|
||||
page = 1
|
||||
page_size = 10
|
||||
search = ''
|
||||
data = {}
|
||||
if 'page' in args:
|
||||
page = int(args['page'])
|
||||
|
||||
if 'page_size' in args:
|
||||
page_size = int(args['page_size'])
|
||||
|
||||
if 'search' in args:
|
||||
search = args['search']
|
||||
|
||||
conn = pSqliteDb('domain')
|
||||
conn_dnsapi = pSqliteDb('dnsapi')
|
||||
limit = str((page - 1) * page_size) + ',' + str(page_size)
|
||||
condition = ''
|
||||
if not search == '':
|
||||
condition = "domain like '%" + search + "%'"
|
||||
field = 'id,domain,dnsapi_id,email,status,effective_date,expiration_date,remark,addtime'
|
||||
clist = conn.where(condition, ()).field(
|
||||
field).limit(limit).order('id desc').select()
|
||||
|
||||
for i in range(len(clist)):
|
||||
tdata = conn_dnsapi.field('id,name').where('id=?',(clist[i]['dnsapi_id'],)).select()
|
||||
if len(tdata) > 0:
|
||||
# print(tdata[0]['name'])
|
||||
clist[i]['dnsapi_id_alias'] = clist[i]['dnsapi_id']+'('+ tdata[0]['name'] +')'
|
||||
else:
|
||||
clist[i]['dnsapi_id_alias'] = clist[i]['dnsapi_id']+' (无)'
|
||||
|
||||
count = conn.where(condition, ()).count()
|
||||
_page = {}
|
||||
_page['count'] = count
|
||||
_page['p'] = page
|
||||
_page['row'] = page_size
|
||||
_page['tojs'] = 'dbList'
|
||||
data['page'] = mw.getPage(_page)
|
||||
data['data'] = clist
|
||||
|
||||
return mw.getJson(data)
|
||||
|
||||
def getDnsapiData(dnsapi_id):
|
||||
if dnsapi_id == '0':
|
||||
return {}
|
||||
conn_dnsapi = pSqliteDb('dnsapi')
|
||||
tdata = conn_dnsapi.field('id,name,type,val').where('id=?',(dnsapi_id,)).select()
|
||||
|
||||
if len(tdata)>0:
|
||||
return tdata[0]
|
||||
return {}
|
||||
|
||||
def getDnsapiExportVar(val):
|
||||
cmd_list = val.split('~')
|
||||
def_var = ''
|
||||
for x in range(len(cmd_list)):
|
||||
v = cmd_list[x]
|
||||
vlist = v.split('|')
|
||||
def_var += 'export '+vlist[0]+'="'+vlist[1]+'"\n'
|
||||
return def_var
|
||||
|
||||
def getDnsapiKv(val):
|
||||
cmd_list = val.split('~')
|
||||
def_var = {}
|
||||
for x in range(len(cmd_list)):
|
||||
v = cmd_list[x]
|
||||
vlist = v.split('|')
|
||||
kk = vlist[0]
|
||||
vv = vlist[1]
|
||||
def_var[kk] = vv
|
||||
return def_var
|
||||
|
||||
def domainApplyPathJudge(domain):
|
||||
acme_dir = "/root/.acme.sh"
|
||||
if mw.isAppleSystem():
|
||||
user = getRunUser()
|
||||
acme_dir = "/Users/"+user+"/.acme.sh"
|
||||
|
||||
abs_domain_path = acme_dir+'/'+domain+'_ecc'
|
||||
# print(abs_domain_path)
|
||||
if os.path.exists(abs_domain_path):
|
||||
return True, abs_domain_path
|
||||
|
||||
abs_domain_path = acme_dir+'/'+domain
|
||||
# print(abs_domain_path)
|
||||
if os.path.exists(abs_domain_path):
|
||||
return True, abs_domain_path
|
||||
|
||||
return False,''
|
||||
|
||||
|
||||
def hookWriteLog(line):
|
||||
hook_file = runLog()
|
||||
mdate = time.strftime('%Y-%m-%d %X', time.localtime())
|
||||
log = "["+mdate+"]:"+line
|
||||
print(log)
|
||||
return mw.writeFile(hook_file,log+"\n",'a+')
|
||||
|
||||
def runHookPy(domain,path):
|
||||
# print(domain,path)
|
||||
run_log = runLog()
|
||||
hook_file = getConf()
|
||||
cmd = 'cd '+mw.getPanelDir()
|
||||
cmd += ' && python3 '+hook_file + ' ' + domain + ' ' + path
|
||||
cmd += ' >> '+ run_log
|
||||
print(cmd)
|
||||
return mw.execShell(cmd)
|
||||
|
||||
def autoUpdateSslData(domain, path):
|
||||
ssl_cer_file = path + '/'+domain+'.cer'
|
||||
ssl_info = mw.getCertName(ssl_cer_file)
|
||||
|
||||
time_begin = int(time.mktime(time.strptime(ssl_info['notBefore'], "%Y-%m-%d")))
|
||||
time_end = int(time.mktime(time.strptime(ssl_info['notAfter'], "%Y-%m-%d")))
|
||||
# print(ssl_info)
|
||||
# print(time_begin,time_end)
|
||||
conn = pSqliteDb('domain')
|
||||
conn.where('domain=?', (domain,)).update({
|
||||
'effective_date':str(time_begin),
|
||||
'expiration_date':str(time_end),
|
||||
})
|
||||
|
||||
return True
|
||||
|
||||
def runHookDstDomain(row):
|
||||
domain = row['domain']
|
||||
email = str(row['email'])
|
||||
|
||||
if row['effective_date'] != '':
|
||||
effective_date = int(row['effective_date'])
|
||||
now_int = int(time.time())
|
||||
day = (now_int - effective_date)/86400
|
||||
if int(day) < 8:
|
||||
common_log = '【'+domain+'】未过期'
|
||||
hookWriteLog(common_log)
|
||||
return
|
||||
|
||||
hookWriteLog('开始申请【'+domain+'】SSL证书')
|
||||
cmd = "#!/bin/bash\n"
|
||||
|
||||
if mw.isAppleSystem():
|
||||
user = getRunUser()
|
||||
cmd += "source /Users/"+user+"/.zshrc\n"
|
||||
|
||||
cmd_data = getDnsapiData(row['dnsapi_id'])
|
||||
# print(cmd_data)
|
||||
export_val = getDnsapiExportVar(cmd_data['val'])
|
||||
cmd += export_val
|
||||
|
||||
# acme.sh --register-account -m my@example.com
|
||||
cmd_register = 'acme.sh --register-account -m '+ email + '\n'
|
||||
cmd += cmd_register
|
||||
|
||||
|
||||
# acme.sh --issue -d "example.com" -d "*.example.com" --dns dns_cf
|
||||
cmd_apply = 'acme.sh --issue --dns '+str(cmd_data['type'])+' -d '+domain+' -d "*.'+domain+'"'
|
||||
if row['effective_date'] != '':
|
||||
effective_date = int(row['effective_date'])
|
||||
now_int = int(time.time())
|
||||
day = (now_int - effective_date)/86400
|
||||
if int(day) > 7:
|
||||
cmd_apply = 'acme.sh --issue --dns '+str(cmd_data['type'])+' -d '+domain+' -d "*.'+domain+'" --force'
|
||||
cmd += cmd_apply
|
||||
|
||||
run_log = runLog()
|
||||
cmd += ' >> '+ run_log
|
||||
print(cmd)
|
||||
os.system(cmd)
|
||||
hookWriteLog('结束申请【'+domain+'】SSL证书')
|
||||
isok, path = domainApplyPathJudge(domain)
|
||||
print(isok,path)
|
||||
if isok:
|
||||
autoUpdateSslData(domain, path)
|
||||
hookWriteLog('开始执行【'+domain+'】Hook脚本')
|
||||
runHookPy(domain,path)
|
||||
hookWriteLog('结束执行【'+domain+'】Hook脚本')
|
||||
|
||||
def getHookIdCmd():
|
||||
args = getArgs()
|
||||
data = checkArgs(args, ['id'])
|
||||
if not data[0]:
|
||||
return data[1]
|
||||
|
||||
cmd = "cd "+mw.getPanelDir()+" "
|
||||
cmd += '&& python3 plugins/acme_pandominassl_apply/index.py run_hook_id 1.0 {"id":"'+args['id']+'"}'
|
||||
return mw.returnJson(True, 'ok',cmd)
|
||||
|
||||
def runHookId():
|
||||
args = getArgs()
|
||||
data = checkArgs(args, ['id'])
|
||||
if not data[0]:
|
||||
return data[1]
|
||||
|
||||
conn = pSqliteDb('domain')
|
||||
field = 'id,domain,dnsapi_id,email,effective_date,expiration_date,remark'
|
||||
row = conn.field(field).where('id=?',(args['id'],)).find()
|
||||
runHookDstDomain(row)
|
||||
return 'run hook '+args['id']+' end'
|
||||
|
||||
def runHookCmd():
|
||||
cmd = "cd "+mw.getPanelDir()+" "
|
||||
cmd += '&& python3 plugins/acme_pandominassl_apply/index.py run_hook'
|
||||
return mw.returnJson(True, 'ok',cmd)
|
||||
|
||||
def runHook():
|
||||
conn = pSqliteDb('domain')
|
||||
|
||||
field = 'id,domain,dnsapi_id,email,effective_date,expiration_date,remark'
|
||||
clist = conn.field(field).where('status=?',(1,)).limit('1000').order('id desc').select()
|
||||
|
||||
for idx in range(len(clist)):
|
||||
row = clist[idx]
|
||||
# print(row)
|
||||
runHookDstDomain(row)
|
||||
time.sleep(1)
|
||||
return 'run hook end'
|
||||
|
||||
def autoSyncDomain(domain, dnsapi_id, email):
|
||||
conn = pSqliteDb('domain')
|
||||
|
||||
field = 'id,domain,dnsapi_id,email,effective_date,expiration_date,remark'
|
||||
fdata = conn.field(field).where('domain=?',(domain,)).select()
|
||||
# print(fdata)
|
||||
mdate = time.strftime('%Y-%m-%d %X', time.localtime())
|
||||
if len(fdata) == 0:
|
||||
conn.add('domain,dnsapi_id,email,remark,addtime', (domain, dnsapi_id, email,"备注", mdate))
|
||||
print(domain+" 同步成功!")
|
||||
return True
|
||||
|
||||
def runSyncCfDataRow(row, page = 1):
|
||||
# print(row, page)
|
||||
|
||||
cf_key = row['kv']['CF_Key']
|
||||
cf_mail = row['kv']['CF_Email']
|
||||
header = {
|
||||
'X-Auth-Email': cf_mail,
|
||||
'X-Auth-Key': cf_key,
|
||||
'Content-Type': 'application/json'
|
||||
}
|
||||
url = "https://api.cloudflare.com/client/v4/zones?status=active&page="+str(page)+"&per_page=20&order=status&direction=desc&match=all"
|
||||
try:
|
||||
r = requests.get(url, timeout=30, headers=header)
|
||||
r.raise_for_status()
|
||||
r.encoding = r.apparent_encoding
|
||||
# print(r.text)
|
||||
jdata = json.loads(r.text)
|
||||
result = jdata['result']
|
||||
result_info = jdata['result_info']
|
||||
|
||||
# print(len(result))
|
||||
for i in result:
|
||||
autoSyncDomain(i['name'], row['id'], cf_mail)
|
||||
|
||||
if result_info['total_pages'] > page:
|
||||
page += 1
|
||||
runSyncCfDataRow(row, page)
|
||||
# print(result)
|
||||
except Exception as e:
|
||||
print(e)
|
||||
|
||||
# 同步CloudFlare数据
|
||||
def runSyncCfData():
|
||||
print("同步CloudFlare数据开始")
|
||||
conn_dnsapi = pSqliteDb('dnsapi')
|
||||
field = "id,name,type,val"
|
||||
fdata = conn_dnsapi.field(field).where('type=?', ('dns_cf',)).limit('10').select()
|
||||
|
||||
for x in range(len(fdata)):
|
||||
row = fdata[x]
|
||||
dnsapi_kv = getDnsapiKv(row['val'])
|
||||
row['kv'] = dnsapi_kv
|
||||
runSyncCfDataRow(row)
|
||||
|
||||
print("同步CloudFlare数据结束")
|
||||
return ''
|
||||
|
||||
def runSyncCfCmd():
|
||||
cmd = "cd "+mw.getPanelDir()+" "
|
||||
cmd += '&& python3 plugins/acme_pandominassl_apply/index.py run_sync_cf_data'
|
||||
return mw.returnJson(True, 'ok',cmd)
|
||||
|
||||
def runSyncDnspodDataRow(row, page = 1):
|
||||
# print(row, page)
|
||||
|
||||
DPI_Id = row['kv']['DPI_Id']
|
||||
DPI_Key = row['kv']['DPI_Key']
|
||||
data = {
|
||||
'login_token': DPI_Id+','+DPI_Key,
|
||||
'length':5,
|
||||
'format':'json',
|
||||
}
|
||||
url = "https://api.dnspod.com/Domain.List"
|
||||
try:
|
||||
r = requests.post(url, timeout=30, data=data)
|
||||
r.raise_for_status()
|
||||
r.encoding = r.apparent_encoding
|
||||
|
||||
# print(r.text)
|
||||
jdata = json.loads(r.text)
|
||||
info = jdata['info']
|
||||
domains = jdata['domains']
|
||||
|
||||
# print(jdata)
|
||||
for i in domains:
|
||||
autoSyncDomain(i['name'], row['id'], i['owner'])
|
||||
|
||||
page_total = int(info['domain_total']/data['length'])
|
||||
if page_total > page:
|
||||
page += 1
|
||||
runSyncDnspodDataRow(row, page)
|
||||
except Exception as e:
|
||||
print(e)
|
||||
|
||||
# 同步DnsPod数据
|
||||
def runSyncDnsPodData():
|
||||
print("同步DnsPod数据开始")
|
||||
conn_dnsapi = pSqliteDb('dnsapi')
|
||||
field = "id,name,type,val"
|
||||
fdata = conn_dnsapi.field(field).where('type=?', ('dns_dpi',)).limit('10').select()
|
||||
for x in range(len(fdata)):
|
||||
row = fdata[x]
|
||||
dnsapi_kv = getDnsapiKv(row['val'])
|
||||
row['kv'] = dnsapi_kv
|
||||
runSyncDnspodDataRow(row)
|
||||
|
||||
print(fdata)
|
||||
print("同步DnsPod数据结束")
|
||||
return ''
|
||||
|
||||
def runSyncDnsPodCmd():
|
||||
cmd = "cd "+mw.getPanelDir()+" "
|
||||
cmd += '&& python3 plugins/acme_pandominassl_apply/index.py run_sync_dnspod_data'
|
||||
return mw.returnJson(True, 'ok',cmd)
|
||||
|
||||
if __name__ == "__main__":
|
||||
func = sys.argv[1]
|
||||
# print(func)
|
||||
if func == 'status':
|
||||
print(status())
|
||||
elif func == 'start':
|
||||
print(start())
|
||||
elif func == 'stop':
|
||||
print(stop())
|
||||
elif func == 'restart':
|
||||
print(restart())
|
||||
elif func == 'reload':
|
||||
print(reload())
|
||||
elif func == 'run_info':
|
||||
print(runInfo())
|
||||
elif func == 'conf':
|
||||
print(getConf())
|
||||
elif func == 'run_log':
|
||||
print(runLog())
|
||||
elif func == 'config_tpl':
|
||||
print(configTpl())
|
||||
elif func == 'read_config_tpl':
|
||||
print(readConfigTpl())
|
||||
elif func == 'dnsapi_list':
|
||||
print(dnsapiList())
|
||||
elif func == 'dnsapi_list_all':
|
||||
print(dnsapiListAll())
|
||||
elif func == 'dnsapi_add':
|
||||
print(dnsapiAdd())
|
||||
elif func == 'dnsapi_del':
|
||||
print(dnsapiDel())
|
||||
elif func == 'email_list':
|
||||
print(emailList())
|
||||
elif func == 'email_add':
|
||||
print(emailAdd())
|
||||
elif func == 'email_del':
|
||||
print(emailDel())
|
||||
elif func == 'domain_list':
|
||||
print(domainList())
|
||||
elif func == 'domain_add':
|
||||
print(domainAdd())
|
||||
elif func == 'domain_del':
|
||||
print(domainDel())
|
||||
elif func == 'domain_status_toggle':
|
||||
print(domainStatusToggle())
|
||||
elif func == 'run_hook':
|
||||
print(runHook())
|
||||
elif func == 'run_hook_cmd':
|
||||
print(runHookCmd())
|
||||
elif func == 'get_run_hook_id_cmd':
|
||||
print(getHookIdCmd())
|
||||
elif func == 'run_hook_id':
|
||||
print(runHookId())
|
||||
elif func == 'run_sync_cf_data':
|
||||
print(runSyncCfData())
|
||||
elif func == 'run_sync_cf_cmd':
|
||||
print(runSyncCfCmd())
|
||||
elif func == 'run_sync_dnspod_data':
|
||||
print(runSyncDnsPodData())
|
||||
elif func == 'run_sync_dnspod_cmd':
|
||||
print(runSyncDnsPodCmd())
|
||||
else:
|
||||
print('error')
|
||||
@@ -1,17 +0,0 @@
|
||||
{
|
||||
"sort": 7,
|
||||
"ps": "ACME泛域名SSL申请/管理",
|
||||
"name": "acme_pandominassl_apply",
|
||||
"title": "ACME泛域名SSL",
|
||||
"shell": "install.sh",
|
||||
"versions":["1.0"],
|
||||
"tip": "soft",
|
||||
"checks": "server/acme_pandominassl_apply",
|
||||
"path": "server/acme_pandominassl_apply",
|
||||
"display": 1,
|
||||
"author": "ACME",
|
||||
"date": "2024-09-12",
|
||||
"home": "https://github.com/acmesh-official/acme.sh",
|
||||
"type": 0,
|
||||
"pid": "5"
|
||||
}
|
||||
@@ -1,54 +0,0 @@
|
||||
#!/bin/bash
|
||||
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin:/opt/homebrew/bin
|
||||
export PATH
|
||||
|
||||
curPath=`pwd`
|
||||
rootPath=$(dirname "$curPath")
|
||||
rootPath=$(dirname "$rootPath")
|
||||
serverPath=$(dirname "$rootPath")
|
||||
|
||||
VERSION=$2
|
||||
|
||||
# curl https://get.acme.sh | sh
|
||||
# acme.sh --uninstall
|
||||
|
||||
|
||||
# source /Users/xxx/.zshrc
|
||||
|
||||
# https://github.com/acmesh-official/acme.sh/wiki/dnsapi
|
||||
# https://docs.dnspod.com/api-legacy/domains.html#get-the-domain-list
|
||||
|
||||
# cd /www/server/mdserver-web && python3 plugins/acme_pandominassl_apply/index.py run_hook
|
||||
# cd /www/server/mdserver-web && python3 plugins/acme_pandominassl_apply/index.py run_sync_cf_data
|
||||
# cd /www/server/mdserver-web && python3 plugins/acme_pandominassl_apply/index.py run_sync_dnspod_data
|
||||
|
||||
|
||||
if [ -f ${rootPath}/bin/activate ];then
|
||||
source ${rootPath}/bin/activate
|
||||
fi
|
||||
|
||||
# pip install cloudflare
|
||||
Install_App()
|
||||
{
|
||||
echo '正在安装脚本文件...'
|
||||
mkdir -p $serverPath/source
|
||||
mkdir -p $serverPath/acme_pandominassl_apply
|
||||
echo "${VERSION}" > $serverPath/acme_pandominassl_apply/version.pl
|
||||
|
||||
cd ${rootPath} && python3 ${rootPath}/plugins/acme_pandominassl_apply/index.py start
|
||||
echo '安装[ACME泛域名SSL]完成'
|
||||
}
|
||||
|
||||
Uninstall_App()
|
||||
{
|
||||
cd ${rootPath} && python3 ${rootPath}/plugins/acme_pandominassl_apply/index.py stop
|
||||
rm -rf $serverPath/acme_pandominassl_apply
|
||||
echo "卸载[ACME泛域名SSL]成功"
|
||||
}
|
||||
|
||||
action=$1
|
||||
if [ "${1}" == 'install' ];then
|
||||
Install_App
|
||||
else
|
||||
Uninstall_App
|
||||
fi
|
||||
@@ -1,792 +0,0 @@
|
||||
function apaPost(method, args,callback){
|
||||
var loadT = layer.msg('正在获取...', { icon: 16, time: 0, shade: 0.3 });
|
||||
|
||||
var req_data = {};
|
||||
req_data['name'] = 'acme_pandominassl_apply';
|
||||
req_data['func'] = method;
|
||||
req_data['version'] = $('.plugin_version').attr('version');
|
||||
|
||||
if (typeof(args) == 'string'){
|
||||
req_data['args'] = JSON.stringify(toArrayObject(args));
|
||||
} else {
|
||||
req_data['args'] = JSON.stringify(args);
|
||||
}
|
||||
|
||||
$.post('/plugins/run', req_data, function(data) {
|
||||
layer.close(loadT);
|
||||
if (!data.status){
|
||||
//错误展示10S
|
||||
layer.msg(data.msg,{icon:0,time:2000,shade: [10, '#000']});
|
||||
return;
|
||||
}
|
||||
|
||||
if(typeof(callback) == 'function'){
|
||||
callback(data);
|
||||
}
|
||||
},'json');
|
||||
}
|
||||
|
||||
|
||||
function apaPostN(method, args,callback){
|
||||
|
||||
var req_data = {};
|
||||
req_data['name'] = 'acme_pandominassl_apply';
|
||||
req_data['func'] = method;
|
||||
req_data['version'] = $('.plugin_version').attr('version');
|
||||
|
||||
if (typeof(args) == 'string'){
|
||||
req_data['args'] = JSON.stringify(toArrayObject(args));
|
||||
} else {
|
||||
req_data['args'] = JSON.stringify(args);
|
||||
}
|
||||
|
||||
$.post('/plugins/run', req_data, function(data) {
|
||||
if (!data.status){
|
||||
//错误展示10S
|
||||
layer.msg(data.msg,{icon:0,time:2000,shade: [10, '#000']});
|
||||
return;
|
||||
}
|
||||
|
||||
if(typeof(callback) == 'function'){
|
||||
callback(data);
|
||||
}
|
||||
},'json');
|
||||
}
|
||||
|
||||
function apaPostCallbak(method, version, args,callback){
|
||||
var loadT = layer.msg('正在获取...', { icon: 16, time: 0, shade: 0.3 });
|
||||
|
||||
var req_data = {};
|
||||
req_data['name'] = 'acme_pandominassl_apply';
|
||||
req_data['func'] = method;
|
||||
args['version'] = version;
|
||||
|
||||
if (typeof(args) == 'string'){
|
||||
req_data['args'] = JSON.stringify(toArrayObject(args));
|
||||
} else {
|
||||
req_data['args'] = JSON.stringify(args);
|
||||
}
|
||||
|
||||
$.post('/plugins/callback', req_data, function(data) {
|
||||
layer.close(loadT);
|
||||
if (!data.status){
|
||||
layer.msg(data.msg,{icon:0,time:2000,shade: [0.3, '#000']});
|
||||
return;
|
||||
}
|
||||
|
||||
if(typeof(callback) == 'function'){
|
||||
callback(data);
|
||||
}
|
||||
},'json');
|
||||
}
|
||||
|
||||
function apaReadme(){
|
||||
var readme = '<ul class="help-info-text c7">';
|
||||
readme += '<li>ACME泛域名SSL申请/管理/HOOK</li>';
|
||||
readme += '<li>通过DNS验证获取SSL证书!</li>';
|
||||
readme += '<li>HOOK: ssl发生变动时调用!</li>';
|
||||
readme += '<li>暂时仅支持1000个域名管理!</li>';
|
||||
readme += '<li>DNSAPI文档: https://github.com/acmesh-official/acme.sh/wiki/dnsapi</li>';
|
||||
readme += '<li>默认7天强制更新!</li>';
|
||||
|
||||
|
||||
readme += '</ul>';
|
||||
$('.soft-man-con').html(readme);
|
||||
}
|
||||
|
||||
|
||||
function emailDel(id, name){
|
||||
safeMessage('删除['+name+']','您真的要删除['+name+']吗?',function(){
|
||||
var data='id='+id+'&name='+name;
|
||||
apaPost('email_del', data, function(data){
|
||||
var rdata = $.parseJSON(data.data);
|
||||
showMsg(rdata.msg,function(){
|
||||
emailList();
|
||||
},{icon: rdata.status ? 1 : 2}, 600);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function emailAdd(type){
|
||||
layer.open({
|
||||
type: 1,
|
||||
area: '500px',
|
||||
title: '添加邮件地址',
|
||||
closeBtn: 1,
|
||||
shift: 5,
|
||||
shadeClose: true,
|
||||
btn:["提交","关闭"],
|
||||
content: "<form class='bt-form pd20' id='email_add'>\
|
||||
<div class='line'>\
|
||||
<span class='tname'>邮件地址</span>\
|
||||
<div class='info-r'><input name='addr' class='bt-input-text mr5' style='width:100%;' placeholder='邮件地址' type='text'></div>\
|
||||
</div>\
|
||||
<div class='line'>\
|
||||
<span class='tname'>备注</span>\
|
||||
<div class='info-r'><input name='remark' class='bt-input-text mr5' style='width:100%;' placeholder='备注' type='text'></div>\
|
||||
</div>\
|
||||
</form>",
|
||||
success:function(){
|
||||
$("input[name='addr']").keyup(function(){
|
||||
var v = $(this).val();
|
||||
$("input[name='remark']").val(v);
|
||||
});
|
||||
},
|
||||
yes:function(index) {
|
||||
var data = $("#email_add").serialize();
|
||||
data = decodeURIComponent(data);
|
||||
// data = toArrayObject(data);
|
||||
apaPost('email_add', data, function(data){
|
||||
var rdata = $.parseJSON(data.data);
|
||||
showMsg(rdata.msg,function(){
|
||||
if (rdata.status){
|
||||
layer.close(index);
|
||||
emailList();
|
||||
}
|
||||
},{icon: rdata.status ? 1 : 2}, 2000);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
function emailList(page, search){
|
||||
var _data = {};
|
||||
if (typeof(page) =='undefined'){
|
||||
var page = 1;
|
||||
}
|
||||
|
||||
_data['page'] = page;
|
||||
_data['page_size'] = 10;
|
||||
if(typeof(search) != 'undefined'){
|
||||
_data['search'] = search;
|
||||
}
|
||||
|
||||
apaPost('email_list', _data, function(data){
|
||||
var rdata = $.parseJSON(data.data);
|
||||
var list = '';
|
||||
for(i in rdata.data){
|
||||
list += '<tr>';
|
||||
list +='<td><input value="'+rdata.data[i]['id']+'" class="check" onclick="checkSelect();" type="checkbox"></td>';
|
||||
list += '<td>' + rdata.data[i]['addr'] +'</td>';
|
||||
list += '<td>' + rdata.data[i]['remark'] +'</td>';
|
||||
list += '<td style="text-align:right">';
|
||||
list += '<a href="javascript:;" class="btlink" onclick="emailDel(\''+rdata.data[i]['id']+'\',\''+rdata.data[i]['addr']+'\')" title="删除">删除</a>' +
|
||||
'</td>';
|
||||
list += '</tr>';
|
||||
}
|
||||
|
||||
var con = '<div class="safe bgw">\
|
||||
<button onclick="emailAdd()" title="添加邮件地址" class="btn btn-success btn-sm" type="button" style="margin-right: 5px;">添加邮件地址</button>\
|
||||
<span style="float:right"> \
|
||||
<button batch="true" style="float: right;display: none;margin-left:10px;" onclick="delDbBatch();" title="删除选中项" class="btn btn-default btn-sm">删除选中</button>\
|
||||
</span>\
|
||||
<div class="divtable mtb10">\
|
||||
<div class="tablescroll">\
|
||||
<table id="DataBody" class="table table-hover" width="100%" cellspacing="0" cellpadding="0" border="0" style="border: 0 none;">\
|
||||
<thead><tr><th width="30"><input class="check" onclick="checkSelect();" type="checkbox"></th>\
|
||||
<th>邮件地址</th>\
|
||||
<th>备注</th>\
|
||||
<th style="text-align:right;">操作</th></tr></thead>\
|
||||
<tbody>'+ list +'</tbody>\
|
||||
</table>\
|
||||
</div>\
|
||||
<div class="dataTables_paginate paging_bootstrap page"></div>\
|
||||
</div>\
|
||||
</div>';
|
||||
|
||||
$(".soft-man-con").html(con);
|
||||
$('.dataTables_paginate').html(rdata.page);
|
||||
|
||||
readerTableChecked();
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
function dnsapiDel(id, name){
|
||||
safeMessage('删除['+name+']','您真的要删除['+name+']吗?',function(){
|
||||
var data='id='+id+'&name='+name;
|
||||
apaPost('dnsapi_del', data, function(data){
|
||||
var rdata = $.parseJSON(data.data);
|
||||
showMsg(rdata.msg,function(){
|
||||
dnsapiList();
|
||||
},{icon: rdata.status ? 1 : 2}, 600);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
var dnsapi_option = [
|
||||
{"name":"dns_cf", "title":'cloudflare', 'key':'CF_Key:CF_Email'},
|
||||
{"name":"dns_dp", "title":'dnspod/国内', 'key':'DP_Id:DP_Key'},
|
||||
{"name":"dns_dpi", "title":'dnspod/国际', 'key':'DPI_Id:DPI_Key'},
|
||||
{"name":"dns_gd", "title":'GoDaddy', 'key':'GD_Key:GD_Secret'},
|
||||
{"name":"dns_pdns", "title":'PowerDNS', 'key':'PDNS_Url:PDNS_ServerId:PDNS_Token:PDNS_Ttl'},
|
||||
{"name":"dns_lua", "title":'LuaDNS', 'key':'LUA_Key:LUA_Email'},
|
||||
{"name":"dns_me", "title":'DNSMadeEasy', 'key':'ME_Key:ME_Secret'},
|
||||
{"name":"dns_aws", "title":'Amazon Route53', 'key':'AWS_ACCESS_KEY_ID:AWS_SECRET_ACCESS_KEY'},
|
||||
{"name":"dns_ali", "title":'Aliyun', 'key':'Ali_Key:Ali_Secret'},
|
||||
{"name":"dns_ispconfig", "title":'ISPConfig', 'key':'ISPC_User:ISPC_Password:ISPC_Api:ISPC_Api_Insecure'},
|
||||
{"name":"dns_ad", "title":'Alwaysdata', 'key':'AD_API_KEY'},
|
||||
{"name":"dns_linode_v4", "title":'Linode', 'key':'LINODE_V4_API_KEY'},
|
||||
{"name":"dns_freedns", "title":'FreeDNS', 'key':'FREEDNS_User:FREEDNS_Password'},
|
||||
{"name":"dns_cyon", "title":'cyon.ch', 'key':'CY_Username:CY_Password:CY_OTP_Secret'},
|
||||
{"name":"dns_gandi_livedns", "title":'LiveDNS', 'key':'GANDI_LIVEDNS_TOKEN'},
|
||||
{"name":"dns_knot", "title":'Knot', 'key':'KNOT_SERVER:KNOT_KEY'},
|
||||
{"name":"dns_dgon", "title":'DigitalOcean', 'key':'DO_API_KEY'},
|
||||
{"name":"dns_cloudns", "title":'ClouDNS.net', 'key':'CLOUDNS_SUB_AUTH_ID:CLOUDNS_AUTH_PASSWORD'},
|
||||
{"name":"dns_namesilo", "title":'Namesilo', 'key':'Namesilo_Key'},
|
||||
{"name":"dns_azure", "title":'Azure', 'key':'AZUREDNS_SUBSCRIPTIONID:AZUREDNS_TENANTID:AZUREDNS_APPID:AZUREDNS_CLIENTSECRET'},
|
||||
{"name":"dns_selectel", "title":'selectel.com', 'key':'SL_Key'},
|
||||
{"name":"dns_zonomi", "title":'zonomi.com', 'key':'ZM_Key'},
|
||||
{"name":"dns_kinghost", "title":'KingHost', 'key':'KINGHOST_Username:KINGHOST_Password'},
|
||||
{"name":"dns_zilore", "title":'Zilore', 'key':'Zilore_Key'},
|
||||
{"name":"dns_gcloud", "title":'Google Cloud DNS', 'key':'CLOUDSDK_ACTIVE_CONFIG_NAME'},
|
||||
{"name":"dns_mydnsjp", "title":'MyDNS.JP', 'key':'MYDNSJP_MasterID:MYDNSJP_Password'},
|
||||
{"name":"dns_doapi", "title":'do.de', 'key':'DO_LETOKEN'},
|
||||
{"name":"dns_online", "title":'Online', 'key':'ONLINE_API_KEY'},
|
||||
{"name":"dns_cn", "title":'Core-Networks', 'key':'CN_User:CN_Password'},
|
||||
{"name":"dns_ultra", "title":'UltraDNS', 'key':'ULTRA_USR:ULTRA_PWD'},
|
||||
{"name":"dns_hetzner", "title":'Hetzner', 'key':'HETZNER_Token'},
|
||||
{"name":"dns_ddnss", "title":'DDNSS.de', 'key':'DDNSS_Token'},
|
||||
];
|
||||
|
||||
function getDnsapiKey(name){
|
||||
for (var i = 0; i < dnsapi_option.length; i++) {
|
||||
if (dnsapi_option[i]['name'] == name){
|
||||
return dnsapi_option[i]['key'];
|
||||
}
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
function getDnsapiTitle(name){
|
||||
for (var i = 0; i < dnsapi_option.length; i++) {
|
||||
if (dnsapi_option[i]['name'] == name){
|
||||
return dnsapi_option[i]['title'];
|
||||
}
|
||||
}
|
||||
return '其他';
|
||||
}
|
||||
|
||||
|
||||
|
||||
function dnsapiAdd(row){
|
||||
// console.log(row);
|
||||
var option_name = '';
|
||||
var option_remark = '';
|
||||
var option_type = 'cf';
|
||||
var option_val = '';
|
||||
var option_id = 0;
|
||||
if (typeof(row) != 'undefined'){
|
||||
option_name = row['name'];
|
||||
option_remark = row['remark'];
|
||||
option_type = row['type'];
|
||||
option_val = row['val'];
|
||||
option_id = row['id'];
|
||||
|
||||
}
|
||||
|
||||
// console.log(option_name);
|
||||
function renderDnsapiOption(name, val){
|
||||
var vlist = {};
|
||||
if (val != ''){
|
||||
var t = val.split('~');
|
||||
for (var i = 0; i < t.length; i++) {
|
||||
var kv = t[i].split('|');
|
||||
if (kv.length == 2){
|
||||
vlist[kv[0]] = kv[1];
|
||||
} else {
|
||||
vlist[kv[0]] = '';
|
||||
}
|
||||
}
|
||||
// console.log(vlist);
|
||||
}
|
||||
|
||||
|
||||
var key = getDnsapiKey(name);
|
||||
var klist = key.split(':');
|
||||
// console.log(klist);
|
||||
var option_html = '';
|
||||
for (var i = 0; i < klist.length; i++) {
|
||||
var klist_val = '';
|
||||
if (klist[i] in vlist){
|
||||
klist_val = vlist[klist[i]];
|
||||
}
|
||||
|
||||
option_html += "\
|
||||
<span class='tname'>"+klist[i]+"</span>\
|
||||
<div class='info-r'>\
|
||||
<input name='"+klist[i]+"' class='bt-input-text mr5' style='width:100%;' value='"+klist_val+"' placeholder='请输入对应值' type='text'>\
|
||||
</div>";
|
||||
}
|
||||
$('#dnsapi_option').html(option_html);
|
||||
}
|
||||
|
||||
layer.open({
|
||||
type: 1,
|
||||
area: '500px',
|
||||
title: '添加DNSAPI',
|
||||
closeBtn: 1,
|
||||
shift: 5,
|
||||
shadeClose: true,
|
||||
btn:["提交","关闭"],
|
||||
content: "<form class='bt-form pd20' id='dnsapi_add'>\
|
||||
<div class='line'>\
|
||||
<span class='tname'>名称</span>\
|
||||
<div class='info-r'><input name='name' class='bt-input-text mr5' style='width:100%;' placeholder='名称' value='"+option_name+"' type='text'></div>\
|
||||
</div>\
|
||||
<div class='line'>\
|
||||
<span class='tname'>DNSAPI类型</span>\
|
||||
<div class='info-r'>\
|
||||
<select class='bt-input-text mr5' name='type'>\
|
||||
<option name='cf'>cloudflare</option>\
|
||||
</select>\
|
||||
</div>\
|
||||
</div>\
|
||||
<div class='line' id='dnsapi_option'>\
|
||||
<span class='tname'>CF_Key</span>\
|
||||
<div class='info-r'>\
|
||||
<input name='v1' class='bt-input-text mr5' style='width:100%;' placeholder='请输入对应值' type='text'>\
|
||||
</div>\
|
||||
<span class='tname'>CF_Email</span>\
|
||||
<div class='info-r'>\
|
||||
<input name='v2' class='bt-input-text mr5' style='width:100%;' placeholder='请输入对应值' type='text'>\
|
||||
</div>\
|
||||
</div>\
|
||||
<div class='line'>\
|
||||
<span class='tname'>备注</span>\
|
||||
<div class='info-r'><input name='remark' class='bt-input-text mr5' style='width:100%;' placeholder='备注' value='"+option_remark+"' type='text'></div>\
|
||||
</div>\
|
||||
<input name='id' value='"+option_id+"' type='hidden'>\
|
||||
</form>",
|
||||
success:function(){
|
||||
$("input[name='name']").keyup(function(){
|
||||
var v = $(this).val();
|
||||
$("input[name='remark']").val(v);
|
||||
});
|
||||
|
||||
var option = '';
|
||||
for (var i = 0; i<dnsapi_option.length; i++) {
|
||||
|
||||
if (dnsapi_option[i]['name'] == option_type){
|
||||
option += "<option value='"+dnsapi_option[i]['name']+"' key='"+dnsapi_option[i]['key']+"' selected>"+dnsapi_option[i]['title']+"</option>";
|
||||
} else {
|
||||
option += "<option value='"+dnsapi_option[i]['name']+"' key='"+dnsapi_option[i]['key']+"'>"+dnsapi_option[i]['title']+"</option>";
|
||||
}
|
||||
}
|
||||
|
||||
renderDnsapiOption(option_type, option_val);
|
||||
$('select[name="type"]').html(option);
|
||||
$('select[name="type"]').change(function(){
|
||||
var name = $(this).val();
|
||||
|
||||
if (option_type == name){
|
||||
renderDnsapiOption(name, option_val);
|
||||
} else {
|
||||
renderDnsapiOption(name,'');
|
||||
}
|
||||
|
||||
});
|
||||
},
|
||||
yes:function(index) {
|
||||
var data = $("#dnsapi_add").serialize();
|
||||
data = decodeURIComponent(data);
|
||||
data = toArrayObject(data);
|
||||
|
||||
var key = getDnsapiKey(data['type']);
|
||||
var klist = key.split(':');
|
||||
var val = '';
|
||||
for (var i = 0; i < klist.length; i++) {
|
||||
var k = klist[i];
|
||||
if (k in data){
|
||||
if (klist.length - 1 == i){
|
||||
val += k + '|' + data[k];
|
||||
} else {
|
||||
val += k + '|' + data[k]+'~';
|
||||
}
|
||||
}
|
||||
}
|
||||
data['val'] = val;
|
||||
apaPost('dnsapi_add', data, function(data){
|
||||
var rdata = $.parseJSON(data.data);
|
||||
showMsg(rdata.msg,function(){
|
||||
if (rdata.status){
|
||||
layer.close(index);
|
||||
dnsapiList();
|
||||
}
|
||||
},{icon: rdata.status ? 1 : 2}, 2000);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
function dnsapiList(page, search){
|
||||
var _data = {};
|
||||
if (typeof(page) =='undefined'){
|
||||
var page = 1;
|
||||
}
|
||||
|
||||
_data['page'] = page;
|
||||
_data['page_size'] = 10;
|
||||
if(typeof(search) != 'undefined'){
|
||||
_data['search'] = search;
|
||||
}
|
||||
|
||||
apaPost('dnsapi_list', _data, function(data){
|
||||
var rdata = $.parseJSON(data.data);
|
||||
var list = '';
|
||||
for(i in rdata.data){
|
||||
list += '<tr>';
|
||||
list +='<td><input value="'+rdata.data[i]['id']+'" class="check" onclick="checkSelect();" type="checkbox"></td>';
|
||||
list += '<td>' + rdata.data[i]['name'] +'</td>';
|
||||
list += '<td>' + getDnsapiTitle(rdata.data[i]['type']) +'</td>';
|
||||
list += '<td>' + rdata.data[i]['val'].split('~').join("<br/>") +'</td>';
|
||||
list += '<td>' + rdata.data[i]['remark'] +'</td>';
|
||||
|
||||
list += '<td style="text-align:right">';
|
||||
list += '<a href="javascript:;" index="'+i+'" class="btlink edit" title="编辑">编辑</a> | ';
|
||||
list += '<a href="javascript:;" class="btlink" onclick="dnsapiDel(\''+rdata.data[i]['id']+'\',\''+rdata.data[i]['name']+'\')" title="删除">删除</a>';
|
||||
list += '</td></tr>';
|
||||
}
|
||||
|
||||
var con = '<div class="safe bgw">\
|
||||
<button onclick="dnsapiAdd()" title="DNSAPI" class="btn btn-success btn-sm" type="button" style="margin-right: 5px;">添加DNSAPI</button>\
|
||||
<span style="float:right"> \
|
||||
<button batch="true" style="float: right;display: none;margin-left:10px;" onclick="delDbBatch();" title="删除选中项" class="btn btn-default btn-sm">删除选中</button>\
|
||||
</span>\
|
||||
<div class="divtable mtb10">\
|
||||
<div class="tablescroll">\
|
||||
<table id="DataBody" class="table table-hover" width="100%" cellspacing="0" cellpadding="0" border="0" style="border: 0 none;">\
|
||||
<thead><tr><th width="30"><input class="check" onclick="checkSelect();" type="checkbox"></th>\
|
||||
<th>名称</th>\
|
||||
<th>类型</th>\
|
||||
<th>值</th>\
|
||||
<th>备注</th>\
|
||||
<th style="text-align:right;">操作</th></tr></thead>\
|
||||
<tbody>'+ list +'</tbody>\
|
||||
</table>\
|
||||
</div>\
|
||||
<div class="dataTables_paginate paging_bootstrap page"></div>\
|
||||
</div>\
|
||||
</div>';
|
||||
|
||||
$(".soft-man-con").html(con);
|
||||
$('.dataTables_paginate').html(rdata.page);
|
||||
|
||||
$('.edit').click(function(){
|
||||
var idx = $(this).attr('index');
|
||||
var row = rdata.data[idx];
|
||||
// console.log(row);
|
||||
dnsapiAdd(row);
|
||||
})
|
||||
|
||||
readerTableChecked();
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
function domainDel(id, name){
|
||||
safeMessage('删除['+name+']','您真的要删除['+name+']吗?',function(){
|
||||
var data='id='+id+'&name='+name;
|
||||
apaPost('domain_del', data, function(data){
|
||||
var rdata = $.parseJSON(data.data);
|
||||
showMsg(rdata.msg,function(){
|
||||
domainList();
|
||||
},{icon: rdata.status ? 1 : 2}, 600);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function domainStatusToggle(id){
|
||||
var data='id='+id;
|
||||
apaPost('domain_status_toggle', data, function(data){
|
||||
var rdata = $.parseJSON(data.data);
|
||||
showMsg(rdata.msg,function(){
|
||||
domainList();
|
||||
},{icon: rdata.status ? 1 : 2}, 600);
|
||||
});
|
||||
}
|
||||
|
||||
function domainIdCmd(id){
|
||||
var data='id='+id;
|
||||
apaPost('get_run_hook_id_cmd', data, function(data){
|
||||
var rdata = $.parseJSON(data.data);
|
||||
// console.log(rdata);
|
||||
layer.open({
|
||||
title: "手动同步命令",
|
||||
area: ['600px', '180px'],
|
||||
type:1,
|
||||
closeBtn: 1,
|
||||
shadeClose: false,
|
||||
btn:["复制","取消"],
|
||||
content: '<div class="pd15">\
|
||||
<div class="divtable">\
|
||||
<pre class="layui-code">'+rdata.data+'</pre>\
|
||||
</div>\
|
||||
</div>',
|
||||
success:function(){
|
||||
copyText(rdata.data);
|
||||
},
|
||||
yes:function(){
|
||||
copyText(rdata.data);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function domainHookCmd(){
|
||||
apaPost('run_hook_cmd', {}, function(data){
|
||||
var rdata = $.parseJSON(data.data);
|
||||
layer.open({
|
||||
title: "手动同步全部命令",
|
||||
area: ['600px', '180px'],
|
||||
type:1,
|
||||
closeBtn: 1,
|
||||
shadeClose: false,
|
||||
btn:["复制","取消"],
|
||||
content: '<div class="pd15">\
|
||||
<div class="divtable">\
|
||||
<pre class="layui-code">'+rdata.data+'</pre>\
|
||||
</div>\
|
||||
</div>',
|
||||
success:function(){
|
||||
copyText(rdata.data);
|
||||
},
|
||||
yes:function(){
|
||||
copyText(rdata.data);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function syncCfCmd(){
|
||||
apaPost('run_sync_cf_cmd', {}, function(data){
|
||||
var rdata = $.parseJSON(data.data);
|
||||
layer.open({
|
||||
title: "手动同步CloudFlare全部域名命令",
|
||||
area: ['600px', '180px'],
|
||||
type:1,
|
||||
closeBtn: 1,
|
||||
shadeClose: false,
|
||||
btn:["复制","取消"],
|
||||
content: '<div class="pd15">\
|
||||
<div class="divtable">\
|
||||
<pre class="layui-code">'+rdata.data+'</pre>\
|
||||
</div>\
|
||||
</div>',
|
||||
success:function(){
|
||||
copyText(rdata.data);
|
||||
},
|
||||
yes:function(){
|
||||
copyText(rdata.data);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function syncDnsPodCmd(){
|
||||
apaPost('run_sync_dnspod_cmd', {}, function(data){
|
||||
var rdata = $.parseJSON(data.data);
|
||||
layer.open({
|
||||
title: "手动同步DnsPod全部域名命令",
|
||||
area: ['600px', '180px'],
|
||||
type:1,
|
||||
closeBtn: 1,
|
||||
shadeClose: false,
|
||||
btn:["复制","取消"],
|
||||
content: '<div class="pd15">\
|
||||
<div class="divtable">\
|
||||
<pre class="layui-code">'+rdata.data+'</pre>\
|
||||
</div>\
|
||||
</div>',
|
||||
success:function(){
|
||||
copyText(rdata.data);
|
||||
},
|
||||
yes:function(){
|
||||
copyText(rdata.data);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function domainAdd(row){
|
||||
|
||||
var option_domian = '';
|
||||
var option_remark = '备注';
|
||||
var option_email = '';
|
||||
var option_id = 0;
|
||||
var option_dnsapi_id = 0;
|
||||
if (typeof(row) != 'undefined'){
|
||||
option_domian = row['domain'];
|
||||
option_remark = row['remark'];
|
||||
option_email = row['email'];
|
||||
option_id = row['id'];
|
||||
option_dnsapi_id = row['dnsapi_id'];
|
||||
}
|
||||
|
||||
layer.open({
|
||||
type: 1,
|
||||
area: '500px',
|
||||
title: '添加顶级域名',
|
||||
closeBtn: 1,
|
||||
shift: 5,
|
||||
shadeClose: true,
|
||||
btn:["提交","关闭"],
|
||||
content: "<form class='bt-form pd20' id='domain_add'>\
|
||||
<div class='line'>\
|
||||
<span class='tname'>域名</span>\
|
||||
<div class='info-r'><input name='domain' class='bt-input-text mr5' style='width:100%;' value='"+option_domian+"' placeholder='域名' type='text'></div>\
|
||||
</div>\
|
||||
<div class='line'>\
|
||||
<span class='tname'>DNSAPI</span>\
|
||||
<div class='info-r'>\
|
||||
<select class='bt-input-text mr5' name='dnsapi_id'>\
|
||||
<option name='0'>无设置</option>\
|
||||
</select>\
|
||||
</div>\
|
||||
</div>\
|
||||
<div class='line'>\
|
||||
<span class='tname'>邮件</span>\
|
||||
<div class='info-r'><input name='email' class='bt-input-text mr5' style='width:100%;' value='"+option_email+"' placeholder='邮件' type='text'></div>\
|
||||
</div>\
|
||||
<div class='line'>\
|
||||
<span class='tname'>备注</span>\
|
||||
<div class='info-r'><input name='remark' class='bt-input-text mr5' style='width:100%;' value='"+option_remark+"' placeholder='备注' type='text'></div>\
|
||||
</div>\
|
||||
<input name='id' value='"+option_id+"' type='hidden'>\
|
||||
</form>",
|
||||
success:function(){
|
||||
// $("input[name='domain']").keyup(function(){
|
||||
// var v = $(this).val();
|
||||
// $("input[name='remark']").val(v);
|
||||
// });
|
||||
|
||||
var dnsapi_id_html = "<option value='0'>无设置</option>";
|
||||
apaPostN('dnsapi_list_all', {}, function(data){
|
||||
var rdata = $.parseJSON(data.data);
|
||||
for (var i = 0; i < rdata.length; i++) {
|
||||
if (option_dnsapi_id == rdata[i]['id']){
|
||||
dnsapi_id_html += "<option value='"+rdata[i]['id']+"' selected>"+rdata[i]['name']+"</option>";
|
||||
} else {
|
||||
dnsapi_id_html += "<option value='"+rdata[i]['id']+"'>"+rdata[i]['name']+"</option>";
|
||||
}
|
||||
}
|
||||
$('select[name="dnsapi_id"]').html(dnsapi_id_html);
|
||||
});
|
||||
},
|
||||
yes:function(index) {
|
||||
var data = $("#domain_add").serialize();
|
||||
data = decodeURIComponent(data);
|
||||
apaPost('domain_add', data, function(data){
|
||||
var rdata = $.parseJSON(data.data);
|
||||
showMsg(rdata.msg,function(){
|
||||
if (rdata.status){
|
||||
layer.close(index);
|
||||
domainList();
|
||||
}
|
||||
},{icon: rdata.status ? 1 : 2}, 2000);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function domainList(page, search){
|
||||
var _data = {};
|
||||
if (typeof(page) =='undefined'){
|
||||
var page = 1;
|
||||
}
|
||||
|
||||
_data['page'] = page;
|
||||
_data['page_size'] = 10;
|
||||
if(typeof(search) != 'undefined'){
|
||||
_data['search'] = search;
|
||||
}
|
||||
|
||||
apaPost('domain_list', _data, function(data){
|
||||
var rdata = $.parseJSON(data.data);
|
||||
// console.log(rdata);
|
||||
var list = '';
|
||||
for(i in rdata.data){
|
||||
list += '<tr>';
|
||||
list +='<td><input value="'+rdata.data[i]['id']+'" class="check" onclick="checkSelect();" type="checkbox"></td>';
|
||||
list += '<td>' + rdata.data[i]['domain'] +'</td>';
|
||||
list += '<td>' + rdata.data[i]['dnsapi_id_alias'] +'</td>';
|
||||
list += '<td>' + rdata.data[i]['email'] +'</td>';
|
||||
list += '<td>' + rdata.data[i]['remark'] +'</td>';
|
||||
|
||||
|
||||
|
||||
if (rdata.data[i]['effective_date'] == ''){
|
||||
list += '<td>空/未申请</td>';
|
||||
} else {
|
||||
list += '<td>'+getFormatTime(rdata.data[i]['effective_date'],'yyyy/MM/dd')+'</td>';
|
||||
}
|
||||
|
||||
if (rdata.data[i]['expiration_date'] == ''){
|
||||
list += '<td>空/未申请</td>';
|
||||
} else {
|
||||
list += '<td>'+getFormatTime(rdata.data[i]['expiration_date'],'yyyy/MM/dd')+'</td>';
|
||||
}
|
||||
|
||||
if (rdata.data[i]['status'] == '0'){
|
||||
list += '<td><a href="javascript:;" index="'+i+'" class="btlink status">否</a></td>';
|
||||
} else {
|
||||
list += '<td><a href="javascript:;" index="'+i+'" class="btlink status">是</a></td>';
|
||||
}
|
||||
|
||||
list += '<td style="text-align:right">';
|
||||
list += '<a href="javascript:;" index="'+i+'" class="btlink cmd" title="命令">命令</a> | ';
|
||||
list += '<a href="javascript:;" index="'+i+'" class="btlink edit" title="编辑">编辑</a> | ';
|
||||
list += '<a href="javascript:;" class="btlink" onclick="domainDel(\''+rdata.data[i]['id']+'\',\''+rdata.data[i]['domain']+'\')" title="删除">删除</a>';
|
||||
list += '</td></tr>';
|
||||
}
|
||||
|
||||
var con = '<div class="safe bgw">\
|
||||
<button onclick="domainAdd()" title="添加顶级域名" class="btn btn-success btn-sm" type="button" style="margin-right: 5px;">添加域名</button>\
|
||||
<button onclick="domainHookCmd()" title="全部同步命令" class="btn btn-success btn-sm" type="button" style="margin-right: 5px;">全部同步命令</button>\
|
||||
<button onclick="syncCfCmd()" title="cloudflare同步命令" class="btn btn-success btn-sm" type="button" style="margin-right: 5px;">cloudflare同步命令</button>\
|
||||
<button onclick="syncDnsPodCmd()" title="DnsPod国际同步命令" class="btn btn-success btn-sm" type="button" style="margin-right: 5px;">DnsPod国际同步命令</button>\
|
||||
<div class="divtable mtb10">\
|
||||
<div class="tablescroll">\
|
||||
<table id="DataBody" class="table table-hover" width="100%" cellspacing="0" cellpadding="0" border="0" style="border: 0 none;">\
|
||||
<thead><tr><th width="30"><input class="check" onclick="checkSelect();" type="checkbox"></th>\
|
||||
<th>域名</th>\
|
||||
<th>DNSAPI</th>\
|
||||
<th>邮件</th>\
|
||||
<th>备注</th>\
|
||||
<th>开始</th>\
|
||||
<th>结束</th>\
|
||||
<th>同步</th>\
|
||||
<th style="text-align:right;">操作</th></tr></thead>\
|
||||
<tbody>'+ list +'</tbody>\
|
||||
</table>\
|
||||
</div>\
|
||||
<div class="dataTables_paginate paging_bootstrap page"></div>\
|
||||
</div>\
|
||||
</div>';
|
||||
|
||||
$(".soft-man-con").html(con);
|
||||
$('.dataTables_paginate').html(rdata.page);
|
||||
|
||||
$('.edit').click(function(){
|
||||
var idx = $(this).attr('index');
|
||||
var row = rdata.data[idx];
|
||||
domainAdd(row);
|
||||
});
|
||||
|
||||
$('.status').click(function(){
|
||||
var idx = $(this).attr('index');
|
||||
var row = rdata.data[idx];
|
||||
domainStatusToggle(row['id']);
|
||||
});
|
||||
|
||||
$('.cmd').click(function(){
|
||||
var idx = $(this).attr('index');
|
||||
var row = rdata.data[idx];
|
||||
domainIdCmd(row['id']);
|
||||
});
|
||||
|
||||
readerTableChecked();
|
||||
});
|
||||
}
|
||||
@@ -1,134 +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 'acme_pandominassl_apply'
|
||||
|
||||
|
||||
def getPluginDir():
|
||||
return mw.getPluginDir() + '/' + getPluginName()
|
||||
|
||||
|
||||
def getServerDir():
|
||||
return mw.getServerDir() + '/' + getPluginName()
|
||||
|
||||
|
||||
def getTaskConf():
|
||||
conf = getServerDir() + "/cron_config.json"
|
||||
return conf
|
||||
|
||||
def getTaskDeltaConf():
|
||||
conf = getServerDir() + "/cron_delta_config.json"
|
||||
return conf
|
||||
|
||||
|
||||
def getConfigData():
|
||||
try:
|
||||
return json.loads(mw.readFile(getTaskConf()))
|
||||
except:
|
||||
pass
|
||||
return {
|
||||
"task_id": -1,
|
||||
"period": "day-n",
|
||||
"where1": "1",
|
||||
"hour": "0",
|
||||
"minute": "15",
|
||||
}
|
||||
|
||||
|
||||
def createBgTask():
|
||||
removeBgTask()
|
||||
createBgTaskByName(getPluginName())
|
||||
return True
|
||||
|
||||
|
||||
def createBgTaskByName(name):
|
||||
args = getConfigData()
|
||||
_name = "[勿删]ACME泛域名SSL[APA]"
|
||||
res = mw.M("crontab").field("id, name").where("name=?", (_name,)).find()
|
||||
if res:
|
||||
return True
|
||||
|
||||
if "task_id" in args and args["task_id"] > 0:
|
||||
res = mw.M("crontab").field("id, name").where(
|
||||
"id=?", (args["task_id"],)).find()
|
||||
if res and res["id"] == args["task_id"]:
|
||||
print("计划任务已经存在!")
|
||||
return True
|
||||
import crontab_api
|
||||
api = crontab_api.crontab_api()
|
||||
|
||||
mw_dir = mw.getPanelDir()
|
||||
cmd = '''
|
||||
mw_dir=%s
|
||||
rname=%s
|
||||
plugin_path=%s
|
||||
script_path=%s
|
||||
logs_file=$plugin_path/${rname}.log
|
||||
''' % (mw_dir, name, getServerDir(), getPluginDir())
|
||||
cmd += 'echo "★【`date +"%Y-%m-%d %H:%M:%S"`】 STSRT★" >> $logs_file' + "\n"
|
||||
cmd += 'echo ">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>" >> $logs_file' + "\n"
|
||||
cmd += 'echo "python3 $script_path/index.py run_hook"' + "\n"
|
||||
cmd += 'cd $mw_dir && python3 $script_path/index.py run_hook' + "\n"
|
||||
cmd += 'echo "【`date +"%Y-%m-%d %H:%M:%S"`】 END★" >> $logs_file' + "\n"
|
||||
cmd += 'echo "<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<" >> $logs_file' + "\n"
|
||||
|
||||
params = {
|
||||
'name': _name,
|
||||
'type': args['period'],
|
||||
'week': "",
|
||||
'where1': args['where1'],
|
||||
'hour': args['hour'],
|
||||
'minute': args['minute'],
|
||||
'save': "",
|
||||
'backup_to': "",
|
||||
'stype': "toShell",
|
||||
'sname': '',
|
||||
'sbody': cmd,
|
||||
'urladdress': '',
|
||||
}
|
||||
|
||||
task_id = api.add(params)
|
||||
if task_id > 0:
|
||||
args["task_id"] = task_id
|
||||
args["name"] = name
|
||||
mw.writeFile(getTaskConf(), json.dumps(args))
|
||||
|
||||
|
||||
def removeBgTask():
|
||||
cfg = getConfigData()
|
||||
if "task_id" in cfg and cfg["task_id"] > 0:
|
||||
res = mw.M("crontab").field("id, name").where(
|
||||
"id=?", (cfg["task_id"],)).find()
|
||||
if res and res["id"] == cfg["task_id"]:
|
||||
import crontab_api
|
||||
api = crontab_api.crontab_api()
|
||||
data = api.delete(cfg["task_id"])
|
||||
if data[0]:
|
||||
cfg["task_id"] = -1
|
||||
mw.writeFile(getTaskConf(), json.dumps(cfg))
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
if len(sys.argv) > 1:
|
||||
action = sys.argv[1]
|
||||
if action == "remove":
|
||||
removeBgTask()
|
||||
elif action == "add":
|
||||
createBgTask()
|
||||
|
Before Width: | Height: | Size: 42 KiB |
@@ -1,31 +0,0 @@
|
||||
<style>
|
||||
.overflow_hide {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
vertical-align: middle;
|
||||
}
|
||||
</style>
|
||||
|
||||
<div class="bt-form">
|
||||
<div class='plugin_version'></div>
|
||||
<div class="bt-w-main">
|
||||
<div class="bt-w-menu">
|
||||
<p class="bgw" onclick="pluginService('alist');">服务</p>
|
||||
<p onclick="pluginInitD('alist');">自启动</p>
|
||||
<p onclick="alistCommonFunc();">常用功能</p>
|
||||
<p onclick="pluginConfigTpl('alist',$('.plugin_version').attr('version'));">配置修改</p>
|
||||
<p onclick="pluginLogs('alist','','run_log');">运行日志</p>
|
||||
<p onclick="alistReadme();">相关说明</p>
|
||||
|
||||
</div>
|
||||
<div class="bt-w-con pd15">
|
||||
<div class="soft-man-con" style="height: 520px; overflow: auto;"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script type="text/javascript">
|
||||
$.getScript( "/plugins/file?name=alist&f=js/alist.js", function(){
|
||||
pluginService('alist', $('.plugin_version').attr('version'));
|
||||
});
|
||||
</script>
|
||||
@@ -1,319 +0,0 @@
|
||||
# coding:utf-8
|
||||
|
||||
import sys
|
||||
import io
|
||||
import os
|
||||
import time
|
||||
import re
|
||||
import json
|
||||
|
||||
web_dir = os.getcwd() + "/web"
|
||||
if os.path.exists(web_dir):
|
||||
sys.path.append(web_dir)
|
||||
os.chdir(web_dir)
|
||||
|
||||
import core.mw as mw
|
||||
|
||||
app_debug = False
|
||||
if mw.isAppleSystem():
|
||||
app_debug = True
|
||||
|
||||
|
||||
def getPluginName():
|
||||
return 'alist'
|
||||
|
||||
|
||||
def getPluginDir():
|
||||
return mw.getPluginDir() + '/' + getPluginName()
|
||||
|
||||
|
||||
def getServerDir():
|
||||
return mw.getServerDir() + '/' + getPluginName()
|
||||
|
||||
|
||||
def getInitDFile():
|
||||
current_os = mw.getOs()
|
||||
if current_os == 'darwin':
|
||||
return '/tmp/' + getPluginName()
|
||||
|
||||
if current_os.startswith('freebsd'):
|
||||
return '/etc/rc.d/' + getPluginName()
|
||||
|
||||
return '/etc/init.d/' + getPluginName()
|
||||
|
||||
|
||||
def getInitDTpl():
|
||||
path = getPluginDir() + "/init.d/" + getPluginName() + ".tpl"
|
||||
return path
|
||||
|
||||
|
||||
def getArgs():
|
||||
args = sys.argv[3:]
|
||||
tmp = {}
|
||||
args_len = len(args)
|
||||
|
||||
if args_len == 1:
|
||||
t = args[0].strip('{').strip('}')
|
||||
if t.strip() == '':
|
||||
tmp = []
|
||||
else:
|
||||
t = t.split(':')
|
||||
tmp[t[0]] = t[1]
|
||||
tmp[t[0]] = t[1]
|
||||
elif args_len > 1:
|
||||
for i in range(len(args)):
|
||||
t = args[i].split(':')
|
||||
tmp[t[0]] = t[1]
|
||||
return tmp
|
||||
|
||||
def checkArgs(data, ck=[]):
|
||||
for i in range(len(ck)):
|
||||
if not ck[i] in data:
|
||||
return (False, mw.returnJson(False, '参数:(' + ck[i] + ')没有!'))
|
||||
return (True, mw.returnJson(True, 'ok'))
|
||||
|
||||
|
||||
def getConf():
|
||||
path = getServerDir() + "/data/config.json"
|
||||
return path
|
||||
|
||||
def configTpl():
|
||||
path = getPluginDir() + '/tpl'
|
||||
pathFile = os.listdir(path)
|
||||
tmp = []
|
||||
for one in pathFile:
|
||||
file = path + '/' + one
|
||||
tmp.append(file)
|
||||
return mw.getJson(tmp)
|
||||
|
||||
|
||||
def readConfigTpl():
|
||||
args = getArgs()
|
||||
data = checkArgs(args, ['file'])
|
||||
if not data[0]:
|
||||
return data[1]
|
||||
|
||||
content = mw.readFile(args['file'])
|
||||
content = contentReplace(content)
|
||||
return mw.returnJson(True, 'ok', content)
|
||||
|
||||
def __release_port(port, ps = '开启端口'):
|
||||
try:
|
||||
from utils.firewall import Firewall as MwFirewall
|
||||
MwFirewall.instance().addAcceptPort(port, ps, 'port')
|
||||
return port
|
||||
except Exception as e:
|
||||
return "Release failed {}".format(e)
|
||||
|
||||
def __delete_port(port):
|
||||
try:
|
||||
from utils.firewall import Firewall as MwFirewall
|
||||
MwFirewall.instance().delAcceptPortCmd(port, 'tcp')
|
||||
return port
|
||||
except Exception as e:
|
||||
return "Delete failed {}".format(e)
|
||||
|
||||
|
||||
def openPort():
|
||||
content = mw.readFile(getConf())
|
||||
data = json.loads(content)
|
||||
http_port = data['scheme']['http_port']
|
||||
__release_port(http_port, 'alist')
|
||||
return True
|
||||
|
||||
def status():
|
||||
cmd = "ps aux|grep alist |grep -v grep | grep -v python | grep -v mdserver-web | awk '{print $2}'"
|
||||
data = mw.execShell(cmd)
|
||||
if data[0] == '':
|
||||
return 'stop'
|
||||
return 'start'
|
||||
|
||||
def contentReplace(content):
|
||||
service_path = mw.getServerDir()
|
||||
content = content.replace('{$ROOT_PATH}', mw.getFatherDir())
|
||||
content = content.replace('{$SERVER_PATH}', service_path)
|
||||
content = content.replace('{$SERVER_APP}', service_path + '/'+getPluginName())
|
||||
return content
|
||||
|
||||
def initDreplace():
|
||||
|
||||
file_tpl = getInitDTpl()
|
||||
service_path = mw.getServerDir()
|
||||
|
||||
initD_path = getServerDir() + '/init.d'
|
||||
if not os.path.exists(initD_path):
|
||||
os.mkdir(initD_path)
|
||||
file_bin = initD_path + '/' + getPluginName()
|
||||
|
||||
# initd replace
|
||||
if not os.path.exists(file_bin):
|
||||
content = mw.readFile(file_tpl)
|
||||
content = content.replace('{$SERVER_PATH}', service_path)
|
||||
mw.writeFile(file_bin, content)
|
||||
mw.execShell('chmod +x ' + file_bin)
|
||||
|
||||
|
||||
# systemd
|
||||
systemDir = mw.systemdCfgDir()
|
||||
systemService = systemDir + '/' + getPluginName() + '.service'
|
||||
if os.path.exists(systemDir) and not os.path.exists(systemService):
|
||||
systemServiceTpl = getPluginDir() + '/init.d/' + getPluginName() + '.service.tpl'
|
||||
service_path = mw.getServerDir()
|
||||
se_content = mw.readFile(systemServiceTpl)
|
||||
se_content = se_content.replace('{$SERVER_PATH}', service_path)
|
||||
mw.writeFile(systemService, se_content)
|
||||
mw.execShell('systemctl daemon-reload')
|
||||
time.sleep(1)
|
||||
return file_bin
|
||||
|
||||
|
||||
def alistOp(method):
|
||||
file = initDreplace()
|
||||
|
||||
current_os = mw.getOs()
|
||||
if current_os == "darwin":
|
||||
data = mw.execShell(file + ' ' + method)
|
||||
if data[1] == '':
|
||||
return 'ok'
|
||||
return data[1]
|
||||
|
||||
if current_os.startswith("freebsd"):
|
||||
data = mw.execShell('service ' + getPluginName() + ' ' + method)
|
||||
if data[1] == '':
|
||||
return 'ok'
|
||||
return data[1]
|
||||
|
||||
data = mw.execShell('systemctl ' + method + ' ' + getPluginName())
|
||||
if data[1] == '':
|
||||
return 'ok'
|
||||
return data[1]
|
||||
|
||||
|
||||
def start():
|
||||
return alistOp('start')
|
||||
|
||||
|
||||
def stop():
|
||||
return alistOp('stop')
|
||||
|
||||
|
||||
def restart():
|
||||
status = alistOp('restart')
|
||||
return status
|
||||
|
||||
|
||||
def reload():
|
||||
return alistOp('reload')
|
||||
|
||||
|
||||
def initdStatus():
|
||||
current_os = mw.getOs()
|
||||
if current_os == 'darwin':
|
||||
return "Apple Computer does not support"
|
||||
|
||||
if current_os.startswith('freebsd'):
|
||||
initd_bin = getInitDFile()
|
||||
if os.path.exists(initd_bin):
|
||||
return 'ok'
|
||||
|
||||
shell_cmd = 'systemctl status ' + \
|
||||
getPluginName() + ' | grep loaded | grep "enabled;"'
|
||||
data = mw.execShell(shell_cmd)
|
||||
if data[0] == '':
|
||||
return 'fail'
|
||||
return 'ok'
|
||||
|
||||
|
||||
def initdInstall():
|
||||
current_os = mw.getOs()
|
||||
if current_os == 'darwin':
|
||||
return "Apple Computer does not support"
|
||||
|
||||
# freebsd initd install
|
||||
if current_os.startswith('freebsd'):
|
||||
import shutil
|
||||
source_bin = initDreplace()
|
||||
initd_bin = getInitDFile()
|
||||
shutil.copyfile(source_bin, initd_bin)
|
||||
mw.execShell('chmod +x ' + initd_bin)
|
||||
mw.execShell('sysrc ' + getPluginName() + '_enable="YES"')
|
||||
return 'ok'
|
||||
|
||||
mw.execShell('systemctl enable ' + getPluginName())
|
||||
return 'ok'
|
||||
|
||||
|
||||
def initdUinstall():
|
||||
current_os = mw.getOs()
|
||||
if current_os == 'darwin':
|
||||
return "Apple Computer does not support"
|
||||
|
||||
if current_os.startswith('freebsd'):
|
||||
initd_bin = getInitDFile()
|
||||
os.remove(initd_bin)
|
||||
mw.execShell('sysrc ' + getPluginName() + '_enable="NO"')
|
||||
return 'ok'
|
||||
|
||||
mw.execShell('systemctl disable ' + getPluginName())
|
||||
return 'ok'
|
||||
|
||||
|
||||
def runLog():
|
||||
return getServerDir() + '/data/log/log.log'
|
||||
|
||||
def pSqliteDb(dbname='databases'):
|
||||
pos_file = getServerDir() + '/data/'
|
||||
file = pos_file + '/data.db'
|
||||
name = 'data'
|
||||
conn = mw.M(dbname).dbPos(pos_file, name)
|
||||
return conn
|
||||
|
||||
def clearCopyTask():
|
||||
conn = pSqliteDb('x_task_items')
|
||||
conn.where('key=?', ('copy',)).setField('persist_data','[]')
|
||||
restart()
|
||||
return mw.returnJson(True, '清空成功并重启服务!')
|
||||
|
||||
def homePage():
|
||||
content = mw.readFile(getConf())
|
||||
data = json.loads(content)
|
||||
http_port = data['scheme']['http_port']
|
||||
ip = mw.getLocalIp()
|
||||
if mw.isAppleSystem():
|
||||
ip = '127.0.0.1'
|
||||
url = 'http://'+ip+":"+str(http_port)
|
||||
# print(url)
|
||||
return mw.returnJson(True, 'ok!', url)
|
||||
|
||||
if __name__ == "__main__":
|
||||
func = sys.argv[1]
|
||||
if func == 'status':
|
||||
print(status())
|
||||
elif func == 'start':
|
||||
print(start())
|
||||
elif func == 'stop':
|
||||
print(stop())
|
||||
elif func == 'restart':
|
||||
print(restart())
|
||||
elif func == 'reload':
|
||||
print(reload())
|
||||
elif func == 'initd_status':
|
||||
print(initdStatus())
|
||||
elif func == 'initd_install':
|
||||
print(initdInstall())
|
||||
elif func == 'initd_uninstall':
|
||||
print(initdUinstall())
|
||||
elif func == 'conf':
|
||||
print(getConf())
|
||||
elif func == 'run_log':
|
||||
print(runLog())
|
||||
elif func == 'config_tpl':
|
||||
print(configTpl())
|
||||
elif func == 'read_config_tpl':
|
||||
print(readConfigTpl())
|
||||
elif func == 'clear_copy_task':
|
||||
print(clearCopyTask())
|
||||
elif func == 'home_page':
|
||||
print(homePage())
|
||||
else:
|
||||
print('error')
|
||||
@@ -1,17 +0,0 @@
|
||||
{
|
||||
"sort": 7,
|
||||
"ps": "一个支持多种存储的文件列表程序",
|
||||
"name": "alist",
|
||||
"title": "Alist",
|
||||
"shell": "install.sh",
|
||||
"versions":["3.37.4","3.38.0"],
|
||||
"tip": "soft",
|
||||
"checks": "server/alist",
|
||||
"path": "server/alist",
|
||||
"display": 1,
|
||||
"author": "alist",
|
||||
"date": "2022-10-09",
|
||||
"home": "https://alist.nn.ci",
|
||||
"type": 0,
|
||||
"pid": "5"
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
[Unit]
|
||||
Description=A file list program that supports multiple storage
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
WorkingDirectory={$SERVER_PATH}/alist
|
||||
ExecStart={$SERVER_PATH}/alist/alist server
|
||||
ExecReload=/bin/kill -USR2 $MAINPID
|
||||
Restart=on-failure
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
@@ -1,46 +0,0 @@
|
||||
#!/bin/sh
|
||||
# chkconfig: 2345 55 25
|
||||
# description: alist Service
|
||||
|
||||
### BEGIN INIT INFO
|
||||
# Provides: alist
|
||||
# Required-Start: $all
|
||||
# Required-Stop: $all
|
||||
# Default-Start: 2 3 4 5
|
||||
# Default-Stop: 0 1 6
|
||||
# Short-Description: starts alist
|
||||
# Description: starts the MDW-Web
|
||||
### END INIT INFO
|
||||
|
||||
# Simple alist init.d script conceived to work on Linux systems
|
||||
# as it does use of the /proc filesystem.
|
||||
|
||||
app_start(){
|
||||
echo "Starting alist server..."
|
||||
cd {$SERVER_PATH}/alist
|
||||
./alist server >> {$SERVER_PATH}/alist/logs.pl 2>&1 &
|
||||
}
|
||||
|
||||
app_stop(){
|
||||
echo "dztasks stopped"
|
||||
ps -ef| grep alist | grep -v grep | grep -v python | grep -v sh | awk '{print $2}'| xargs kill
|
||||
}
|
||||
|
||||
|
||||
case "$1" in
|
||||
start)
|
||||
app_start
|
||||
;;
|
||||
stop)
|
||||
app_stop
|
||||
;;
|
||||
restart|reload)
|
||||
app_stop
|
||||
sleep 0.3
|
||||
app_start
|
||||
;;
|
||||
*)
|
||||
echo "Please use start or stop as first argument"
|
||||
;;
|
||||
esac
|
||||
|
||||
@@ -1,93 +0,0 @@
|
||||
#!/bin/bash
|
||||
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin:/opt/homebrew/bin
|
||||
export PATH
|
||||
|
||||
curPath=`pwd`
|
||||
rootPath=$(dirname "$curPath")
|
||||
rootPath=$(dirname "$rootPath")
|
||||
serverPath=$(dirname "$rootPath")
|
||||
|
||||
# cd /Users/midoks/Desktop/mwdev/server/mdserver-web/plugins/alist && bash install.sh install 3.37.4
|
||||
# cd /www/server/mdserver-web/plugins/alist && bash install.sh install 3.37.4
|
||||
|
||||
VERSION=$2
|
||||
|
||||
sysArch=`arch`
|
||||
sysName=`uname`
|
||||
|
||||
ALIST_ARCH_NAME=amd64
|
||||
if [ "$sysArch" == "arm64" ];then
|
||||
ALIST_ARCH_NAME=arm64
|
||||
elif [ "$sysArch" == "x86_64" ]; then
|
||||
ALIST_ARCH_NAME=amd64
|
||||
elif [ "$sysArch" == "aarch64" ]; then
|
||||
ALIST_ARCH_NAME=arm64
|
||||
fi
|
||||
|
||||
ALIST_NAME=linux
|
||||
if [ "$sysName" == "Darwin" ];then
|
||||
ALIST_NAME=darwin
|
||||
fi
|
||||
|
||||
Install_App()
|
||||
{
|
||||
echo '正在安装脚本文件...'
|
||||
|
||||
mkdir -p $serverPath/source
|
||||
mkdir -p $serverPath/source/alist
|
||||
|
||||
FILE_TGZ=alist-${ALIST_NAME}-${ALIST_ARCH_NAME}.tar.gz
|
||||
|
||||
ALIST_DIR=$serverPath/source/alist
|
||||
|
||||
if [ ! -f $ALIST_DIR/${FILE_TGZ} ];then
|
||||
wget -O $ALIST_DIR/${FILE_TGZ} https://github.com/alist-org/alist/releases/download/v${VERSION}/${FILE_TGZ}
|
||||
fi
|
||||
|
||||
mkdir -p $serverPath/alist
|
||||
|
||||
cd $ALIST_DIR && tar -zxvf ${FILE_TGZ} -C $serverPath/alist
|
||||
echo "${VERSION}" > $serverPath/alist/version.pl
|
||||
|
||||
cd ${rootPath} && python3 ${rootPath}/plugins/alist/index.py start
|
||||
cd ${rootPath} && python3 ${rootPath}/plugins/alist/index.py initd_install
|
||||
|
||||
cd $serverPath/alist && ./alist admin set admin
|
||||
# cd /www/server/alist && ./alist admin set admin
|
||||
|
||||
echo '安装完成'
|
||||
}
|
||||
|
||||
Uninstall_App()
|
||||
{
|
||||
if [ -f /usr/lib/systemd/system/alist.service ];then
|
||||
systemctl stop alist
|
||||
systemctl disable alist
|
||||
rm -rf /usr/lib/systemd/system/alist.service
|
||||
systemctl daemon-reload
|
||||
fi
|
||||
|
||||
if [ -f /lib/systemd/system/alist.service ];then
|
||||
systemctl stop alist
|
||||
systemctl disable alist
|
||||
rm -rf /lib/systemd/system/alist.service
|
||||
systemctl daemon-reload
|
||||
fi
|
||||
|
||||
if [ -f $serverPath/alist/initd/alist ];then
|
||||
$serverPath/alist/initd/alist stop
|
||||
fi
|
||||
|
||||
if [ -d $serverPath/alist ];then
|
||||
rm -rf $serverPath/alist
|
||||
fi
|
||||
|
||||
echo "卸载alist成功"
|
||||
}
|
||||
|
||||
action=$1
|
||||
if [ "${1}" == 'install' ];then
|
||||
Install_App
|
||||
else
|
||||
Uninstall_App
|
||||
fi
|
||||
@@ -1,93 +0,0 @@
|
||||
function alistPost(method, version, args,callback){
|
||||
var loadT = layer.msg('正在获取...', { icon: 16, time: 0, shade: 0.3 });
|
||||
|
||||
var req_data = {};
|
||||
req_data['name'] = 'alist';
|
||||
req_data['func'] = method;
|
||||
req_data['version'] = version;
|
||||
|
||||
if (typeof(args) == 'string'){
|
||||
req_data['args'] = JSON.stringify(toArrayObject(args));
|
||||
} else {
|
||||
req_data['args'] = JSON.stringify(args);
|
||||
}
|
||||
|
||||
$.post('/plugins/run', req_data, function(data) {
|
||||
layer.close(loadT);
|
||||
if (!data.status){
|
||||
//错误展示10S
|
||||
layer.msg(data.msg,{icon:0,time:2000,shade: [10, '#000']});
|
||||
return;
|
||||
}
|
||||
|
||||
if(typeof(callback) == 'function'){
|
||||
callback(data);
|
||||
}
|
||||
},'json');
|
||||
}
|
||||
|
||||
function alistPostCallbak(method, version, args,callback){
|
||||
var loadT = layer.msg('正在获取...', { icon: 16, time: 0, shade: 0.3 });
|
||||
|
||||
var req_data = {};
|
||||
req_data['name'] = 'alist';
|
||||
req_data['func'] = method;
|
||||
args['version'] = version;
|
||||
|
||||
if (typeof(args) == 'string'){
|
||||
req_data['args'] = JSON.stringify(toArrayObject(args));
|
||||
} else {
|
||||
req_data['args'] = JSON.stringify(args);
|
||||
}
|
||||
|
||||
$.post('/plugins/callback', req_data, function(data) {
|
||||
layer.close(loadT);
|
||||
if (!data.status){
|
||||
layer.msg(data.msg,{icon:0,time:2000,shade: [0.3, '#000']});
|
||||
return;
|
||||
}
|
||||
|
||||
if(typeof(callback) == 'function'){
|
||||
callback(data);
|
||||
}
|
||||
},'json');
|
||||
}
|
||||
|
||||
function clearTaskCopy(){
|
||||
layer.confirm('您真的要清空复制任务吗?', { icon: 3, closeBtn: 2 }, function() {
|
||||
alistPost('clear_copy_task', '', {}, function(data){
|
||||
var rdata = $.parseJSON(data.data);
|
||||
showMsg(rdata.msg, function(){},{ icon: rdata.status ? 1 : 2 });
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function commonHomePage(){
|
||||
alistPost('home_page', '', {}, function(data){
|
||||
var rdata = $.parseJSON(data.data);
|
||||
window.open(rdata.data);
|
||||
});
|
||||
}
|
||||
|
||||
function alistCommonFunc(){
|
||||
var con = '';
|
||||
con += '<hr/><p class="conf_p" style="text-align:center;">\
|
||||
<button class="btn btn-default btn-sm" onclick="commonHomePage()">主页</button>\
|
||||
<button class="btn btn-default btn-sm" onclick="clearTaskCopy()">清空复制任务</button>\
|
||||
</p>';
|
||||
|
||||
$(".soft-man-con").html(con);
|
||||
}
|
||||
|
||||
function alistReadme(){
|
||||
|
||||
|
||||
var readme = '<ul class="help-info-text c7">';
|
||||
readme += '<li>手动开启默认端口:5244</li>';
|
||||
readme += '<li>默认admin:admin</li>';
|
||||
readme += '<li>手动改密码: cd /www/server/alist && ./alist admin set NEW_PASSWORD</li>';
|
||||
readme += '</ul>';
|
||||
|
||||
$('.soft-man-con').html(readme);
|
||||
}
|
||||
|
||||
@@ -1,424 +0,0 @@
|
||||
# coding:utf-8
|
||||
|
||||
'''
|
||||
doc: https://docs.python.org/zh-cn/3/library/ftplib.html
|
||||
'''
|
||||
|
||||
import sys
|
||||
import io
|
||||
import os
|
||||
import time
|
||||
import re
|
||||
import json
|
||||
|
||||
import paramiko
|
||||
import ftplib
|
||||
|
||||
web_dir = os.getcwd() + "/web"
|
||||
if os.path.exists(web_dir):
|
||||
sys.path.append(web_dir)
|
||||
os.chdir(web_dir)
|
||||
|
||||
import core.mw as mw
|
||||
|
||||
DEBUG = True
|
||||
BLOCK_SIZE = 1024 * 1024 * 2
|
||||
# BLOCK_SIZE = 50
|
||||
PROGRESS_FILE_NAME = "PROGRESS_FILE_NAME"
|
||||
|
||||
"""
|
||||
=============自定义异常===================
|
||||
"""
|
||||
|
||||
|
||||
class OsError(Exception):
|
||||
"""OS端异常"""
|
||||
|
||||
|
||||
class ObjectNotFound(OsError):
|
||||
"""对象不存在时抛出的异常"""
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
message = "文件对象不存在。"
|
||||
super(ObjectNotFound, self).__init__(message, *args, **kwargs)
|
||||
|
||||
|
||||
class APIError(Exception):
|
||||
"""API参数错误异常"""
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
_api_error_msg = 'API资料校验失败,请核实!'
|
||||
super(APIError, self).__init__(_api_error_msg, *args, **kwargs)
|
||||
|
||||
|
||||
class FtpPSClient:
|
||||
_title = "FTP"
|
||||
_name = "ftp"
|
||||
__host = None
|
||||
__port = None
|
||||
__user = None
|
||||
__password = None
|
||||
default_port = 21
|
||||
default_backup_path = "/backup/"
|
||||
config_file = "cfg.json"
|
||||
|
||||
def __init__(self, load_config=True, timeout=600):
|
||||
self.timeout = timeout
|
||||
if load_config:
|
||||
data = self.get_config()
|
||||
self.injection_config(data)
|
||||
|
||||
def get_config(self):
|
||||
default_config = {
|
||||
"ftp_host": '',
|
||||
"ftp_user": '',
|
||||
"ftp_pass": '',
|
||||
"backup_path": self.default_backup_path
|
||||
}
|
||||
|
||||
cfg = mw.getServerDir() + "/backup_ftp/" + self.config_file
|
||||
if os.path.exists(cfg):
|
||||
data = mw.readFile(cfg)
|
||||
return json.loads(data)
|
||||
else:
|
||||
return default_config
|
||||
|
||||
def injection_config(self, data):
|
||||
self.__host = host = data["ftp_host"].strip()
|
||||
if host.find(':') == -1:
|
||||
self.__port = self.default_port
|
||||
else:
|
||||
self.__host = host.split(':')[0]
|
||||
self.__port = host.split(':')[1]
|
||||
|
||||
self.__user = data['ftp_user'].strip()
|
||||
self.__password = data['ftp_pass'].strip()
|
||||
bp = data['backup_path'].strip()
|
||||
if bp:
|
||||
self.backup_path = self.getPath(bp)
|
||||
else:
|
||||
self.backup_path = self.getPath(self.default_backup_path)
|
||||
|
||||
def authorize(self):
|
||||
try:
|
||||
if self.timeout is not None:
|
||||
ftp = ftplib.FTP(timeout=self.timeout)
|
||||
else:
|
||||
ftp = ftplib.FTP()
|
||||
|
||||
debuglevel = 0
|
||||
# if DEBUG:
|
||||
# debuglevel = 3
|
||||
ftp.set_debuglevel(debuglevel)
|
||||
# ftp.set_pasv(True)
|
||||
ftp.connect(self.__host, int(self.__port))
|
||||
ftp.login(self.__user, self.__password)
|
||||
return ftp
|
||||
except Exception as e:
|
||||
raise OsError("无法连接FTP客户端,请检查配置参数是否正确!")
|
||||
|
||||
# 取目录路径
|
||||
def getPath(self, path):
|
||||
if path[-1:] != '/':
|
||||
path += '/'
|
||||
if path[:1] != '/':
|
||||
path = '/' + path
|
||||
return path.replace('//', '/')
|
||||
|
||||
def generateDownloadUrl(self, object_name):
|
||||
|
||||
return 'ftp://' + \
|
||||
self.__user + ':' + \
|
||||
self.__password + '@' + \
|
||||
self.__host + ':' + \
|
||||
"/" + object_name
|
||||
|
||||
def buildDirName(self, data_type, file_name):
|
||||
import re
|
||||
prefix_dict = {
|
||||
"site": "web",
|
||||
"database": "db",
|
||||
"path": "path",
|
||||
}
|
||||
file_regx = prefix_dict.get(data_type) + r"_(.+)_20\d+_\d+\."
|
||||
sub_search = re.search(file_regx, file_name)
|
||||
sub_path_name = ""
|
||||
if sub_search:
|
||||
sub_path_name = sub_search.groups()[0]
|
||||
sub_path_name += '/'
|
||||
|
||||
# 构建OS存储路径
|
||||
object_name = self.backup_path + \
|
||||
data_type + '/' + \
|
||||
sub_path_name + \
|
||||
file_name
|
||||
return object_name
|
||||
|
||||
def uploadFile(self, filename, data_type=None, *args, **kwargs):
|
||||
|
||||
client = self.authorize()
|
||||
|
||||
local_file_name = filename
|
||||
filename = os.path.abspath(filename)
|
||||
dirname = os.path.dirname(filename)
|
||||
temp_name = os.path.split(filename)[1]
|
||||
|
||||
object_name = self.buildDirName(data_type, temp_name)
|
||||
|
||||
upload_tmp_dir = os.path.join(dirname, ".upload_tmp")
|
||||
if not os.path.exists(upload_tmp_dir):
|
||||
os.mkdir(upload_tmp_dir)
|
||||
|
||||
print("|-正在上传文件到 {}".format(object_name))
|
||||
|
||||
total_bytes = os.path.getsize(filename)
|
||||
object_md5_name = mw.md5(object_name)
|
||||
pg_file = os.path.join(upload_tmp_dir, object_md5_name + ".pl")
|
||||
|
||||
block_size = BLOCK_SIZE
|
||||
if kwargs.get("block_size"):
|
||||
try:
|
||||
block_size = float(kwargs.get("block_size"))
|
||||
except:
|
||||
pass
|
||||
|
||||
remote_file_size = None
|
||||
if not os.path.exists(pg_file):
|
||||
# import uuid
|
||||
# uid = str(uuid.uuid1())
|
||||
progress_info = {
|
||||
"filename": local_file_name,
|
||||
"total_bytes": total_bytes,
|
||||
"uploaded_bytes": 0,
|
||||
}
|
||||
mw.writeFile(pg_file, json.dumps(progress_info))
|
||||
else:
|
||||
progress_info = json.loads(mw.readFile(pg_file))
|
||||
if total_bytes == progress_info.get("total_bytes"):
|
||||
# 取远程文件大小
|
||||
_max_loop = 10
|
||||
while _max_loop > 0:
|
||||
try:
|
||||
time.sleep(1)
|
||||
remote_file_size = client.size(object_name)
|
||||
if remote_file_size > total_bytes:
|
||||
remote_file_size = None
|
||||
break
|
||||
except Exception as e:
|
||||
if DEBUG:
|
||||
print(type(e))
|
||||
print(e)
|
||||
_max_loop -= 1
|
||||
else:
|
||||
remote_file_size = None
|
||||
|
||||
uploaded_bytes = 0 if remote_file_size is None else remote_file_size
|
||||
|
||||
dir_name = os.path.split(object_name)[0]
|
||||
if dir_name:
|
||||
self.createDirP(dir_name)
|
||||
|
||||
upload_start = time.time()
|
||||
|
||||
try:
|
||||
if total_bytes > 1024 * 1024 * 1024:
|
||||
with open(local_file_name, 'rb') as file_handler:
|
||||
if remote_file_size is not None:
|
||||
file_handler.seek(remote_file_size)
|
||||
|
||||
client.voidcmd("TYPE I")
|
||||
datasock = ''
|
||||
esize = ''
|
||||
|
||||
datasock, esize = client.ntransfercmd(
|
||||
"STOR " + object_name, remote_file_size)
|
||||
|
||||
while True:
|
||||
buf = file_handler.read(block_size)
|
||||
if not len(buf):
|
||||
break
|
||||
datasock.sendall(buf)
|
||||
uploaded_bytes += len(buf)
|
||||
if DEBUG:
|
||||
print('\ruploading %.2f%%' %
|
||||
(float(uploaded_bytes) / total_bytes * 100))
|
||||
|
||||
print("uploaded_bytes", uploaded_bytes)
|
||||
if uploaded_bytes == total_bytes:
|
||||
break
|
||||
datasock.close()
|
||||
|
||||
if DEBUG:
|
||||
print('close data handle')
|
||||
try:
|
||||
client.voidcmd('NOOP')
|
||||
except Exception as e:
|
||||
if DEBUG:
|
||||
print("Send NOOP command error:")
|
||||
print(e)
|
||||
else:
|
||||
if DEBUG:
|
||||
print('keep alive cmd success')
|
||||
client.voidresp()
|
||||
if DEBUG:
|
||||
print('No loop cmd')
|
||||
else:
|
||||
# 小于1G文件直接上传
|
||||
file_handler = open(local_file_name, "rb")
|
||||
client.storbinary('STOR %s' % object_name,
|
||||
file_handler, blocksize=block_size)
|
||||
file_handler.close()
|
||||
except Exception as e:
|
||||
print(str(e))
|
||||
|
||||
completed_file_size = None
|
||||
_max_loop = 10
|
||||
while _max_loop > 0:
|
||||
try:
|
||||
time.sleep(1)
|
||||
completed_file_size = client.size(object_name)
|
||||
break
|
||||
except Exception as e:
|
||||
_max_loop -= 1
|
||||
if DEBUG:
|
||||
print("size error:" + str(e))
|
||||
|
||||
# 上传完成
|
||||
if completed_file_size == total_bytes:
|
||||
if DEBUG:
|
||||
upload_completed = time.time()
|
||||
upload_diff = upload_completed - upload_start
|
||||
print("文件上传成功, 耗时: {}s。".format(upload_diff))
|
||||
if os.path.exists(pg_file):
|
||||
os.remove(pg_file)
|
||||
return True
|
||||
else:
|
||||
if os.path.exists(pg_file):
|
||||
os.remove(pg_file)
|
||||
print("文件上传后大小不一致!")
|
||||
|
||||
print("completed_file_size:" + str(completed_file_size))
|
||||
print("total_bytes:", total_bytes)
|
||||
print("object_md5_name:", object_md5_name)
|
||||
print("pg_file:", pg_file)
|
||||
print("filename:", filename)
|
||||
print("dirname:", dirname)
|
||||
print("object_name:", object_name)
|
||||
return False
|
||||
|
||||
def createDirP(self, dir_name):
|
||||
"""创建远程目录
|
||||
|
||||
:param dir_name: 目录名称
|
||||
:return:
|
||||
"""
|
||||
try:
|
||||
dirnames = dir_name.split('/')
|
||||
ftp = self.authorize()
|
||||
# ftp.cwd(get.path);
|
||||
for dirname in dirnames:
|
||||
if not dirname or not dirname.strip():
|
||||
continue
|
||||
try:
|
||||
flist = ftp.nlst()
|
||||
if not dirname in flist:
|
||||
ftp.mkd(dirname)
|
||||
except:
|
||||
# print("mlsd mode.")
|
||||
try:
|
||||
flist = list(ftp.mlsd())[1:]
|
||||
for f in flist:
|
||||
if dirname == f[0]:
|
||||
break
|
||||
else:
|
||||
ftp.mkd(dirname)
|
||||
except:
|
||||
return False
|
||||
ftp.cwd(dirname)
|
||||
return True
|
||||
except:
|
||||
return False
|
||||
|
||||
def createDir(self, path, name):
|
||||
ftp = self.authorize()
|
||||
path = self.getPath(path)
|
||||
ftp.cwd(path)
|
||||
try:
|
||||
ftp.mkd(name)
|
||||
ftp.close()
|
||||
return True
|
||||
except Exception as e:
|
||||
print(str(e))
|
||||
ftp.close()
|
||||
return False
|
||||
|
||||
def deleteDir(self, path, dir_name):
|
||||
try:
|
||||
ftp = self.authorize()
|
||||
ftp.rmd(dir_name)
|
||||
return True
|
||||
except ftplib.error_perm as e:
|
||||
print("deleteDir:" + str(e) + ":" + dir_name)
|
||||
except Exception as e:
|
||||
print(e)
|
||||
return False
|
||||
|
||||
def deleteFile(self, filename):
|
||||
try:
|
||||
ftp = self.authorize()
|
||||
ftp.delete(filename)
|
||||
return True
|
||||
except Exception as e:
|
||||
print(str(e))
|
||||
return False
|
||||
|
||||
def getList(self, path="/"):
|
||||
ftp = self.authorize()
|
||||
path = self.getPath(path)
|
||||
ftp.cwd(path)
|
||||
mlsd = False
|
||||
try:
|
||||
files = list(ftp.mlsd())
|
||||
mlsd = True
|
||||
except:
|
||||
try:
|
||||
files = ftp.nlst(path)
|
||||
mlsd = False
|
||||
except:
|
||||
raise RuntimeError("FTP服务器数据返回异常!")
|
||||
ftp.close()
|
||||
# print(files)
|
||||
f_list = []
|
||||
dirs = []
|
||||
data = []
|
||||
default_time = '1971/01/01 01:01:01'
|
||||
for dt in files:
|
||||
# print(dt)
|
||||
if mlsd:
|
||||
dt_name = dt[0]
|
||||
dt_info = dt[1]
|
||||
else:
|
||||
if dt.find("/") >= 0:
|
||||
dt = dt.split("/")[-1]
|
||||
tmp = {}
|
||||
tmp['name'] = dt_name
|
||||
if dt_name == '.' or dt_name == '..':
|
||||
continue
|
||||
|
||||
tmp['time'] = dt_info['modify']
|
||||
try:
|
||||
tmp['size'] = dt_info['size']
|
||||
tmp['type'] = "File"
|
||||
tmp['download'] = self.generateDownloadUrl(path + dt_name)
|
||||
f_list.append(tmp)
|
||||
except:
|
||||
tmp['size'] = dt_info['sizd']
|
||||
tmp['type'] = None
|
||||
tmp['download'] = ''
|
||||
dirs.append(tmp)
|
||||
data = dirs + f_list
|
||||
|
||||
mlist = {}
|
||||
mlist['path'] = path
|
||||
mlist['list'] = data
|
||||
return mlist
|
||||
|
Before Width: | Height: | Size: 1.5 KiB |
@@ -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>
|
||||
@@ -1,318 +0,0 @@
|
||||
# coding:utf-8
|
||||
|
||||
import sys
|
||||
import io
|
||||
import os
|
||||
import time
|
||||
import re
|
||||
import json
|
||||
|
||||
# print(sys.platform)
|
||||
if sys.platform != "darwin":
|
||||
os.chdir("/www/server/mdserver-web")
|
||||
|
||||
|
||||
web_dir = os.getcwd() + "/web"
|
||||
if os.path.exists(web_dir):
|
||||
sys.path.append(web_dir)
|
||||
os.chdir(web_dir)
|
||||
|
||||
import core.mw as mw
|
||||
import core.db as db
|
||||
|
||||
_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 findPathName(path, filename):
|
||||
f = os.scandir(path)
|
||||
l = []
|
||||
for ff in f:
|
||||
t = {}
|
||||
if ff.name.find(filename) > -1:
|
||||
t['filename'] = path + '/' + ff.name
|
||||
l.append(t)
|
||||
return l
|
||||
|
||||
def backupAllFunc(stype):
|
||||
|
||||
name = sys.argv[2]
|
||||
num = sys.argv[3]
|
||||
|
||||
prefix_dict = {
|
||||
"site": "web",
|
||||
"database": "db",
|
||||
"path": "path",
|
||||
}
|
||||
|
||||
backups = []
|
||||
# print("stype:", stype)
|
||||
# 提前获取-清理多余备份
|
||||
if stype == 'site':
|
||||
pid = mw.M('sites').where('name=?', (name,)).getField('id')
|
||||
backups = mw.M('backup').where('type=? and pid=?', ('0', pid)).field('id,filename').select()
|
||||
if stype == 'database':
|
||||
db_path = mw.getServerDir() + '/mysql'
|
||||
pid = mw.M('databases').dbPos(db_path, 'mysql').where('name=?', (name,)).getField('id')
|
||||
backups = mw.M('backup').where('type=? and pid=?', ('1', pid)).field('id,filename').select()
|
||||
if stype == 'path':
|
||||
backup_dir = mw.getBackupDir()
|
||||
backup_path = backup_dir + '/path'
|
||||
_name = 'path_{}'.format(os.path.basename(name))
|
||||
backups = findPathName(backup_path, _name)
|
||||
|
||||
# 其他类型关系性数据库(mysql类的)
|
||||
if stype.find('database_') > -1:
|
||||
plugin_name = stype.replace('database_', '')
|
||||
db_path = mw.getServerDir() + '/' + plugin_name
|
||||
pid = mw.M('databases').dbPos(db_path, 'mysql').where('name=?', (name,)).getField('id')
|
||||
backups = mw.M('backup').where('type=? and pid=?', ('1', pid)).field('id,filename').select()
|
||||
|
||||
args = stype + " " + name + " " + num
|
||||
cmd = 'python3 ' + mw.getPanelDir() + '/scripts/backup.py ' + args
|
||||
if stype.find('database_') > -1:
|
||||
plugin_name = stype.replace('database_', '')
|
||||
args = "database " + name + " " + num
|
||||
cmd = 'python3 ' + mw.getPanelDir() + '/plugins/' + plugin_name + '/scripts/backup.py ' + args
|
||||
|
||||
os.system(cmd)
|
||||
|
||||
# 开始执行上传信息.
|
||||
|
||||
if stype.find('database_') > -1:
|
||||
bk_name = 'database'
|
||||
plugin_name = stype.replace('database_', '')
|
||||
bk_prefix = plugin_name + '/db'
|
||||
stype = 'database'
|
||||
else:
|
||||
bk_prefix = prefix_dict[stype]
|
||||
bk_name = stype
|
||||
|
||||
find_path = mw.getBackupDir() + '/' + bk_name + '/' + bk_prefix + '_' + name
|
||||
if stype == 'path':
|
||||
_name = 'path_{}'.format(os.path.basename(name))
|
||||
find_path = mw.getBackupDir() + '/path/'+_name
|
||||
|
||||
find_new_file = "ls " + find_path + "_* | grep '.gz' | cut -d \\ -f 1 | awk 'END {print}'"
|
||||
|
||||
filename = mw.execShell(find_new_file)[0].strip()
|
||||
if filename == "":
|
||||
mw.echoInfo("not find upload file!")
|
||||
return ''
|
||||
|
||||
mw.echoInfo("准备上传文件 {}".format(filename))
|
||||
ftp = FtpPSClient()
|
||||
ftp.uploadFile(filename, stype)
|
||||
|
||||
# print(backups)
|
||||
backups = sorted(backups, key=lambda x: x['filename'], reverse=False)
|
||||
mw.echoStart('开始删除远程备份')
|
||||
num = int(num)
|
||||
sep = len(backups) - num
|
||||
if sep > -1:
|
||||
for backup in backups:
|
||||
fn = os.path.basename(backup['filename'])
|
||||
object_name = ftp.buildDirName(stype, fn)
|
||||
ftp.deleteFile(object_name)
|
||||
mw.echoInfo("已清理远程过期备份文件:" + object_name)
|
||||
sep -= 1
|
||||
if sep < 0:
|
||||
break
|
||||
mw.echoEnd('结束删除远程备份')
|
||||
|
||||
return ''
|
||||
|
||||
|
||||
def in_array(name, arr=[]):
|
||||
for x in arr:
|
||||
if name == x:
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def installPreInspection():
|
||||
return 'ok'
|
||||
|
||||
if __name__ == "__main__":
|
||||
func = sys.argv[1]
|
||||
if func == 'status':
|
||||
print(status())
|
||||
elif func == 'start':
|
||||
print(start())
|
||||
elif func == 'stop':
|
||||
print(stop())
|
||||
elif func == 'restart':
|
||||
print(restart())
|
||||
elif func == 'reload':
|
||||
print(reload())
|
||||
elif func == 'install_pre_inspection':
|
||||
print(installPreInspection())
|
||||
elif func == 'conf':
|
||||
print(getConf())
|
||||
elif func == 'set_config':
|
||||
print(setConf())
|
||||
elif func == "get_list":
|
||||
print(getList())
|
||||
elif func == "create_dir":
|
||||
print(createDir())
|
||||
elif func == "delete_dir":
|
||||
print(deleteDir())
|
||||
elif func == 'delete_file':
|
||||
print(deleteFile())
|
||||
elif in_array(func, ['site', 'database', 'path']) or func.find('database_') > -1:
|
||||
print(backupAllFunc(func))
|
||||
else:
|
||||
print('error')
|
||||
@@ -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"
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
#!/bin/bash
|
||||
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
|
||||
export PATH
|
||||
|
||||
curPath=`pwd`
|
||||
rootPath=$(dirname "$curPath")
|
||||
rootPath=$(dirname "$rootPath")
|
||||
serverPath=$(dirname "$rootPath")
|
||||
|
||||
sys_os=`uname`
|
||||
|
||||
|
||||
Install_App()
|
||||
{
|
||||
mkdir -p ${serverPath}/backup_ftp
|
||||
echo "${1}" > ${serverPath}/backup_ftp/version.pl
|
||||
echo '安装完成'
|
||||
|
||||
}
|
||||
|
||||
Uninstall_App()
|
||||
{
|
||||
rm -rf ${serverPath}/backup_ftp
|
||||
}
|
||||
|
||||
action=$1
|
||||
if [ "${1}" == 'install' ];then
|
||||
Install_App $2
|
||||
else
|
||||
Uninstall_App $2
|
||||
fi
|
||||
@@ -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');
|
||||
|
Before Width: | Height: | Size: 254 B |
@@ -1,74 +0,0 @@
|
||||
<div class="bt-form">
|
||||
<div class="bt-w-main">
|
||||
<div class="bt-w-menu">
|
||||
<p class="bgw" onclick="pluginService('clean');">服务</p>
|
||||
<p onclick="pluginConfig('clean');">配置修改</p>
|
||||
<p onclick="commonFunc();">常用功能</p>
|
||||
<p onclick="pluginLogs('clean',null,'run_log');">运行日志</p>
|
||||
<p onclick="pRead()">说明</p>
|
||||
</div>
|
||||
<div class="bt-w-con pd15">
|
||||
<div class="soft-man-con"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script type="text/javascript">
|
||||
|
||||
pluginService('clean');
|
||||
|
||||
function cleanPost(method, version, args, callback){
|
||||
var loadT = layer.msg('正在获取...', { icon: 16, time: 0, shade: 0.3 });
|
||||
|
||||
var req_data = {};
|
||||
req_data['name'] = 'clean';
|
||||
req_data['func'] = method;
|
||||
req_data['version'] = version;
|
||||
|
||||
if (typeof(args) == 'string'){
|
||||
req_data['args'] = JSON.stringify(toArrayObject(args));
|
||||
} else {
|
||||
req_data['args'] = JSON.stringify(args);
|
||||
}
|
||||
|
||||
$.post('/plugins/run', req_data, function(data) {
|
||||
layer.close(loadT);
|
||||
if (!data.status){
|
||||
layer.msg(data.msg,{icon:0,time:2000,shade: [10, '#000']});
|
||||
return;
|
||||
}
|
||||
|
||||
if(typeof(callback) == 'function'){
|
||||
callback(data);
|
||||
}
|
||||
},'json');
|
||||
}
|
||||
|
||||
function cleanRun(){
|
||||
cleanPost('clean_run', '', {}, function(rdata){
|
||||
// var rdata = $.parseJSON(rdata.data);
|
||||
// layer.msg(rdata.msg, { icon: rdata.status ? 1 : 2 });
|
||||
});
|
||||
|
||||
layer.msg("执行成功!", { icon: 1});
|
||||
}
|
||||
|
||||
function commonFunc(){
|
||||
|
||||
con = '<hr/><p class="conf_p" style="text-align:center;">\
|
||||
<button class="btn btn-default btn-sm" onclick="cleanRun()">手动执行</button> \
|
||||
</p>';
|
||||
|
||||
$(".soft-man-con").html(con);
|
||||
}
|
||||
|
||||
function pRead(){
|
||||
var readme = '<ul class="help-info-text c7">';
|
||||
readme += '<li>启动后,加入到计划任务每隔7天[0:15]执行。</li>';
|
||||
readme += '<li>【*】代表要删除。</li>';
|
||||
readme += '<li>添加/删除清除日志,可在`配置修改`中添加/删除。</li>';
|
||||
readme += '<li>如果是目录,会寻找【.log】后缀的文件清空。</li>';
|
||||
readme += '</ul>';
|
||||
|
||||
$('.soft-man-con').html(readme);
|
||||
}
|
||||
</script>
|
||||
@@ -1,294 +0,0 @@
|
||||
# coding:utf-8
|
||||
|
||||
import sys
|
||||
import io
|
||||
import os
|
||||
import time
|
||||
|
||||
web_dir = os.getcwd() + "/web"
|
||||
if os.path.exists(web_dir):
|
||||
sys.path.append(web_dir)
|
||||
os.chdir(web_dir)
|
||||
|
||||
import core.mw as mw
|
||||
|
||||
app_debug = False
|
||||
if mw.isAppleSystem():
|
||||
app_debug = True
|
||||
|
||||
|
||||
def getPluginName():
|
||||
return 'clean'
|
||||
|
||||
|
||||
def getPluginDir():
|
||||
return mw.getPluginDir() + '/' + getPluginName()
|
||||
|
||||
|
||||
def getServerDir():
|
||||
return mw.getServerDir() + '/' + getPluginName()
|
||||
|
||||
|
||||
def getInitDFile():
|
||||
if app_debug:
|
||||
return '/tmp/' + getPluginName()
|
||||
return '/etc/init.d/' + getPluginName()
|
||||
|
||||
|
||||
def getConf():
|
||||
path = getServerDir() + "/clean.conf"
|
||||
return path
|
||||
|
||||
|
||||
def getArgs():
|
||||
args = sys.argv[2:]
|
||||
tmp = {}
|
||||
args_len = len(args)
|
||||
|
||||
if args_len == 1:
|
||||
t = args[0].strip('{').strip('}')
|
||||
t = t.split(':')
|
||||
tmp[t[0]] = t[1]
|
||||
elif args_len > 1:
|
||||
for i in range(len(args)):
|
||||
t = args[i].split(':')
|
||||
tmp[t[0]] = t[1]
|
||||
|
||||
return tmp
|
||||
|
||||
|
||||
def getLockFile():
|
||||
return getServerDir() + "/installed_lock.pl"
|
||||
|
||||
|
||||
def runLog():
|
||||
return getServerDir() + "/clean.log"
|
||||
|
||||
|
||||
def status():
|
||||
initConf()
|
||||
if os.path.exists(getLockFile()):
|
||||
return "start"
|
||||
return 'stop'
|
||||
|
||||
|
||||
def initConf():
|
||||
conf = getConf()
|
||||
if not os.path.exists(conf):
|
||||
content = ""
|
||||
|
||||
clog = [
|
||||
"/var/log/cron-*",
|
||||
"/var/log/maillog-*",
|
||||
"/var/log/secure-*",
|
||||
"/var/log/spooler-*",
|
||||
"/var/log/yum.log-*",
|
||||
"/var/log/messages-*",
|
||||
"/var/log/btmp-*",
|
||||
"/var/log/auth.*",
|
||||
"/var/log/messages.*",
|
||||
"/var/log/debug.*",
|
||||
"/var/log/syslog.*",
|
||||
"/var/log/btmp.*",
|
||||
"/var/log/sa/sa*",
|
||||
"/var/log/sysstat/sa*",
|
||||
"/var/log/atop/atop*",
|
||||
"/var/log/anaconda/*.log",
|
||||
|
||||
"/var/log/dpkg.log.*",
|
||||
"/var/log/alternatives.log.*",
|
||||
"/var/log/user.log.*",
|
||||
"/var/log/kern.log.*",
|
||||
"/var/log/daemon.log.*",
|
||||
|
||||
"/var/log/*.gz",
|
||||
"/var/log/*.xz",
|
||||
"/var/log/*.log.*",
|
||||
|
||||
"/var/log/audit/audit.log.*",
|
||||
"/var/log/hawkey.log-*",
|
||||
"/var/log/apt/*.gz",
|
||||
"/var/log/apt/*.xz",
|
||||
"/var/log/rhsm/rhsm.log-*",
|
||||
"/var/log/rhsm/rhsmcertd.log-*",
|
||||
"/var/log/exim4/*.gz",
|
||||
"/var/log/journal/*",
|
||||
"/var/spool/clientmqueue/*",
|
||||
|
||||
"/tmp/yum_save_*",
|
||||
"/tmp/tmp.*",
|
||||
|
||||
"/www/server/dztasks/logs/dztasks.*.log",
|
||||
"/www/server/dztasks/logs/dztasks_*",
|
||||
]
|
||||
for i in clog:
|
||||
content += i + "\n"
|
||||
|
||||
# 常用日志
|
||||
clogcom = [
|
||||
"/var/log/messages",
|
||||
"/var/log/btmp",
|
||||
"/var/log/wtmp",
|
||||
"/var/log/secure",
|
||||
"/var/log/lastlog",
|
||||
"/var/log/cron",
|
||||
"/www/wwwlogs",
|
||||
"/www/server/rsyncd",
|
||||
"/www/server/acme_pandominassl_apply",
|
||||
"/www/server/sphinx/index",
|
||||
"/www/server/mongodb/logs",
|
||||
"/www/server/php/53/var/log",
|
||||
"/www/server/php/54/var/log",
|
||||
"/www/server/php/55/var/log",
|
||||
"/www/server/php/56/var/log",
|
||||
"/www/server/php/70/var/log",
|
||||
"/www/server/php/71/var/log",
|
||||
"/www/server/php/72/var/log",
|
||||
"/www/server/php/73/var/log",
|
||||
"/www/server/php/74/var/log",
|
||||
"/www/server/php/80/var/log",
|
||||
"/www/server/php/81/var/log",
|
||||
"/www/server/php/82/var/log",
|
||||
"/www/server/php/83/var/log",
|
||||
"/www/server/php/84/var/log",
|
||||
"/www/server/openresty/nginx/logs",
|
||||
"/www/server/phpmyadmin",
|
||||
"/www/server/redis/data",
|
||||
"/www/server/alist/data/log",
|
||||
"/www/server/dztasks/logs",
|
||||
"/www/server/cron",
|
||||
]
|
||||
for i in clogcom:
|
||||
if os.path.exists(i):
|
||||
content += i + "\n"
|
||||
|
||||
# 清理日志
|
||||
rootDir = "/var/log"
|
||||
|
||||
l = os.listdir(rootDir)
|
||||
for x in range(len(l)):
|
||||
abspath = rootDir + "/" + l[x]
|
||||
content += abspath + "\n"
|
||||
mw.writeFile(conf, content)
|
||||
|
||||
|
||||
def start():
|
||||
initConf()
|
||||
|
||||
lock_file = getLockFile()
|
||||
if not os.path.exists(lock_file):
|
||||
mw.writeFile(lock_file, "")
|
||||
|
||||
import tool_task
|
||||
tool_task.createBgTask()
|
||||
return 'ok'
|
||||
|
||||
return 'fail'
|
||||
|
||||
|
||||
def stop():
|
||||
initConf()
|
||||
lock_file = getLockFile()
|
||||
if os.path.exists(lock_file):
|
||||
os.remove(lock_file)
|
||||
import tool_task
|
||||
tool_task.removeBgTask()
|
||||
return 'ok'
|
||||
|
||||
return 'fail'
|
||||
|
||||
|
||||
def reload():
|
||||
return 'ok'
|
||||
|
||||
|
||||
def get_filePath_fileName_fileExt(filename):
|
||||
(filepath, tempfilename) = os.path.split(filename)
|
||||
(shotname, extension) = os.path.splitext(tempfilename)
|
||||
return filepath, shotname, extension
|
||||
|
||||
|
||||
def cleanFileLog(path):
|
||||
filepath, shotname, extension = get_filePath_fileName_fileExt(path)
|
||||
if extension == ".log":
|
||||
cmd = "echo \"\" > " + path
|
||||
tmp = mw.execShell(cmd)
|
||||
if tmp[1] != "":
|
||||
cmd += " | error:" + tmp[1].strip()
|
||||
print(cmd)
|
||||
|
||||
|
||||
def cleanSelfFileLog(path):
|
||||
filepath, shotname, extension = get_filePath_fileName_fileExt(path)
|
||||
if extension == ".log":
|
||||
cmd = "echo \"\" > " + path
|
||||
tmp = mw.execShell(cmd)
|
||||
if tmp[1] != "":
|
||||
cmd += " | error:" + tmp[1].strip()
|
||||
print(cmd)
|
||||
|
||||
|
||||
def cleanDirLog(path):
|
||||
l = os.listdir(path)
|
||||
for x in range(len(l)):
|
||||
abspath = path + "/" + l[x]
|
||||
if os.path.isfile(abspath):
|
||||
cleanFileLog(abspath)
|
||||
if os.path.isdir(abspath):
|
||||
cleanDirLog(abspath)
|
||||
|
||||
|
||||
def cleanRun():
|
||||
plugin_dir = getPluginDir()
|
||||
log_file = getServerDir()+'/clean.log'
|
||||
cmd = 'python3 '+plugin_dir+'/index.py clean > '+log_file
|
||||
os.system(cmd)
|
||||
return mw.returnJson(True, '执行成功!')
|
||||
|
||||
def cleanLog():
|
||||
conf = getConf()
|
||||
clist = mw.readFile(conf).strip()
|
||||
clist = clist.split("\n")
|
||||
|
||||
for x in clist:
|
||||
abspath = x.strip()
|
||||
|
||||
if abspath.find('*') > 1:
|
||||
cmd = 'rm -rf ' + abspath
|
||||
print(cmd)
|
||||
data = mw.execShell(cmd)
|
||||
# print(data)
|
||||
continue
|
||||
|
||||
if os.path.exists(abspath):
|
||||
if os.path.isfile(abspath):
|
||||
cleanSelfFileLog(abspath)
|
||||
continue
|
||||
|
||||
if os.path.isdir(abspath):
|
||||
cleanDirLog(abspath)
|
||||
continue
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
func = sys.argv[1]
|
||||
if func == 'status':
|
||||
print(status())
|
||||
elif func == 'start':
|
||||
print(start())
|
||||
elif func == 'stop':
|
||||
print(stop())
|
||||
elif func == 'restart':
|
||||
print(restart())
|
||||
elif func == 'reload':
|
||||
print(reload())
|
||||
elif func == 'conf':
|
||||
print(getConf())
|
||||
elif func == 'run_log':
|
||||
print(runLog())
|
||||
elif func == 'clean':
|
||||
cleanLog()
|
||||
elif func == 'clean_run':
|
||||
print(cleanRun())
|
||||
else:
|
||||
print('error')
|
||||
@@ -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"
|
||||
}
|
||||
@@ -1,42 +0,0 @@
|
||||
#!/bin/bash
|
||||
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
|
||||
export PATH
|
||||
|
||||
# cd /Users/midoks/Desktop/mwdev/server/mdserver-web/plugins/mongodb && /bin/bash install.sh install 5.0.4
|
||||
# cd /www/server/mdserver-web/plugins/mongodb && /bin/bash install.sh install 5.0.4
|
||||
|
||||
curPath=`pwd`
|
||||
rootPath=$(dirname "$curPath")
|
||||
rootPath=$(dirname "$rootPath")
|
||||
serverPath=$(dirname "$rootPath")
|
||||
|
||||
VERSION=$2
|
||||
|
||||
if [ -f ${rootPath}/bin/activate ];then
|
||||
source ${rootPath}/bin/activate
|
||||
fi
|
||||
|
||||
|
||||
Install_app()
|
||||
{
|
||||
echo '正在安装脚本文件...'
|
||||
mkdir -p $serverPath/clean
|
||||
|
||||
cd ${rootPath} && python3 ${rootPath}/plugins/clean/index.py start
|
||||
echo "${VERSION}" > $serverPath/clean/version.pl
|
||||
echo '安装完成'
|
||||
}
|
||||
|
||||
Uninstall_app()
|
||||
{
|
||||
cd ${rootPath} && python3 ${rootPath}/plugins/clean/index.py stop
|
||||
rm -rf $serverPath/clean
|
||||
echo "Uninstall_clean"
|
||||
}
|
||||
|
||||
action=$1
|
||||
if [ "${1}" == 'install' ];then
|
||||
Install_app
|
||||
else
|
||||
Uninstall_app
|
||||
fi
|
||||
@@ -1,129 +0,0 @@
|
||||
# coding:utf-8
|
||||
|
||||
import sys
|
||||
import io
|
||||
import os
|
||||
import time
|
||||
import json
|
||||
|
||||
web_dir = os.getcwd() + "/web"
|
||||
if os.path.exists(web_dir):
|
||||
sys.path.append(web_dir)
|
||||
os.chdir(web_dir)
|
||||
|
||||
import core.mw as mw
|
||||
from utils.crontab import crontab as MwCrontab
|
||||
|
||||
|
||||
app_debug = False
|
||||
if mw.isAppleSystem():
|
||||
app_debug = True
|
||||
|
||||
|
||||
def getPluginName():
|
||||
return 'clean'
|
||||
|
||||
|
||||
def getPluginDir():
|
||||
return mw.getPluginDir() + '/' + getPluginName()
|
||||
|
||||
|
||||
def getServerDir():
|
||||
return mw.getServerDir() + '/' + getPluginName()
|
||||
|
||||
|
||||
def getTaskConf():
|
||||
conf = getServerDir() + "/task_config.json"
|
||||
return conf
|
||||
|
||||
|
||||
def getConfigData():
|
||||
conf = getTaskConf()
|
||||
if os.path.exists(conf):
|
||||
return json.loads(mw.readFile(getTaskConf()))
|
||||
return {
|
||||
"task_id": -1,
|
||||
"period": "day-n",
|
||||
"where1": "7",
|
||||
"hour": "0",
|
||||
"minute": "15",
|
||||
}
|
||||
|
||||
|
||||
def createBgTask():
|
||||
removeBgTask()
|
||||
createBgTaskByName(getPluginName())
|
||||
|
||||
|
||||
def createBgTaskByName(name):
|
||||
args = getConfigData()
|
||||
_name = "[勿删]日志清理[" + name + "]"
|
||||
res = mw.M("crontab").field("id, name").where("name=?", (_name,)).find()
|
||||
if res:
|
||||
return True
|
||||
|
||||
if "task_id" in args and args["task_id"] > 0:
|
||||
res = mw.M("crontab").field("id, name").where(
|
||||
"id=?", (args["task_id"],)).find()
|
||||
if res and res["id"] == args["task_id"]:
|
||||
print("计划任务已经存在!")
|
||||
return True
|
||||
|
||||
mw_dir = mw.getPanelDir()
|
||||
cmd = '''
|
||||
mw_dir=%s
|
||||
rname=%s
|
||||
plugin_path=%s
|
||||
script_path=%s
|
||||
logs_file=$plugin_path/${rname}.log
|
||||
''' % (mw_dir, name, getServerDir(), getPluginDir())
|
||||
cmd += 'echo "★【`date +"%Y-%m-%d %H:%M:%S"`】 STSRT★" >> $logs_file' + "\n"
|
||||
cmd += 'echo ">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>" >> $logs_file' + "\n"
|
||||
cmd += 'echo "python3 $script_path/index.py clean >> $logs_file 2>&1"' + "\n"
|
||||
cmd += 'cd $mw_dir && python3 $script_path/index.py clean >> $logs_file 2>&1' + "\n"
|
||||
cmd += 'echo "【`date +"%Y-%m-%d %H:%M:%S"`】 END★" >> $logs_file' + "\n"
|
||||
cmd += 'echo "<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<" >> $logs_file' + "\n"
|
||||
|
||||
params = {
|
||||
'name': _name,
|
||||
'type': args['period'],
|
||||
'week': "",
|
||||
'where1': args['where1'],
|
||||
'hour': args['hour'],
|
||||
'minute': args['minute'],
|
||||
'save': "",
|
||||
'backup_to': "",
|
||||
'stype': "toShell",
|
||||
'sname': '',
|
||||
'sbody': cmd,
|
||||
'url_address': '',
|
||||
}
|
||||
|
||||
task_id = MwCrontab.instance().add(params)
|
||||
if task_id > 0:
|
||||
args["task_id"] = task_id
|
||||
args["name"] = name
|
||||
mw.writeFile(getTaskConf(), json.dumps(args))
|
||||
|
||||
|
||||
def removeBgTask():
|
||||
cfg = getConfigData()
|
||||
if "task_id" in cfg and cfg["task_id"] > 0:
|
||||
res = mw.M("crontab").field("id, name").where(
|
||||
"id=?", (cfg["task_id"],)).find()
|
||||
if res and res["id"] == cfg["task_id"]:
|
||||
data = MwCrontab.instance().delete(cfg["task_id"])
|
||||
if data[0]:
|
||||
cfg["task_id"] = -1
|
||||
mw.writeFile(getTaskConf(), json.dumps(cfg))
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
if len(sys.argv) > 1:
|
||||
action = sys.argv[1]
|
||||
if action == "remove":
|
||||
removeBgTask()
|
||||
elif action == "add":
|
||||
createBgTask()
|
||||
|
Before Width: | Height: | Size: 31 KiB |
@@ -1,31 +0,0 @@
|
||||
<style>
|
||||
.overflow_hide {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
vertical-align: middle;
|
||||
}
|
||||
</style>
|
||||
|
||||
<div class="bt-form">
|
||||
<div class='plugin_version'></div>
|
||||
<div class="bt-w-main">
|
||||
<div class="bt-w-menu">
|
||||
<p class="bgw" onclick="pluginService('cloudreve');">服务</p>
|
||||
<p onclick="pluginInitD('cloudreve');">自启动</p>
|
||||
<p onclick="alistCommonFunc();">常用功能</p>
|
||||
<p onclick="pluginConfigTpl('cloudreve',$('.plugin_version').attr('version'));">配置修改</p>
|
||||
<p onclick="pluginLogs('cloudreve','','run_log');">运行日志</p>
|
||||
<p onclick="alistReadme();">相关说明</p>
|
||||
|
||||
</div>
|
||||
<div class="bt-w-con pd15">
|
||||
<div class="soft-man-con" style="height: 520px; overflow: auto;"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script type="text/javascript">
|
||||
$.getScript( "/plugins/file?name=cloudreve&f=js/cloudreve.js", function(){
|
||||
pluginService('cloudreve', $('.plugin_version').attr('version'));
|
||||
});
|
||||
</script>
|
||||
@@ -1,292 +0,0 @@
|
||||
# coding:utf-8
|
||||
|
||||
import sys
|
||||
import io
|
||||
import os
|
||||
import time
|
||||
import re
|
||||
|
||||
web_dir = os.getcwd() + "/web"
|
||||
if os.path.exists(web_dir):
|
||||
sys.path.append(web_dir)
|
||||
os.chdir(web_dir)
|
||||
|
||||
import core.mw as mw
|
||||
|
||||
app_debug = False
|
||||
if mw.isAppleSystem():
|
||||
app_debug = True
|
||||
|
||||
|
||||
def getPluginName():
|
||||
return 'cloudreve'
|
||||
|
||||
|
||||
def getPluginDir():
|
||||
return mw.getPluginDir() + '/' + getPluginName()
|
||||
|
||||
|
||||
def getServerDir():
|
||||
return mw.getServerDir() + '/' + getPluginName()
|
||||
|
||||
|
||||
def getInitDFile():
|
||||
current_os = mw.getOs()
|
||||
if current_os == 'darwin':
|
||||
return '/tmp/' + getPluginName()
|
||||
|
||||
if current_os.startswith('freebsd'):
|
||||
return '/etc/rc.d/' + getPluginName()
|
||||
|
||||
return '/etc/init.d/' + getPluginName()
|
||||
|
||||
|
||||
def getInitDTpl():
|
||||
path = getPluginDir() + "/init.d/" + getPluginName() + ".tpl"
|
||||
return path
|
||||
|
||||
|
||||
def getArgs():
|
||||
args = sys.argv[3:]
|
||||
tmp = {}
|
||||
args_len = len(args)
|
||||
|
||||
if args_len == 1:
|
||||
t = args[0].strip('{').strip('}')
|
||||
if t.strip() == '':
|
||||
tmp = []
|
||||
else:
|
||||
t = t.split(':')
|
||||
tmp[t[0]] = t[1]
|
||||
tmp[t[0]] = t[1]
|
||||
elif args_len > 1:
|
||||
for i in range(len(args)):
|
||||
t = args[i].split(':')
|
||||
tmp[t[0]] = t[1]
|
||||
return tmp
|
||||
|
||||
def checkArgs(data, ck=[]):
|
||||
for i in range(len(ck)):
|
||||
if not ck[i] in data:
|
||||
return (False, mw.returnJson(False, '参数:(' + ck[i] + ')没有!'))
|
||||
return (True, mw.returnJson(True, 'ok'))
|
||||
|
||||
|
||||
def getConf():
|
||||
path = getServerDir() + "/conf.ini"
|
||||
return path
|
||||
|
||||
def configTpl():
|
||||
path = getPluginDir() + '/tpl'
|
||||
pathFile = os.listdir(path)
|
||||
tmp = []
|
||||
for one in pathFile:
|
||||
file = path + '/' + one
|
||||
tmp.append(file)
|
||||
return mw.getJson(tmp)
|
||||
|
||||
|
||||
def readConfigTpl():
|
||||
args = getArgs()
|
||||
data = checkArgs(args, ['file'])
|
||||
if not data[0]:
|
||||
return data[1]
|
||||
|
||||
content = mw.readFile(args['file'])
|
||||
content = contentReplace(content)
|
||||
return mw.returnJson(True, 'ok', content)
|
||||
|
||||
|
||||
def status():
|
||||
data = mw.execShell(
|
||||
"ps aux|grep cloudreve |grep -v grep | grep -v python | grep -v mdserver-web | awk '{print $2}'")
|
||||
|
||||
if data[0] == '':
|
||||
return 'stop'
|
||||
return 'start'
|
||||
|
||||
def contentReplace(content):
|
||||
service_path = mw.getServerDir()
|
||||
content = content.replace('{$ROOT_PATH}', mw.getFatherDir())
|
||||
content = content.replace('{$SERVER_PATH}', service_path)
|
||||
content = content.replace('{$SERVER_APP}', service_path + '/'+getPluginName())
|
||||
return content
|
||||
|
||||
def initDreplace():
|
||||
|
||||
file_tpl = getInitDTpl()
|
||||
service_path = mw.getServerDir()
|
||||
|
||||
initD_path = getServerDir() + '/init.d'
|
||||
if not os.path.exists(initD_path):
|
||||
os.mkdir(initD_path)
|
||||
file_bin = initD_path + '/' + getPluginName()
|
||||
|
||||
# initd replace
|
||||
if not os.path.exists(file_bin):
|
||||
content = mw.readFile(file_tpl)
|
||||
content = content.replace('{$SERVER_PATH}', service_path)
|
||||
mw.writeFile(file_bin, content)
|
||||
mw.execShell('chmod +x ' + file_bin)
|
||||
|
||||
# systemd
|
||||
systemDir = mw.systemdCfgDir()
|
||||
systemService = systemDir + '/' + getPluginName() + '.service'
|
||||
if os.path.exists(systemDir) and not os.path.exists(systemService):
|
||||
systemServiceTpl = getPluginDir() + '/init.d/' + getPluginName() + '.service.tpl'
|
||||
service_path = mw.getServerDir()
|
||||
se_content = mw.readFile(systemServiceTpl)
|
||||
se_content = se_content.replace('{$SERVER_PATH}', service_path)
|
||||
mw.writeFile(systemService, se_content)
|
||||
mw.execShell('systemctl daemon-reload')
|
||||
|
||||
return file_bin
|
||||
|
||||
|
||||
def alistOp(method):
|
||||
file = initDreplace()
|
||||
|
||||
current_os = mw.getOs()
|
||||
if current_os == "darwin":
|
||||
data = mw.execShell(file + ' ' + method)
|
||||
if data[1] == '':
|
||||
return 'ok'
|
||||
return data[1]
|
||||
|
||||
if current_os.startswith("freebsd"):
|
||||
data = mw.execShell('service ' + getPluginName() + ' ' + method)
|
||||
if data[1] == '':
|
||||
return 'ok'
|
||||
return data[1]
|
||||
|
||||
data = mw.execShell('systemctl ' + method + ' ' + getPluginName())
|
||||
if data[1] == '':
|
||||
return 'ok'
|
||||
return data[1]
|
||||
|
||||
|
||||
def start():
|
||||
return alistOp('start')
|
||||
|
||||
|
||||
def stop():
|
||||
return alistOp('stop')
|
||||
|
||||
|
||||
def restart():
|
||||
status = alistOp('restart')
|
||||
return status
|
||||
|
||||
|
||||
def reload():
|
||||
return alistOp('reload')
|
||||
|
||||
|
||||
def initdStatus():
|
||||
current_os = mw.getOs()
|
||||
if current_os == 'darwin':
|
||||
return "Apple Computer does not support"
|
||||
|
||||
if current_os.startswith('freebsd'):
|
||||
initd_bin = getInitDFile()
|
||||
if os.path.exists(initd_bin):
|
||||
return 'ok'
|
||||
|
||||
shell_cmd = 'systemctl status ' + \
|
||||
getPluginName() + ' | grep loaded | grep "enabled;"'
|
||||
data = mw.execShell(shell_cmd)
|
||||
if data[0] == '':
|
||||
return 'fail'
|
||||
return 'ok'
|
||||
|
||||
|
||||
def initdInstall():
|
||||
current_os = mw.getOs()
|
||||
if current_os == 'darwin':
|
||||
return "Apple Computer does not support"
|
||||
|
||||
# freebsd initd install
|
||||
if current_os.startswith('freebsd'):
|
||||
import shutil
|
||||
source_bin = initDreplace()
|
||||
initd_bin = getInitDFile()
|
||||
shutil.copyfile(source_bin, initd_bin)
|
||||
mw.execShell('chmod +x ' + initd_bin)
|
||||
mw.execShell('sysrc ' + getPluginName() + '_enable="YES"')
|
||||
return 'ok'
|
||||
|
||||
mw.execShell('systemctl enable ' + getPluginName())
|
||||
return 'ok'
|
||||
|
||||
|
||||
def initdUinstall():
|
||||
current_os = mw.getOs()
|
||||
if current_os == 'darwin':
|
||||
return "Apple Computer does not support"
|
||||
|
||||
if current_os.startswith('freebsd'):
|
||||
initd_bin = getInitDFile()
|
||||
os.remove(initd_bin)
|
||||
mw.execShell('sysrc ' + getPluginName() + '_enable="NO"')
|
||||
return 'ok'
|
||||
|
||||
mw.execShell('systemctl disable ' + getPluginName())
|
||||
return 'ok'
|
||||
|
||||
|
||||
def runLog():
|
||||
return getServerDir() + '/logs.pl'
|
||||
|
||||
def pSqliteDb(dbname='databases'):
|
||||
pos_file = getServerDir() + '/data/'
|
||||
file = pos_file + '/data.db'
|
||||
name = 'data'
|
||||
conn = mw.M(dbname).dbPos(pos_file, name)
|
||||
return conn
|
||||
|
||||
def getCloudrevePort():
|
||||
file = getConf()
|
||||
content = mw.readFile(file)
|
||||
rep = r'Listen\s*=\s*(.*)'
|
||||
tmp = re.search(rep, content)
|
||||
return tmp.groups()[0].strip()
|
||||
|
||||
def homePage():
|
||||
http_port = getCloudrevePort()
|
||||
ip = mw.getLocalIp()
|
||||
if mw.isAppleSystem():
|
||||
ip = '127.0.0.1'
|
||||
url = 'http://'+ip+""+str(http_port)
|
||||
# print(url)
|
||||
return mw.returnJson(True, 'ok!', url)
|
||||
|
||||
if __name__ == "__main__":
|
||||
func = sys.argv[1]
|
||||
if func == 'status':
|
||||
print(status())
|
||||
elif func == 'start':
|
||||
print(start())
|
||||
elif func == 'stop':
|
||||
print(stop())
|
||||
elif func == 'restart':
|
||||
print(restart())
|
||||
elif func == 'reload':
|
||||
print(reload())
|
||||
elif func == 'initd_status':
|
||||
print(initdStatus())
|
||||
elif func == 'initd_install':
|
||||
print(initdInstall())
|
||||
elif func == 'initd_uninstall':
|
||||
print(initdUinstall())
|
||||
elif func == 'conf':
|
||||
print(getConf())
|
||||
elif func == 'run_log':
|
||||
print(runLog())
|
||||
elif func == 'config_tpl':
|
||||
print(configTpl())
|
||||
elif func == 'read_config_tpl':
|
||||
print(readConfigTpl())
|
||||
elif func == 'home_page':
|
||||
print(homePage())
|
||||
else:
|
||||
print('error')
|
||||
@@ -1,17 +0,0 @@
|
||||
{
|
||||
"sort": 7,
|
||||
"ps": "一个支持多种存储的文件列表程序",
|
||||
"name": "cloudreve",
|
||||
"title": "cloudreve",
|
||||
"shell": "install.sh",
|
||||
"versions":["3.8.3"],
|
||||
"tip": "soft",
|
||||
"checks": "server/cloudreve",
|
||||
"path": "server/cloudreve",
|
||||
"display": 1,
|
||||
"author": "cloudreve",
|
||||
"date": "2024-10-14",
|
||||
"home": "https://cloudreve.org/",
|
||||
"type": 0,
|
||||
"pid": "5"
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
[Unit]
|
||||
Description=A file list program that supports multiple storage
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
WorkingDirectory={$SERVER_PATH}/cloudreve
|
||||
ExecStart={$SERVER_PATH}/cloudreve/cloudreve
|
||||
ExecReload=/bin/kill -USR2 $MAINPID
|
||||
Restart=on-failure
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
@@ -1,46 +0,0 @@
|
||||
#!/bin/sh
|
||||
# chkconfig: 2345 55 25
|
||||
# description: cloudreve Service
|
||||
|
||||
### BEGIN INIT INFO
|
||||
# Provides: cloudreve
|
||||
# Required-Start: $all
|
||||
# Required-Stop: $all
|
||||
# Default-Start: 2 3 4 5
|
||||
# Default-Stop: 0 1 6
|
||||
# Short-Description: starts cloudreve
|
||||
# Description: starts the MDW-Web
|
||||
### END INIT INFO
|
||||
|
||||
# Simple cloudreve init.d script conceived to work on Linux systems
|
||||
# as it does use of the /proc filesystem.
|
||||
|
||||
app_start(){
|
||||
echo "Starting cloudreve server..."
|
||||
cd {$SERVER_PATH}/cloudreve
|
||||
./cloudreve >> {$SERVER_PATH}/cloudreve/logs.pl 2>&1 &
|
||||
}
|
||||
|
||||
app_stop(){
|
||||
echo "dztasks stopped"
|
||||
ps -ef| grep cloudreve | grep -v grep | grep -v python | grep -v sh | awk '{print $2}'| xargs kill
|
||||
}
|
||||
|
||||
|
||||
case "$1" in
|
||||
start)
|
||||
app_start
|
||||
;;
|
||||
stop)
|
||||
app_stop
|
||||
;;
|
||||
restart|reload)
|
||||
app_stop
|
||||
sleep 0.3
|
||||
app_start
|
||||
;;
|
||||
*)
|
||||
echo "Please use start or stop as first argument"
|
||||
;;
|
||||
esac
|
||||
|
||||
@@ -1,90 +0,0 @@
|
||||
#!/bin/bash
|
||||
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin:/opt/homebrew/bin
|
||||
export PATH
|
||||
|
||||
curPath=`pwd`
|
||||
rootPath=$(dirname "$curPath")
|
||||
rootPath=$(dirname "$rootPath")
|
||||
serverPath=$(dirname "$rootPath")
|
||||
|
||||
# cd /Users/midoks/Desktop/mwdev/server/mdserver-web/plugins/cloudreve && bash install.sh install 3.8.3
|
||||
# cd /www/server/mdserver-web/plugins/cloudreve && bash install.sh install 3.8.3
|
||||
|
||||
VERSION=$2
|
||||
|
||||
sysArch=`arch`
|
||||
sysName=`uname`
|
||||
|
||||
ALIST_ARCH_NAME=amd64
|
||||
if [ "$sysArch" == "arm64" ];then
|
||||
ALIST_ARCH_NAME=arm64
|
||||
elif [ "$sysArch" == "x86_64" ]; then
|
||||
ALIST_ARCH_NAME=amd64
|
||||
elif [ "$sysArch" == "aarch64" ]; then
|
||||
ALIST_ARCH_NAME=aarch64
|
||||
fi
|
||||
|
||||
ALIST_NAME=linux
|
||||
if [ "$sysName" == "Darwin" ];then
|
||||
ALIST_NAME=darwin
|
||||
fi
|
||||
|
||||
Install_App()
|
||||
{
|
||||
echo '正在安装脚本文件...'
|
||||
|
||||
mkdir -p $serverPath/source
|
||||
mkdir -p $serverPath/source/cloudreve
|
||||
|
||||
FILE_TGZ=cloudreve_${VERSION}_${ALIST_NAME}_${ALIST_ARCH_NAME}.tar.gz
|
||||
CLOUDREVE_DIR=$serverPath/source/cloudreve
|
||||
|
||||
if [ ! -f $CLOUDREVE_DIR/${FILE_TGZ} ];then
|
||||
wget -O $CLOUDREVE_DIR/${FILE_TGZ} https://github.com/cloudreve/Cloudreve/releases/download/${VERSION}/${FILE_TGZ}
|
||||
fi
|
||||
|
||||
mkdir -p $serverPath/cloudreve
|
||||
|
||||
cd $CLOUDREVE_DIR && tar -zxvf ${FILE_TGZ} -C $serverPath/cloudreve
|
||||
echo "${VERSION}" > $serverPath/cloudreve/version.pl
|
||||
|
||||
cd ${rootPath} && python3 ${rootPath}/plugins/cloudreve/index.py start
|
||||
cd ${rootPath} && python3 ${rootPath}/plugins/cloudreve/index.py initd_install
|
||||
|
||||
# cd $serverPath/cloudreve && ./alist admin set admin
|
||||
echo '安装cloudreve完成'
|
||||
}
|
||||
|
||||
Uninstall_App()
|
||||
{
|
||||
if [ -f /usr/lib/systemd/system/cloudreve.service ];then
|
||||
systemctl stop cloudreve
|
||||
systemctl disable cloudreve
|
||||
rm -rf /usr/lib/systemd/system/cloudreve.service
|
||||
systemctl daemon-reload
|
||||
fi
|
||||
|
||||
if [ -f /lib/systemd/system/cloudreve.service ];then
|
||||
systemctl stop cloudreve
|
||||
systemctl disable cloudreve
|
||||
rm -rf /lib/systemd/system/cloudreve.service
|
||||
systemctl daemon-reload
|
||||
fi
|
||||
|
||||
if [ -f $serverPath/cloudreve/initd/cloudreve ];then
|
||||
$serverPath/cloudreve/initd/cloudreve stop
|
||||
fi
|
||||
|
||||
if [ -d $serverPath/cloudreve ];then
|
||||
rm -rf $serverPath/cloudreve
|
||||
fi
|
||||
|
||||
echo "卸载cloudreve成功"
|
||||
}
|
||||
|
||||
action=$1
|
||||
if [ "${1}" == 'install' ];then
|
||||
Install_App
|
||||
else
|
||||
Uninstall_App
|
||||
fi
|
||||
@@ -1,82 +0,0 @@
|
||||
function alistPost(method, version, args,callback){
|
||||
var loadT = layer.msg('正在获取...', { icon: 16, time: 0, shade: 0.3 });
|
||||
|
||||
var req_data = {};
|
||||
req_data['name'] = 'cloudreve';
|
||||
req_data['func'] = method;
|
||||
req_data['version'] = version;
|
||||
|
||||
if (typeof(args) == 'string'){
|
||||
req_data['args'] = JSON.stringify(toArrayObject(args));
|
||||
} else {
|
||||
req_data['args'] = JSON.stringify(args);
|
||||
}
|
||||
|
||||
$.post('/plugins/run', req_data, function(data) {
|
||||
layer.close(loadT);
|
||||
if (!data.status){
|
||||
//错误展示10S
|
||||
layer.msg(data.msg,{icon:0,time:2000,shade: [10, '#000']});
|
||||
return;
|
||||
}
|
||||
|
||||
if(typeof(callback) == 'function'){
|
||||
callback(data);
|
||||
}
|
||||
},'json');
|
||||
}
|
||||
|
||||
function alistPostCallbak(method, version, args,callback){
|
||||
var loadT = layer.msg('正在获取...', { icon: 16, time: 0, shade: 0.3 });
|
||||
|
||||
var req_data = {};
|
||||
req_data['name'] = 'cloudreve';
|
||||
req_data['func'] = method;
|
||||
args['version'] = version;
|
||||
|
||||
if (typeof(args) == 'string'){
|
||||
req_data['args'] = JSON.stringify(toArrayObject(args));
|
||||
} else {
|
||||
req_data['args'] = JSON.stringify(args);
|
||||
}
|
||||
|
||||
$.post('/plugins/callback', req_data, function(data) {
|
||||
layer.close(loadT);
|
||||
if (!data.status){
|
||||
layer.msg(data.msg,{icon:0,time:2000,shade: [0.3, '#000']});
|
||||
return;
|
||||
}
|
||||
|
||||
if(typeof(callback) == 'function'){
|
||||
callback(data);
|
||||
}
|
||||
},'json');
|
||||
}
|
||||
|
||||
|
||||
function commonHomePage(){
|
||||
alistPost('home_page', '', {}, function(data){
|
||||
var rdata = $.parseJSON(data.data);
|
||||
window.open(rdata.data);
|
||||
});
|
||||
}
|
||||
|
||||
function alistCommonFunc(){
|
||||
var con = '';
|
||||
con += '<hr/><p class="conf_p" style="text-align:center;">\
|
||||
<button class="btn btn-default btn-sm" onclick="commonHomePage()">主页</button>\
|
||||
</p>';
|
||||
|
||||
$(".soft-man-con").html(con);
|
||||
}
|
||||
|
||||
function alistReadme(){
|
||||
|
||||
|
||||
var readme = '<ul class="help-info-text c7">';
|
||||
readme += '<li>记住安装时生成的用户+密码</li>';
|
||||
readme += '</ul>';
|
||||
|
||||
$('.soft-man-con').html(readme);
|
||||
}
|
||||
|
||||
|
Before Width: | Height: | Size: 2.3 KiB |
@@ -1,35 +0,0 @@
|
||||
<style>
|
||||
.overflow_hide {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
#select_db xm-select{
|
||||
min-height: 30px;
|
||||
line-height: 30px;
|
||||
}
|
||||
|
||||
#select_db xm-select *{
|
||||
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('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>
|
||||
@@ -1,101 +0,0 @@
|
||||
# coding:utf-8
|
||||
|
||||
import sys
|
||||
import io
|
||||
import os
|
||||
import time
|
||||
import re
|
||||
|
||||
web_dir = os.getcwd() + "/web"
|
||||
if os.path.exists(web_dir):
|
||||
sys.path.append(web_dir)
|
||||
os.chdir(web_dir)
|
||||
|
||||
import core.mw as mw
|
||||
|
||||
app_debug = False
|
||||
if mw.isAppleSystem():
|
||||
app_debug = True
|
||||
|
||||
|
||||
def getPluginName():
|
||||
return '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')
|
||||
@@ -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"
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
#!/bin/bash
|
||||
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin:/opt/homebrew/bin
|
||||
export PATH
|
||||
|
||||
curPath=`pwd`
|
||||
rootPath=$(dirname "$curPath")
|
||||
rootPath=$(dirname "$rootPath")
|
||||
serverPath=$(dirname "$rootPath")
|
||||
|
||||
VERSION=$2
|
||||
|
||||
Install_App()
|
||||
{
|
||||
echo '正在安装脚本文件...'
|
||||
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
|
||||
@@ -1,258 +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
|
||||
|
||||
web_dir = os.getcwd() + "/web"
|
||||
if os.path.exists(web_dir):
|
||||
sys.path.append(web_dir)
|
||||
os.chdir(web_dir)
|
||||
|
||||
import core.mw as mw
|
||||
|
||||
|
||||
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 = r'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 = rb'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 ----------------------------------
|
||||
|
||||
@@ -1,360 +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
|
||||
|
||||
web_dir = os.getcwd() + "/web"
|
||||
if os.path.exists(web_dir):
|
||||
sys.path.append(web_dir)
|
||||
os.chdir(web_dir)
|
||||
|
||||
import core.mw as mw
|
||||
|
||||
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)
|
||||
if where:
|
||||
count = collection_instance.count_documents(where)
|
||||
else:
|
||||
collection_stats = db_instance.command("collStats", collection)
|
||||
count = collection_stats.get("count")
|
||||
|
||||
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'
|
||||
@@ -1,349 +0,0 @@
|
||||
# coding:utf-8
|
||||
|
||||
import sys
|
||||
import io
|
||||
import os
|
||||
import time
|
||||
import re
|
||||
import redis
|
||||
|
||||
web_dir = os.getcwd() + "/web"
|
||||
if os.path.exists(web_dir):
|
||||
sys.path.append(web_dir)
|
||||
os.chdir(web_dir)
|
||||
|
||||
import core.mw as mw
|
||||
|
||||
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 = r"\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 ----------------------------------
|
||||
|
||||
@@ -1,751 +0,0 @@
|
||||
# coding:utf-8
|
||||
|
||||
import sys
|
||||
import io
|
||||
import os
|
||||
import time
|
||||
import re
|
||||
import pymongo
|
||||
import json
|
||||
|
||||
import core.mw as 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):
|
||||
pass
|
||||
|
||||
def setSid(self, sid):
|
||||
self.__config = self.get_options(sid=sid)
|
||||
|
||||
def conn(self):
|
||||
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, db_pos_name, dbname='databases'):
|
||||
mydb_path = mw.getServerDir() +'/'+db_pos_name
|
||||
name = 'mysql'
|
||||
conn = mw.M(dbname).dbPos(mydb_path, name)
|
||||
return conn
|
||||
|
||||
# 获取配置项
|
||||
def get_options(self, sid=None):
|
||||
result = {}
|
||||
result['socket'] = ''
|
||||
result['port'] = 3306
|
||||
result['host'] = '127.0.0.1'
|
||||
result['username'] = 'root'
|
||||
|
||||
if sid in ['mysql', 'mysql-apt', 'mysql-yum']:
|
||||
my_cnf_path = "{}/{}/etc/my.cnf".format(mw.getServerDir(),sid)
|
||||
|
||||
mydb_content = mw.readFile(my_cnf_path)
|
||||
if not mydb_content: return False
|
||||
|
||||
mysql_pass = self.sqliteDb(sid, 'config').where('id=?', (1,)).getField('mysql_root')
|
||||
result['password'] = mysql_pass
|
||||
|
||||
rep = r'port\s*=\s*(.*)'
|
||||
port_re = re.search(rep, mydb_content)
|
||||
if port_re:
|
||||
result['port'] = int(port_re.groups()[0].strip())
|
||||
socket_rep = r'socket\s*=\s*(.*)'
|
||||
socket_re = re.search(socket_rep, mydb_content)
|
||||
if socket_re:
|
||||
result['socket'] = socket_re.groups()[0].strip()
|
||||
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
|
||||
|
||||
def getServerList(self):
|
||||
my_cnf_path = "{}/mysql/etc/my.cnf".format(mw.getServerDir())
|
||||
data = []
|
||||
|
||||
local_mysql = "{}/mysql/etc/my.cnf".format(mw.getServerDir())
|
||||
if os.path.exists(local_mysql):
|
||||
data.append({'name':'本地服务器', 'val':'mysql'})
|
||||
|
||||
local_mysql_apt = "{}/mysql-apt/etc/my.cnf".format(mw.getServerDir())
|
||||
if os.path.exists(local_mysql_apt):
|
||||
data.append({'name':'本地服务器[apt]', 'val':'mysql-apt'})
|
||||
|
||||
local_mysql_yum = "{}/mysql-yum/etc/my.cnf".format(mw.getServerDir())
|
||||
if os.path.exists(local_mysql_yum):
|
||||
data.append({'name':'本地服务器[yum]', 'val':'mysql-yum'})
|
||||
return mw.returnData(True, 'ok', data)
|
||||
|
||||
@singleton
|
||||
class nosqlMySQLCtr():
|
||||
|
||||
def __init__(self):
|
||||
pass
|
||||
|
||||
def getServerList(self, args):
|
||||
instance = nosqlMySQL()
|
||||
return instance.getServerList()
|
||||
|
||||
def getInstanceBySid(self, sid):
|
||||
instance = nosqlMySQL()
|
||||
instance.setSid(sid)
|
||||
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']
|
||||
if table == '':
|
||||
page_args = {}
|
||||
page_args['count'] = 0
|
||||
page_args['tojs'] = 'mysqlGetDataList'
|
||||
page_args['p'] = 1
|
||||
page_args['row'] = 10
|
||||
|
||||
rdata = {}
|
||||
rdata['page'] = mw.getPage(page_args)
|
||||
rdata['list'] = []
|
||||
rdata['count'] = 0
|
||||
return mw.returnData(True,'ok', rdata)
|
||||
|
||||
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)
|
||||
|
||||
def getNetRow(self, my_instance):
|
||||
row = {}
|
||||
|
||||
data = my_instance.find("SHOW GLOBAL STATUS LIKE 'Com_select'")
|
||||
row['select'] = data['Value']
|
||||
|
||||
data = my_instance.find("SHOW GLOBAL STATUS LIKE 'Com_insert'")
|
||||
row['insert'] = data['Value']
|
||||
|
||||
data = my_instance.find("SHOW GLOBAL STATUS LIKE 'Com_update'")
|
||||
row['update'] = data['Value']
|
||||
|
||||
data = my_instance.find("SHOW GLOBAL STATUS LIKE 'Com_delete'")
|
||||
row['delete'] = data['Value']
|
||||
|
||||
|
||||
|
||||
data = my_instance.find("SHOW GLOBAL STATUS LIKE 'Bytes_received'")
|
||||
row['recv_bytes'] = data['Value']
|
||||
|
||||
data = my_instance.find("SHOW GLOBAL STATUS LIKE 'Bytes_sent'")
|
||||
row['send_bytes'] = data['Value']
|
||||
return row
|
||||
|
||||
|
||||
def getNetList(self, args):
|
||||
from datetime import datetime
|
||||
sid = args['sid']
|
||||
my_instance = self.getInstanceBySid(sid).conn()
|
||||
if my_instance is False:
|
||||
return mw.returnData(False,'无法链接')
|
||||
|
||||
rdata = []
|
||||
row = {}
|
||||
row1 = self.getNetRow(my_instance)
|
||||
# 等待1秒
|
||||
time.sleep(1)
|
||||
row2 = self.getNetRow(my_instance)
|
||||
|
||||
data = my_instance.find("SHOW GLOBAL VARIABLES LIKE 'max_connections'")
|
||||
row['max_conn'] = data['Value']
|
||||
|
||||
data = my_instance.find("SHOW GLOBAL STATUS LIKE 'Threads_connected'")
|
||||
row['conn'] = data['Value']
|
||||
|
||||
current_time = datetime.now()
|
||||
row['current_time'] = current_time.strftime("%Y-%m-%d %H:%M:%S")
|
||||
|
||||
row['select'] = int(row2['select']) - int(row1['select'])
|
||||
row['insert'] = int(row2['insert']) - int(row1['insert'])
|
||||
row['update'] = int(row2['update']) - int(row1['update'])
|
||||
row['delete'] = int(row2['delete']) - int(row1['delete'])
|
||||
|
||||
recv_per_second = int(row2['recv_bytes']) - int(row1['recv_bytes'])
|
||||
send_per_second = int(row2['send_bytes']) - int(row1['send_bytes'])
|
||||
|
||||
# 将每秒接收和发送数据量从字节转换为兆比特
|
||||
row['recv_mbps'] = "{:.2f}".format(recv_per_second * 8 / 1000000) + " MBit/s"
|
||||
row['send_mbps'] = "{:.2f}".format(send_per_second * 8 / 1000000) + " MBit/s"
|
||||
|
||||
rdata.append(row)
|
||||
return mw.returnData(True, 'ok', rdata)
|
||||
|
||||
|
||||
def getTopnList(self, args):
|
||||
sid = args['sid']
|
||||
my_instance = self.getInstanceBySid(sid).conn()
|
||||
if my_instance is False:
|
||||
return mw.returnData(False,'无法链接')
|
||||
|
||||
is_performance_schema = my_instance.find("SELECT @@performance_schema")
|
||||
if is_performance_schema is None:
|
||||
return mw.returnData(False,'异常中断,重试!')
|
||||
|
||||
if is_performance_schema["@@performance_schema"] == 0:
|
||||
msg = "performance_schema参数未开启。\n"
|
||||
msg += "在my.cnf配置文件里添加performance_schema=1,并重启mysqld进程生效。"
|
||||
return mw.returnData(False, msg)
|
||||
|
||||
my_instance.execute("SET @sys.statement_truncate_len=4096")
|
||||
data = my_instance.query("select query,db,last_seen,exec_count,max_latency,avg_latency from sys.statement_analysis order by exec_count desc, last_seen desc limit 20")
|
||||
if data is None:
|
||||
return mw.returnData(False, "查询失败!")
|
||||
|
||||
filter_db = args['filter_db']
|
||||
if filter_db == 'yes':
|
||||
new_data = []
|
||||
for x in data:
|
||||
if x['db'] is not None:
|
||||
new_data.append(x)
|
||||
return mw.returnData(True, 'ok', new_data)
|
||||
return mw.returnData(True, 'ok', data)
|
||||
|
||||
# 查看重复或冗余的索引
|
||||
def getRedundantIndexes(self, args):
|
||||
sid = args['sid']
|
||||
my_instance = self.getInstanceBySid(sid).conn()
|
||||
if my_instance is False:
|
||||
return mw.returnData(False,'无法链接')
|
||||
|
||||
is_performance_schema = my_instance.find("SELECT @@performance_schema")
|
||||
if is_performance_schema["@@performance_schema"] == 0:
|
||||
msg = "performance_schema参数未开启。\n"
|
||||
msg += "在my.cnf配置文件里添加performance_schema=1,并重启mysqld进程生效。"
|
||||
return mw.returnData(False, msg)
|
||||
|
||||
data = my_instance.query("select table_schema,table_name,redundant_index_name,redundant_index_columns,sql_drop_index from sys.schema_redundant_indexes")
|
||||
if data is None:
|
||||
return mw.returnData(False, "查询失败!")
|
||||
# print(data)
|
||||
return mw.returnData(True, 'ok', data)
|
||||
|
||||
def redundantIndexesCmd(self, args):
|
||||
sid = args['sid']
|
||||
my_instance = self.getInstanceBySid(sid).conn()
|
||||
if my_instance is False:
|
||||
return mw.returnData(False,'无法链接')
|
||||
|
||||
is_performance_schema = my_instance.find("SELECT @@performance_schema")
|
||||
if is_performance_schema["@@performance_schema"] == 0:
|
||||
msg = "performance_schema参数未开启。\n"
|
||||
msg += "在my.cnf配置文件里添加performance_schema=1,并重启mysqld进程生效。"
|
||||
return mw.returnData(False, msg)
|
||||
|
||||
data = my_instance.query("select table_schema,table_name,redundant_index_name,redundant_index_columns,sql_drop_index from sys.schema_redundant_indexes")
|
||||
if data is None:
|
||||
return mw.returnData(False, "查询失败!")
|
||||
|
||||
index = int(args['index'])
|
||||
|
||||
cmd = data[index]['sql_drop_index']
|
||||
|
||||
my_instance.execute(cmd)
|
||||
return mw.returnData(True, '执行成功!')
|
||||
|
||||
def getTableInfo(self, args):
|
||||
from decimal import Decimal
|
||||
|
||||
sid = args['sid']
|
||||
my_instance = self.getInstanceBySid(sid).conn()
|
||||
if my_instance is False:
|
||||
return mw.returnData(False,'无法链接')
|
||||
|
||||
my_instance.execute("SET sql_mode=(SELECT REPLACE(@@sql_mode,'ONLY_FULL_GROUP_BY',''))")
|
||||
data = my_instance.query(
|
||||
"""
|
||||
SELECT t.TABLE_SCHEMA as TABLE_SCHEMA, t.TABLE_NAME as TABLE_NAME, t.ENGINE as ENGINE,
|
||||
IFNULL(t.DATA_LENGTH/1024/1024/1024, 0) as DATA_LENGTH,
|
||||
IFNULL(t.INDEX_LENGTH/1024/1024/1024, 0) as INDEX_LENGTH,
|
||||
IFNULL((DATA_LENGTH+INDEX_LENGTH)/1024/1024/1024, 0) AS TOTAL_LENGTH,
|
||||
c.column_name AS COLUMN_NAME, c.data_type AS DATA_TYPE, c.COLUMN_TYPE AS COLUMN_TYPE,
|
||||
t.AUTO_INCREMENT AS AUTO_INCREMENT, locate('unsigned', c.COLUMN_TYPE) = 0 AS IS_SIGNED
|
||||
FROM information_schema.TABLES t
|
||||
JOIN information_schema.COLUMNS c ON t.TABLE_SCHEMA = c.TABLE_SCHEMA AND t.table_name=c.table_name
|
||||
WHERE t.TABLE_SCHEMA NOT IN ('mysql', 'information_schema', 'performance_schema', 'sys')
|
||||
GROUP BY TABLE_NAME
|
||||
ORDER BY TOTAL_LENGTH DESC, AUTO_INCREMENT DESC;
|
||||
"""
|
||||
)
|
||||
|
||||
if data is None:
|
||||
return mw.returnData(True, 'ok', [])
|
||||
|
||||
for i in range(len(data)):
|
||||
row = data[i]
|
||||
|
||||
data[i]['DATA_LENGTH'] = round(row['DATA_LENGTH'] or 0, 2)
|
||||
data[i]['INDEX_LENGTH'] = round(row['INDEX_LENGTH'] or 0, 2)
|
||||
data[i]['TOTAL_LENGTH'] = round(row['TOTAL_LENGTH'] or 0, 2)
|
||||
|
||||
AUTO_INCREMENT = row['AUTO_INCREMENT']
|
||||
DATA_TYPE = row['DATA_TYPE']
|
||||
IS_SIGNED = row['IS_SIGNED']
|
||||
RESIDUAL_AUTO_INCREMENT = 0
|
||||
if AUTO_INCREMENT is not None:
|
||||
if DATA_TYPE == 'tinyint':
|
||||
if IS_SIGNED == 0:
|
||||
RESIDUAL_AUTO_INCREMENT = int(255 - AUTO_INCREMENT)
|
||||
if IS_SIGNED == 1:
|
||||
RESIDUAL_AUTO_INCREMENT = int(127 - AUTO_INCREMENT)
|
||||
|
||||
if DATA_TYPE == 'smallint':
|
||||
if IS_SIGNED == 0:
|
||||
RESIDUAL_AUTO_INCREMENT = int(65535 - AUTO_INCREMENT)
|
||||
if IS_SIGNED == 1:
|
||||
RESIDUAL_AUTO_INCREMENT = int(32767 - AUTO_INCREMENT)
|
||||
|
||||
if DATA_TYPE == 'int':
|
||||
if IS_SIGNED == 0:
|
||||
RESIDUAL_AUTO_INCREMENT = int(4294967295 - AUTO_INCREMENT)
|
||||
if IS_SIGNED == 1:
|
||||
RESIDUAL_AUTO_INCREMENT = int(2147483647 - AUTO_INCREMENT)
|
||||
|
||||
if DATA_TYPE == 'mediumint':
|
||||
if IS_SIGNED == 0:
|
||||
RESIDUAL_AUTO_INCREMENT = int(16777215 - AUTO_INCREMENT)
|
||||
if IS_SIGNED == 1:
|
||||
RESIDUAL_AUTO_INCREMENT = int(8388607 - AUTO_INCREMENT)
|
||||
|
||||
if DATA_TYPE == 'bigint':
|
||||
if IS_SIGNED == 0:
|
||||
RESIDUAL_AUTO_INCREMENT = Decimal("18446744073709551615") - Decimal(AUTO_INCREMENT)
|
||||
if IS_SIGNED == 1:
|
||||
RESIDUAL_AUTO_INCREMENT = Decimal("9223372036854775807") - Decimal(AUTO_INCREMENT)
|
||||
else:
|
||||
RESIDUAL_AUTO_INCREMENT = "主键非自增"
|
||||
data[i]['RESIDUAL_AUTO_INCREMENT'] = RESIDUAL_AUTO_INCREMENT
|
||||
return mw.returnData(True, 'ok', data)
|
||||
|
||||
# 查看应用端IP连接数总和
|
||||
def getConnCount(self, args):
|
||||
sid = args['sid']
|
||||
my_instance = self.getInstanceBySid(sid).conn()
|
||||
if my_instance is False:
|
||||
return mw.returnData(False,'无法链接')
|
||||
|
||||
data = my_instance.query(
|
||||
"SELECT user,db,substring_index(HOST,':',1) AS Client_IP,count(1) AS count FROM information_schema.PROCESSLIST "
|
||||
"GROUP BY user,db,substring_index(HOST,':',1) ORDER BY COUNT(1) DESC"
|
||||
)
|
||||
|
||||
for i in range(len(data)):
|
||||
if data[i]['db'] is None:
|
||||
data[i]['db'] = '空'
|
||||
|
||||
if data[i]['Client_IP'] == '':
|
||||
data[i]['Client_IP'] = '空'
|
||||
|
||||
# data2 = my_instance.query("SELECT USER, COUNT(*) as nums FROM information_schema.PROCESSLIST GROUP BY USER ORDER BY COUNT(*) DESC")
|
||||
# print(data)
|
||||
# print(data2)
|
||||
return mw.returnData(True, 'ok', data)
|
||||
|
||||
# 快速找出没有主键的表
|
||||
def getFpkInfo(self, args):
|
||||
sid = args['sid']
|
||||
my_instance = self.getInstanceBySid(sid).conn()
|
||||
if my_instance is False:
|
||||
return mw.returnData(False,'无法链接')
|
||||
|
||||
data = my_instance.query(
|
||||
"""
|
||||
SELECT t.table_schema,
|
||||
t.table_name
|
||||
FROM information_schema.tables t
|
||||
LEFT JOIN information_schema.key_column_usage k
|
||||
ON t.table_schema = k.table_schema
|
||||
AND t.table_name = k.table_name
|
||||
AND k.constraint_name = 'PRIMARY'
|
||||
WHERE t.table_schema NOT IN ('mysql', 'information_schema', 'sys', 'performance_schema')
|
||||
AND k.constraint_name IS NULL
|
||||
AND t.table_type = 'BASE TABLE';
|
||||
"""
|
||||
)
|
||||
return mw.returnData(True, 'ok', data)
|
||||
|
||||
def getLockSql(self, args):
|
||||
sid = args['sid']
|
||||
my_instance = self.getInstanceBySid(sid).conn()
|
||||
if my_instance is False:
|
||||
return mw.returnData(False,'无法链接')
|
||||
|
||||
data = my_instance.query(
|
||||
"""
|
||||
SELECT
|
||||
a.trx_id AS trx_id,
|
||||
a.trx_state AS trx_state,
|
||||
a.trx_started AS trx_started,
|
||||
b.id AS processlist_id,
|
||||
b.info AS info,
|
||||
b.user AS user,
|
||||
b.host AS host,
|
||||
b.db AS db,
|
||||
b.command AS command,
|
||||
b.state AS state,
|
||||
CONCAT('KILL QUERY ', b.id) AS sql_kill_blocking_query
|
||||
FROM
|
||||
information_schema.INNODB_TRX a,
|
||||
information_schema.PROCESSLIST b
|
||||
WHERE
|
||||
a.trx_mysql_thread_id = b.id
|
||||
ORDER BY
|
||||
a.trx_started
|
||||
"""
|
||||
)
|
||||
return mw.returnData(True, 'ok', data)
|
||||
|
||||
def killLockPid(self, args):
|
||||
sid = args['sid']
|
||||
my_instance = self.getInstanceBySid(sid).conn()
|
||||
if my_instance is False:
|
||||
return mw.returnData(False,'无法链接')
|
||||
|
||||
pid = args['pid']
|
||||
my_instance.execute('kill %s' % pid)
|
||||
return mw.returnData(True, '执行成功!')
|
||||
|
||||
def killAllLock(self, args):
|
||||
sid = args['sid']
|
||||
my_instance = self.getInstanceBySid(sid).conn()
|
||||
if my_instance is False:
|
||||
return mw.returnData(False,'无法链接')
|
||||
|
||||
data = self.getLockSql(args)
|
||||
if data['status']:
|
||||
pid_data = data['data']
|
||||
for x in pid_data:
|
||||
cmd = 'kill %s' % x['processlist_id']
|
||||
# print(cmd)
|
||||
my_instance.execute(cmd)
|
||||
return mw.returnData(True, '执行成功!')
|
||||
|
||||
|
||||
|
||||
def getDeadlockInfo(self, args):
|
||||
sid = args['sid']
|
||||
my_instance = self.getInstanceBySid(sid).conn()
|
||||
if my_instance is False:
|
||||
return mw.returnData(False,'无法链接')
|
||||
|
||||
data = my_instance.find("SHOW ENGINE INNODB STATUS")
|
||||
if data is not None:
|
||||
innodb_status = data['Status']
|
||||
deadlock_info = re.search(r"LATEST DETECTED DEADLOCK.*?WE ROLL BACK TRANSACTION\s+\(\d+\)", innodb_status, re.DOTALL)
|
||||
if deadlock_info is None:
|
||||
return mw.returnData(True, 'ok', '无锁表')
|
||||
return mw.returnData(True, 'ok', deadlock_info.group(0))
|
||||
return mw.returnData(True, 'ok', '无锁表')
|
||||
|
||||
def getSlaveStatus(self, args):
|
||||
sid = args['sid']
|
||||
my_instance = self.getInstanceBySid(sid).conn()
|
||||
if my_instance is False:
|
||||
return mw.returnData(False,'无法链接')
|
||||
|
||||
slave_info = my_instance.find('SHOW SLAVE STATUS')
|
||||
if slave_info is None:
|
||||
return mw.returnData(True, 'ok', '未开启从库!')
|
||||
|
||||
msg = ''
|
||||
if slave_info['Auto_Position'] != 1:
|
||||
msg += '你没有开启基于GTID全局事务ID复制,请确保CHANGE MASTER TO MASTER_AUTO_POSITION = 1.\n'
|
||||
|
||||
|
||||
if slave_info['Slave_IO_Running'] == 'Yes' and slave_info['Slave_SQL_Running'] == 'Yes':
|
||||
if slave_info['Seconds_Behind_Master'] == 0:
|
||||
msg = "同步正常,无延迟"
|
||||
return mw.returnData(True, 'ok', msg)
|
||||
else:
|
||||
return mw.returnData(True, 'ok', '同步正常,但有延迟,延迟时间为:%s' % slave_info['Seconds_Behind_Master'])
|
||||
else:
|
||||
msg = '主从复制报错,请检查\nSlave_IO_Running状态值是:%s, | Slave_SQL_Running状态值是:%s\nLast_Error错误信息是:%s\nLast_SQL_Error错误信息是:%s\n' \
|
||||
% (slave_info['Slave_IO_Running'], slave_info['Slave_SQL_Running'], slave_info['Last_Error'], slave_info['Last_SQL_Error'])
|
||||
error_dict = my_instance.find('select LAST_ERROR_NUMBER,LAST_ERROR_MESSAGE,LAST_ERROR_TIMESTAMP from performance_schema.replication_applier_status_by_worker ORDER BY LAST_ERROR_TIMESTAMP desc limit 1')
|
||||
msg += '错误号是:%s \n' % error_dict['LAST_ERROR_NUMBER']
|
||||
msg += '错误信息是:%s \n' % error_dict['LAST_ERROR_MESSAGE']
|
||||
msg += '报错时间是:%s \n' % error_dict['LAST_ERROR_TIMESTAMP']
|
||||
msg += 'MySQL Replication Health is NOT OK!'
|
||||
return mw.returnData(True, 'ok', msg)
|
||||
|
||||
# ---------------------------------- run ----------------------------------
|
||||
|
||||
def get_server_list(args):
|
||||
t = nosqlMySQLCtr()
|
||||
return t.getServerList(args)
|
||||
|
||||
# 获取 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)
|
||||
|
||||
# 查询执行次数最频繁的前N条SQL语句
|
||||
def get_topn_list(args):
|
||||
t = nosqlMySQLCtr()
|
||||
return t.getTopnList(args)
|
||||
|
||||
# MySQL服务器的QPS/TPS/网络带宽指标
|
||||
def get_net_list(args):
|
||||
t = nosqlMySQLCtr()
|
||||
return t.getNetList(args)
|
||||
|
||||
# 查看重复或冗余的索引
|
||||
def get_redundant_indexes(args):
|
||||
t = nosqlMySQLCtr()
|
||||
return t.getRedundantIndexes(args)
|
||||
|
||||
# 删除重复或冗余的索引
|
||||
def redundant_indexes_cmd(args):
|
||||
t = nosqlMySQLCtr()
|
||||
return t.redundantIndexesCmd(args)
|
||||
|
||||
# 统计库里每个表的大小
|
||||
def get_table_info(args):
|
||||
t = nosqlMySQLCtr()
|
||||
return t.getTableInfo(args)
|
||||
|
||||
# 查看应用端IP连接数总和
|
||||
def get_conn_count(args):
|
||||
t = nosqlMySQLCtr()
|
||||
return t.getConnCount(args)
|
||||
|
||||
# 快速找出没有主键的表
|
||||
def get_fpk_info(args):
|
||||
t = nosqlMySQLCtr()
|
||||
return t.getFpkInfo(args)
|
||||
|
||||
# 查看当前锁阻塞的SQL
|
||||
def get_lock_sql(args):
|
||||
t = nosqlMySQLCtr()
|
||||
return t.getLockSql(args)
|
||||
|
||||
# KILL阻塞SQL
|
||||
def kill_lock_pid(args):
|
||||
t = nosqlMySQLCtr()
|
||||
return t.killLockPid(args)
|
||||
|
||||
# KILL阻塞SQL
|
||||
def kill_all_lock(args):
|
||||
t = nosqlMySQLCtr()
|
||||
return t.killAllLock(args)
|
||||
|
||||
# 查看死锁信息
|
||||
def get_deadlock_info(args):
|
||||
t = nosqlMySQLCtr()
|
||||
return t.getDeadlockInfo(args)
|
||||
|
||||
# 查看主从复制信息
|
||||
def get_slave_status(args):
|
||||
t = nosqlMySQLCtr()
|
||||
return t.getSlaveStatus(args)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# 测试
|
||||
def test(args):
|
||||
sid = args['sid']
|
||||
t = nosqlMySQLCtr()
|
||||
print(t.get_options())
|
||||
print("test")
|
||||
return 'ok'
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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 */
|
||||
@@ -1,305 +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/index">点击安装</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>
|
||||
|
||||
|
||||
<div style="float:left;">
|
||||
<button id="mysql_find" type="button" class="btn btn-success btn-sm mr5 ml5" >查找</button>
|
||||
<button id="mysql_common" type="button" class="btn btn-success btn-sm mr5 ml5">常用功能</button>
|
||||
</div>
|
||||
</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>
|
||||
|
||||
<hr style="border:none;" />
|
||||
|
||||
<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>
|
||||
|
||||
<div style="float:right;">
|
||||
|
||||
<button type="button" class="mongodb_find btn btn-success btn-sm mr5 ml5" >
|
||||
<span>查找</span>
|
||||
</button>
|
||||
|
||||
<button type="button" class="mongodb_refresh btn btn-success btn-sm mr5 ml5">
|
||||
<span>刷新</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="mongodb_select" 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>
|
||||
@@ -0,0 +1,350 @@
|
||||
#coding: utf-8
|
||||
# +-------------------------------------------------------------------
|
||||
# | 宝塔Linux面板 x3
|
||||
# +-------------------------------------------------------------------
|
||||
# | Copyright (c) 2015-2017 宝塔软件(http://bt.cn) All rights reserved.
|
||||
# +-------------------------------------------------------------------
|
||||
# | Author: 黄文良 <2879625666@qq.com>
|
||||
# +-------------------------------------------------------------------
|
||||
|
||||
#+--------------------------------------------------------------------
|
||||
#| 自动部署网站
|
||||
#+--------------------------------------------------------------------
|
||||
|
||||
import public,json,os,time;
|
||||
class obj: id=0;
|
||||
class deployment_main:
|
||||
__setupPath = '/www/server/panel/plugin/deployment';
|
||||
__panelPath = '/www/server/panel';
|
||||
logPath = '/www/server/panel/plugin/deployment/speed.json'
|
||||
timeoutCount = 0;
|
||||
oldTime = 0;
|
||||
|
||||
#获取列表
|
||||
def GetList(self,get):
|
||||
self.GetCloudList(get);
|
||||
jsonFile = self.__setupPath + '/list.json';
|
||||
if not os.path.exists(jsonFile): return public.returnMsg(False,'配置文件不存在!');
|
||||
data = {}
|
||||
data = json.loads(public.readFile(jsonFile));
|
||||
|
||||
if not hasattr(get,'type'):
|
||||
get.type = 0;
|
||||
else:
|
||||
get.type = int(get.type)
|
||||
if not hasattr(get,'search'):
|
||||
search = None
|
||||
m = 0
|
||||
else:
|
||||
search = get.search.encode('utf-8').lower();
|
||||
m = 1
|
||||
|
||||
tmp = [];
|
||||
for d in data:
|
||||
i=0;
|
||||
if get.type > 0:
|
||||
if get.type == d['type']: i+=1
|
||||
else:
|
||||
i+=1
|
||||
if search:
|
||||
if d['name'].lower().find(search) != -1: i+=1;
|
||||
if d['title'].lower().find(search) != -1: i+=1;
|
||||
if get.type > 0 and get.type != d['type']: i -= 1;
|
||||
if i>m:tmp.append(d);
|
||||
|
||||
data = tmp;
|
||||
result = {}
|
||||
result['data'] = data;
|
||||
jsonFile = self.__setupPath + '/type.json';
|
||||
if not os.path.exists(jsonFile): self.GetCloudList(get);
|
||||
result['type'] = json.loads(public.readFile(jsonFile));
|
||||
return result;
|
||||
|
||||
#获取插件列表
|
||||
def GetDepList(self,get):
|
||||
jsonFile = self.__setupPath + '/list.json';
|
||||
if not os.path.exists(jsonFile): return public.returnMsg(False,'配置文件不存在!');
|
||||
data = {}
|
||||
data = json.loads(public.readFile(jsonFile));
|
||||
return data;
|
||||
|
||||
|
||||
#从云端获取列表
|
||||
def GetCloudList(self,get):
|
||||
try:
|
||||
import web
|
||||
if not hasattr(web.ctx.session,'package'):
|
||||
downloadUrl = public.get_url() + '/install/lib/plugin/deployment/package.json';
|
||||
tmp = json.loads(public.httpGet(downloadUrl));
|
||||
if not tmp: return public.returnMsg(False,'从云端获取失败!');
|
||||
jsonFile = self.__setupPath + '/list.json';
|
||||
public.writeFile(jsonFile,json.dumps(tmp));
|
||||
|
||||
downloadUrl = public.get_url() + '/install/lib/plugin/deployment/type.json';
|
||||
tmp = json.loads(public.httpGet(downloadUrl));
|
||||
if not tmp: return public.returnMsg(False,'从云端获取失败!');
|
||||
jsonFile = self.__setupPath + '/type.json';
|
||||
public.writeFile(jsonFile,json.dumps(tmp));
|
||||
|
||||
web.ctx.session.package = True
|
||||
return public.returnMsg(True,'更新成功!');
|
||||
return public.returnMsg(True,'无需更新!');
|
||||
except:
|
||||
return public.returnMsg(False,'从云端获取失败!');
|
||||
|
||||
|
||||
|
||||
#添加程序包
|
||||
def AddPackage(self,get):
|
||||
jsonFile = self.__setupPath + '/list.json';
|
||||
if not os.path.exists(jsonFile): return public.returnMsg(False,'配置文件不存在!');
|
||||
|
||||
data = {}
|
||||
data = json.loads(public.readFile(jsonFile));
|
||||
for d in data:
|
||||
if d['name'] == get.dname: return public.returnMsg(False,'您要添加的程序标识已存在!');
|
||||
if d['title'] == get.title: return public.returnMsg(False,'您要添加的程序名称已存在!');
|
||||
|
||||
if hasattr(get,'rewrite'): get.rewrite = True;
|
||||
|
||||
pinfo = {}
|
||||
pinfo['name'] = get.dname;
|
||||
pinfo['title'] = get.title;
|
||||
pinfo['version'] = get.version;
|
||||
pinfo['md5'] = get.md5;
|
||||
pinfo['rewrite'] = get.rewrite;
|
||||
pinfo['php'] = get.php;
|
||||
pinfo['ps'] = get.ps;
|
||||
pinfo['shell'] = get.shell;
|
||||
pinfo['download'] = get.download;
|
||||
data.append(pinfo);
|
||||
public.writeFile(jsonFile,json.dumps(data));
|
||||
return public.returnMsg(True,'添加成功!');
|
||||
|
||||
#删除程序包
|
||||
def DelPackage(self,get):
|
||||
jsonFile = self.__setupPath + '/list.json';
|
||||
if not os.path.exists(jsonFile): return public.returnMsg(False,'配置文件不存在!');
|
||||
|
||||
data = {}
|
||||
data = json.loads(public.readFile(jsonFile));
|
||||
|
||||
tmp = [];
|
||||
for d in data:
|
||||
if d['name'].find(get.dname) != -1: continue;
|
||||
tmp.append(d);
|
||||
|
||||
data = tmp;
|
||||
public.writeFile(jsonFile,json.dumps(data));
|
||||
return public.returnMsg(True,'删除成功!');
|
||||
|
||||
#下载文件
|
||||
def DownloadFile(self,url,filename):
|
||||
try:
|
||||
import urllib,socket
|
||||
socket.setdefaulttimeout(10)
|
||||
self.pre = 0;
|
||||
self.oldTime = time.time();
|
||||
urllib.urlretrieve(url,filename=filename,reporthook= self.DownloadHook)
|
||||
self.WriteLogs(json.dumps({'name':'下载文件','total':0,'used':0,'pre':0,'speed':0}));
|
||||
except:
|
||||
if self.timeoutCount > 5: return;
|
||||
self.timeoutCount += 1
|
||||
time.sleep(5)
|
||||
self.DownloadFile(url,filename)
|
||||
|
||||
#下载文件进度回调
|
||||
def DownloadHook(self,count, blockSize, totalSize):
|
||||
used = count * blockSize
|
||||
pre1 = int((100.0 * used / totalSize))
|
||||
if self.pre != pre1:
|
||||
dspeed = used / (time.time() - self.oldTime);
|
||||
speed = {'name':'下载文件','total':totalSize,'used':used,'pre':self.pre,'speed':dspeed}
|
||||
self.WriteLogs(json.dumps(speed))
|
||||
self.pre = pre1
|
||||
|
||||
#写输出日志
|
||||
def WriteLogs(self,logMsg):
|
||||
fp = open(self.logPath,'w+');
|
||||
fp.write(logMsg)
|
||||
fp.close()
|
||||
|
||||
#一键安装网站程序
|
||||
#param string name 程序名称
|
||||
#param string site_name 网站名称
|
||||
#param string php_version PHP版本
|
||||
def SetupPackage(self,get):
|
||||
name = get.dname
|
||||
site_name = get.site_name;
|
||||
php_version = get.php_version;
|
||||
#取基础信息
|
||||
find = public.M('sites').where('name=?',(site_name,)).field('id,path').find();
|
||||
path = find['path'];
|
||||
|
||||
#获取包信息
|
||||
pinfo = self.GetPackageInfo(name);
|
||||
if not pinfo: return public.returnMsg(False,'指定软件包不存在!');
|
||||
|
||||
#检查本地包
|
||||
self.WriteLogs(json.dumps({'name':'检查软件包','total':0,'used':0,'pre':0,'speed':0}));
|
||||
packageZip = self.__setupPath + '/package/' + name + '.zip';
|
||||
isDownload = False;
|
||||
if os.path.exists(packageZip):
|
||||
md5str = self.GetFileMd5(packageZip);
|
||||
if md5str != pinfo['md5']: isDownload = True;
|
||||
else:
|
||||
isDownload = True;
|
||||
|
||||
#下载文件
|
||||
|
||||
if isDownload:
|
||||
self.WriteLogs(json.dumps({'name':'下载文件','total':0,'used':0,'pre':0,'speed':0}));
|
||||
self.DownloadFile(pinfo['download'], packageZip);
|
||||
if not os.path.exists(packageZip): return public.returnMsg(False,'文件下载失败!');
|
||||
os.system('unzip -o '+packageZip+' -d ' + path + '/');
|
||||
|
||||
#设置权限
|
||||
self.WriteLogs(json.dumps({'name':'设置权限','total':0,'used':0,'pre':0,'speed':0}));
|
||||
os.system('chmod -R 755 ' + path);
|
||||
os.system('chown -R www.www ' + path);
|
||||
if pinfo['chmod'] != "":
|
||||
access = pinfo['chmod'].split(',')
|
||||
for chm in access:
|
||||
tmp = chm.split('|');
|
||||
if len(tmp) != 2: continue;
|
||||
os.system('chmod -R ' + tmp[0] + ' ' + path + '/' + tmp[1]);
|
||||
|
||||
#安装PHP扩展
|
||||
self.WriteLogs(json.dumps({'name':'安装必要的PHP扩展','total':0,'used':0,'pre':0,'speed':0}));
|
||||
if pinfo['ext'] != '':
|
||||
exts = pinfo['ext'].split(',');
|
||||
import files
|
||||
mfile = files.files();
|
||||
for ext in exts:
|
||||
if ext == 'pathinfo':
|
||||
import config
|
||||
con = config.config();
|
||||
get.version = php_version;
|
||||
get.type = 'on';
|
||||
con.setPathInfo(get);
|
||||
else:
|
||||
get.name = ext
|
||||
get.version = php_version
|
||||
get.type = '1';
|
||||
mfile.InstallSoft(get);
|
||||
|
||||
|
||||
#执行额外shell进行依赖安装
|
||||
self.WriteLogs(json.dumps({'name':'执行额外SHELL','total':0,'used':0,'pre':0,'speed':0}));
|
||||
if os.path.exists(path+'/install.sh'):
|
||||
os.system('cd '+path+' && bash ' + 'install.sh');
|
||||
os.system('rm -f ' + path+'/install.sh')
|
||||
|
||||
#是否执行Composer
|
||||
if os.path.exists(path + '/composer.json'):
|
||||
self.WriteLogs(json.dumps({'name':'执行Composer','total':0,'used':0,'pre':0,'speed':0}));
|
||||
if not os.path.exists(path + '/composer.lock'):
|
||||
execPHP = '/www/server/php/' + php_version +'/bin/php';
|
||||
if execPHP:
|
||||
if public.get_url().find('125.88'):
|
||||
os.system('cd ' +path+' && '+execPHP+' /usr/bin/composer config repo.packagist composer https://packagist.phpcomposer.com');
|
||||
import panelSite;
|
||||
phpini = '/www/server/php/' + php_version + '/etc/php.ini'
|
||||
phpiniConf = public.readFile(phpini);
|
||||
phpiniConf = phpiniConf.replace('proc_open,proc_get_status,','');
|
||||
public.writeFile(phpini,phpiniConf);
|
||||
os.system('nohup cd '+path+' && '+execPHP+' /usr/bin/composer install -vvv > /tmp/composer.log 2>&1 &');
|
||||
|
||||
#写伪静态
|
||||
self.WriteLogs(json.dumps({'name':'设置伪静态','total':0,'used':0,'pre':0,'speed':0}));
|
||||
swfile = path + '/nginx.rewrite';
|
||||
if os.path.exists(swfile):
|
||||
rewriteConf = public.readFile(swfile);
|
||||
dwfile = self.__panelPath + '/vhost/rewrite/' + site_name + '.conf';
|
||||
public.writeFile(dwfile,rewriteConf);
|
||||
|
||||
#设置运行目录
|
||||
self.WriteLogs(json.dumps({'name':'设置运行目录','total':0,'used':0,'pre':0,'speed':0}));
|
||||
if pinfo['run'] != '/':
|
||||
import panelSite;
|
||||
siteObj = panelSite.panelSite();
|
||||
mobj = obj();
|
||||
mobj.id = find['id'];
|
||||
mobj.runPath = pinfo['run'];
|
||||
siteObj.SetSiteRunPath(mobj);
|
||||
|
||||
#导入数据
|
||||
self.WriteLogs(json.dumps({'name':'导入数据库','total':0,'used':0,'pre':0,'speed':0}));
|
||||
if os.path.exists(path+'/import.sql'):
|
||||
databaseInfo = public.M('databases').where('pid=?',(find['id'],)).field('username,password').find();
|
||||
if databaseInfo:
|
||||
os.system('/www/server/mysql/bin/mysql -u' + databaseInfo['username'] + ' -p' + databaseInfo['password'] + ' ' + databaseInfo['username'] + ' < ' + path + '/import.sql');
|
||||
os.system('rm -f ' + path + '/import.sql');
|
||||
siteConfigFile = path + '/' + pinfo['config'];
|
||||
if os.path.exists(siteConfigFile):
|
||||
siteConfig = public.readFile(siteConfigFile)
|
||||
siteConfig = siteConfig.replace('BT_DB_USERNAME',databaseInfo['username'])
|
||||
siteConfig = siteConfig.replace('BT_DB_PASSWORD',databaseInfo['password'])
|
||||
siteConfig = siteConfig.replace('BT_DB_NAME',databaseInfo['username'])
|
||||
public.writeFile(siteConfigFile,siteConfig)
|
||||
|
||||
public.serviceReload();
|
||||
self.depTotal(name);
|
||||
self.WriteLogs(json.dumps({'name':'准备部署','total':0,'used':0,'pre':0,'speed':0}));
|
||||
return public.returnMsg(True,pinfo);
|
||||
|
||||
#提交安装统计
|
||||
def depTotal(self,name):
|
||||
try:
|
||||
import urllib2
|
||||
urllib2.urlopen("https://www.bt.cn/Api/depTotal?name=" + name, timeout = 3)
|
||||
return True
|
||||
except:
|
||||
return False;
|
||||
|
||||
#获取进度
|
||||
def GetSpeed(self,get):
|
||||
try:
|
||||
if not os.path.exists(self.logPath): public.returnMsg(False,'当前没有部署任务!');
|
||||
return json.loads(public.readFile(self.logPath));
|
||||
except:
|
||||
return {'name':'准备部署','total':0,'used':0,'pre':0,'speed':0}
|
||||
|
||||
#获取包信息
|
||||
def GetPackageInfo(self,name):
|
||||
data = self.GetDepList(None);
|
||||
if not data: return False;
|
||||
downUrl = public.get_url() + '/install/package';
|
||||
for info in data:
|
||||
if info['name'] == name:
|
||||
info['download'] = info['download'].replace('{Download}',downUrl);
|
||||
return info;
|
||||
return False;
|
||||
|
||||
#检查指定包是否存在
|
||||
def CheckPackageExists(self,name):
|
||||
data = self.GetDepList(None);
|
||||
if not data: return False;
|
||||
for info in data:
|
||||
if info['name'] == name: return True;
|
||||
|
||||
return False;
|
||||
|
||||
#文件的MD5值
|
||||
def GetFileMd5(self,filename):
|
||||
if not os.path.isfile(filename): return False;
|
||||
import hashlib;
|
||||
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 GetSiteId(self,get):
|
||||
return public.M('sites').where('name=?',(get.webname,)).getField('id');
|
||||
|
||||
|
After Width: | Height: | Size: 553 B |
|
After Width: | Height: | Size: 553 B |
@@ -0,0 +1,415 @@
|
||||
<style>
|
||||
.disable {
|
||||
background-color: #ededed;
|
||||
}
|
||||
.onekeyCode .table > tbody > tr > td{
|
||||
line-height:1.7;
|
||||
}
|
||||
.oneKeysearchBar{
|
||||
position: absolute;
|
||||
top: 10px;
|
||||
right:15px;
|
||||
z-index: 20;
|
||||
}
|
||||
.success-msg .suc-con {
|
||||
float: right;
|
||||
width: 280px;
|
||||
}
|
||||
.bt-progress {
|
||||
background-color: #e2e2e2;
|
||||
border-radius: 8px;
|
||||
height: 16px;
|
||||
line-height: 16px;
|
||||
position: relative;
|
||||
margin:5px 0;
|
||||
}
|
||||
.bt-progress-bar {
|
||||
background-color: #5ab76c;
|
||||
border-radius: 8px;
|
||||
height: 16px;
|
||||
max-width: 100%;
|
||||
position: absolute;
|
||||
text-align: right;
|
||||
transition: all 0.3s ease 0s;
|
||||
width: 0;
|
||||
}
|
||||
.bt-progress-text {
|
||||
font-size: 12px;
|
||||
color: #fff;
|
||||
padding: 0 10px;
|
||||
position: static;
|
||||
}
|
||||
.onekey-menu-sub{
|
||||
background-color: #fff;
|
||||
border-bottom: 1px solid #ccc;
|
||||
height: 50px;
|
||||
line-height: 50px;
|
||||
}
|
||||
.onekey-menu-sub span{
|
||||
height: 50px;
|
||||
padding: 0 15px;
|
||||
cursor: pointer;
|
||||
display: inline-block;
|
||||
font-size: 14px;
|
||||
}
|
||||
.onekey-menu-sub .on {
|
||||
border-bottom: 2px solid #20a53a;
|
||||
color: #20a53a;
|
||||
font-weight: bold;
|
||||
}
|
||||
</style>
|
||||
<div class="oneKeysearchBar">
|
||||
<input class="searchInput bt-input-text" type="text" placeholder="请输入检索条件" value="" style="width:140px" />
|
||||
<button class="btn btn-default btn-sm va0" onclick="GetSrcList();">查找</button>
|
||||
</div>
|
||||
<div class="onekey-menu-sub mlr15 onekey-type"></div>
|
||||
<div class="divtable pd15 onekeyCode" style="max-height:500px; overflow:auto">
|
||||
<table class="table table-hover" width="100%">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>名称</th>
|
||||
<th>版本</th>
|
||||
<th>简介</th>
|
||||
<th>支持PHP版本</th>
|
||||
<th style="text-align: right;" width="80">操作</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="zipBody"></tbody>
|
||||
</table>
|
||||
</div>
|
||||
<script type="javascript/text">
|
||||
$('.layui-layer-page').css({'width':'760px'});
|
||||
var openId = add = null;
|
||||
function AddDeployment(maction){
|
||||
if(maction == 1){
|
||||
var pdata = 'title=' + $("input[name='title']").val()
|
||||
+'&dname=' + $("input[name='name']").val()
|
||||
+'&ps=' + $("input[name='ps']").val()
|
||||
+'&version=' + $("input[name='version']").val()
|
||||
+'&rewrite=' + ($("input[name='rewrite']").attr('checked')?1:0)
|
||||
+'&shell=' + ($("input[name='shell']").attr('checked')?1:0)
|
||||
+'&php=' + $("input[name='php']").val()
|
||||
+'&md5=' + $("input[name='md5']").val()
|
||||
+'&download=' + $("input[name='download']").val()
|
||||
var loadT = layer.msg('正在提交 <img src="/static/img/ing.gif">',{icon:16,time:0,shade: [0.3, '#000']});
|
||||
$.post('/plugin?action=a&name=deployment&s=AddPackage',pdata,function(rdata){
|
||||
layer.close(loadT);
|
||||
layer.msg(rdata.msg,{icon:rdata.status?1:5});
|
||||
if(rdata.status) {
|
||||
GetSrcList();
|
||||
layer.close(openId);
|
||||
}
|
||||
});
|
||||
|
||||
return;
|
||||
}
|
||||
openId = layer.open({
|
||||
type: 1,
|
||||
skin: 'demo-class',
|
||||
area: '480px',
|
||||
title: '添加源码包',
|
||||
closeBtn: 2,
|
||||
shift: 5,
|
||||
shadeClose: false,
|
||||
content:'标题:<input type="text" name="title"><br>\
|
||||
标识:<input type="text" name="name"><br>\
|
||||
描述:<input type="text" name="ps"><br>\
|
||||
版本:<input type="text" name="version"><br>\
|
||||
是否写伪静态:<input type="checkbox" name="rewrite"><br>\
|
||||
是否执行安装脚本:<input type="checkbox" name="shell"><br>\
|
||||
支持的PHP版本:<input type="text" name="php"><br>\
|
||||
md5:<input type="text" name="md5">\
|
||||
下载地址:<input type="text" name="download"><br>\
|
||||
<button class="btn btn-default btn-sm" onclick="AddDeployment(1);">提交</button>'
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
$(".searchInput").keyup(function(e){
|
||||
if(e.keyCode == 13){
|
||||
GetSrcList();
|
||||
}
|
||||
});
|
||||
|
||||
function AddSite(codename){
|
||||
var array;
|
||||
var str="";
|
||||
var domainlist='';
|
||||
var domain = array = $("#mainDomain").val().split("\n");
|
||||
var Webport=[];
|
||||
var checkDomain = domain[0].split('.');
|
||||
if(checkDomain.length < 1){
|
||||
layer.msg('域名格式不正确,请重新输入!',{icon:2});
|
||||
return;
|
||||
}
|
||||
for(var i=1; i<domain.length; i++){
|
||||
domainlist += '"'+domain[i]+'",';
|
||||
}
|
||||
Webport = domain[0].split(":")[1];//主域名端口
|
||||
if(Webport==undefined){
|
||||
Webport="80";
|
||||
}
|
||||
domainlist = domainlist.substring(0,domainlist.length-1);//子域名json
|
||||
mainDomain = domain[0].split(':')[0];
|
||||
domain ='{"domain":"'+domain[0]+'","domainlist":['+domainlist+'],"count":'+domain.length+'}';//拼接joson
|
||||
var php_version = $("select[name='version']").val();
|
||||
var loadT = layer.msg('正在创建站点 <img src="/static/img/ing.gif">',{icon:16,time:0,shade: [0.3, "#000"]})
|
||||
var data = $("#addweb").serialize()+"&port="+Webport+"&webname="+domain+'&ftp=false&sql=true&address=localhost&codeing=utf8&version='+php_version;
|
||||
$.post('/site?action=AddSite', data, function(ret) {
|
||||
layer.close(loadT)
|
||||
if(!ret.siteStatus){
|
||||
layer.msg(ret.msg,{icon:5});
|
||||
return;
|
||||
}
|
||||
layer.close(add)
|
||||
var sqlData = '';
|
||||
if (ret.databaseStatus) {
|
||||
sqlData = "<p class='p1'>数据库账号资料</p>\
|
||||
<p><span>数据库名:</span><strong>" + ret.databaseUser + "</strong></p>\
|
||||
<p><span>用户:</span><strong>" + ret.databaseUser + "</strong></p>\
|
||||
<p><span>密码:</span><strong>" + ret.databasePass + "</strong></p>\
|
||||
"
|
||||
}
|
||||
var pdata = 'dname=' + codename + '&site_name=' + mainDomain + '&php_version='+php_version;
|
||||
var loadT = layer.msg('<div class="depSpeed">正在提交 <img src="/static/img/ing.gif"></div>',{icon:16,time:0,shade: [0.3, "#000"]});
|
||||
|
||||
setTimeout(function(){
|
||||
GetSpeed();
|
||||
},2000);
|
||||
|
||||
$.post('/plugin?action=a&name=deployment&s=SetupPackage',pdata,function(rdata){
|
||||
layer.close(loadT)
|
||||
if(!rdata.status){
|
||||
layer.msg('源码下载失败!',{icon:5});
|
||||
return;
|
||||
}
|
||||
|
||||
if(rdata.msg.username != ''){
|
||||
sqlData = "<p class='p1'>已成功部署,无需安装,请登录修改默认账号密码</p>\
|
||||
<p><span>用户:</span><strong>" + rdata.msg.username + "</strong></p>\
|
||||
<p><span>密码:</span><strong>" + rdata.msg.password + "</strong></p>\
|
||||
"
|
||||
}
|
||||
sqlData += "<p><span>访问站点:</span><a class='btlink' href='http://" + mainDomain + rdata.msg.install + "' target='_blank'>http://" + mainDomain + rdata.msg.install + "</a></p>";
|
||||
|
||||
layer.open({
|
||||
type: 1,
|
||||
area: '600px',
|
||||
title: '已成功部署'+codename,
|
||||
closeBtn:2,
|
||||
shadeClose: false,
|
||||
content: "<div class='success-msg'>\
|
||||
<div class='pic'><img src='/static/img/success-pic.png'></div>\
|
||||
<div class='suc-con'>\
|
||||
" + sqlData + "\
|
||||
</div>\
|
||||
</div>",
|
||||
});
|
||||
if ($(".success-msg").height() < 150) {
|
||||
$(".success-msg").find("img").css({
|
||||
"width": "150px",
|
||||
"margin-top": "30px"
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
function GetSpeed(){
|
||||
if(!$('.depSpeed')) return;
|
||||
$.get('/plugin?action=a&name=deployment&s=GetSpeed',function(speed){
|
||||
if(speed.status === false) return;
|
||||
if(speed.name == '下载文件'){
|
||||
speed = '<p>正在'+speed.name+' <img src="/static/img/ing.gif"></p>\
|
||||
<div class="bt-progress"><div class="bt-progress-bar" style="width:'+speed.pre+'%"><span class="bt-progress-text">'+speed.pre+'%</span></div></div>\
|
||||
<p class="f12 c9"><span class="pull-left">'+ToSize(speed.used)+'/'+ToSize(speed.total)+'</span><span class="pull-right">'+ToSize(speed.speed)+'/s</span></p>';
|
||||
$('.depSpeed').prev().hide();
|
||||
$('.depSpeed').css({"margin-left":"-37px","width":"380px"});
|
||||
$('.depSpeed').parents(".layui-layer").css({"margin-left":"-100px"});
|
||||
}else{
|
||||
speed = '<p>'+speed.name+'</p>';
|
||||
$('.depSpeed').prev().show();
|
||||
$('.depSpeed').removeAttr("style");
|
||||
$('.depSpeed').parents(".layui-layer").css({"margin-left":"0"});
|
||||
}
|
||||
|
||||
$('.depSpeed').html(speed);
|
||||
setTimeout(function(){
|
||||
GetSpeed();
|
||||
},1000);
|
||||
});
|
||||
}
|
||||
|
||||
function onekeyCodeSite(codename,versions){
|
||||
$.post('/site?action=GetPHPVersion',function(rdata){
|
||||
var php_version = "";
|
||||
var n = 0;
|
||||
for(var i=rdata.length-1;i>=0;i--){
|
||||
if(versions.indexOf(rdata[i].version) != -1){
|
||||
php_version += "<option value='"+rdata[i].version+"'>"+rdata[i].name+"</option>";
|
||||
n++;
|
||||
}
|
||||
}
|
||||
|
||||
if(n == 0){
|
||||
layer.msg('缺少被支持的PHP版本,请安装!',{icon:5});
|
||||
return;
|
||||
}
|
||||
var con='<form class="bt-form pd20 pb70" id="addweb">\
|
||||
<div class="line"><span class="tname">域名</span>\
|
||||
<div class="info-r c4"><textarea id="mainDomain" class="bt-input-text" name="webname" style="width:398px;height:100px;line-height:22px"></textarea>\
|
||||
<div class="placeholder c9" style="top:10px;left:10px">每行填写一个域名,默认为80端口<br>泛解析添加方法 *.domain.com<br>如另加端口格式为 www.domain.com:88</div>\
|
||||
</div>\
|
||||
</div>\
|
||||
<div class="line"><span class="tname">备注</span>\
|
||||
<div class="info-r c4"><input id="Wbeizhu" class="bt-input-text" name="ps" placeholder="网站备注" style="width:398px" type="text"> </div>\
|
||||
</div>\
|
||||
<div class="line"><span class="tname">根目录</span>\
|
||||
<div class="info-r c4"><input id="inputPath" class="bt-input-text mr5" name="path" value="/www/wwwroot/" placeholder="网站根目录" style="width:398px" type="text"><span class="glyphicon glyphicon-folder-open cursor" onclick="ChangePath(\'inputPath\')"></span> </div>\
|
||||
</div>\
|
||||
<div class="line"><span class="tname">数据库</span>\
|
||||
<div class="info-r c4">\
|
||||
<input id="datauser" class="bt-input-text" name="datauser" placeholder="用户名/数据库名" style="width:190px;margin-right:13px" type="text">\
|
||||
<input id="datapassword" class="bt-input-text" name="datapassword" placeholder="密码" style="width:190px" type="text">\
|
||||
</div>\
|
||||
</div>\
|
||||
<div class="line"><span class="tname">源码</span>\
|
||||
<input class="bt-input-text mr5 disable" name="code" style="width:100px" value="'+codename+'" disabled>\
|
||||
<span class="c9">准备为你部署的源码程序</span>\
|
||||
</div>\
|
||||
<div class="line"><span class="tname">PHP版本</span>\
|
||||
<select class="bt-input-text mr5" name="version" id="c_k3" style="width:100px">\
|
||||
'+php_version+'\
|
||||
</select>\
|
||||
<span class="c9">请选择源码程序支持的php版本</span>\
|
||||
</div>\
|
||||
<div class="bt-form-submit-btn">\
|
||||
<button type="button" class="btn btn-danger btn-sm onekeycodeclose">取消</button>\
|
||||
<button type="button" class="btn btn-success btn-sm" onclick="AddSite(\''+codename+'\')">提交</button>\
|
||||
</div>\
|
||||
</from>';
|
||||
add = layer.open({
|
||||
type: 1,
|
||||
title: "宝塔一键部署"+codename,
|
||||
area: '560px',
|
||||
closeBtn: 2,
|
||||
shadeClose: false,
|
||||
content:con
|
||||
});
|
||||
var placeholder = "<div class='placeholder c9' style='top:10px;left:10px'>每行填写一个域名,默认为80端口<br>泛解析添加方法 *.domain.com<br>如另加端口格式为 www.domain.com:88</div>";
|
||||
$(".onekeycodeclose").click(function(){
|
||||
layer.close(add);
|
||||
});
|
||||
$('#mainDomain').after(placeholder);
|
||||
$(".placeholder").click(function(){
|
||||
$(this).hide();
|
||||
$('#mainDomain').focus();
|
||||
})
|
||||
$('#mainDomain').focus(function() {
|
||||
$(".placeholder").hide();
|
||||
});
|
||||
|
||||
$('#mainDomain').blur(function() {
|
||||
if($(this).val().length==0){
|
||||
$(".placeholder").show();
|
||||
}
|
||||
});
|
||||
//FTP账号数据绑定域名
|
||||
$('#mainDomain').on('input', function() {
|
||||
var defaultPath = '/www/wwwroot';
|
||||
var array;
|
||||
var res,ress;
|
||||
var str = $(this).val();
|
||||
var len = str.replace(/[^\x00-\xff]/g, "**").length;
|
||||
array = str.split("\n");
|
||||
ress =array[0].split(":")[0];
|
||||
res = ress.replace(new RegExp(/([-.])/g), '_');
|
||||
if(res.length > 15) res = res.substr(0,15);
|
||||
if($("#inputPath").val().substr(0,defaultPath.length) == defaultPath) $("#inputPath").val(defaultPath+'/'+ress);
|
||||
if(!isNaN(res.substr(0,1))) res = "sql"+res;
|
||||
if(res.length > 15) res = res.substr(0,15);
|
||||
$("#Wbeizhu").val(ress);
|
||||
$("#datauser").val(res);
|
||||
})
|
||||
$('#Wbeizhu').on('input', function() {
|
||||
var str = $(this).val();
|
||||
var len = str.replace(/[^\x00-\xff]/g, "**").length;
|
||||
if (len > 20) {
|
||||
str = str.substring(0, 20);
|
||||
$(this).val(str);
|
||||
layer.msg('不要超出20个字符', {
|
||||
icon: 0
|
||||
});
|
||||
}
|
||||
})
|
||||
//获取当前时间时间戳,截取后6位
|
||||
var timestamp = new Date().getTime().toString();
|
||||
var dtpw = timestamp.substring(7);
|
||||
$("#datauser").val("sql" + dtpw);
|
||||
$("#datapassword").val(_getRandomString(10));
|
||||
});
|
||||
}
|
||||
|
||||
//生成n位随机密码
|
||||
function _getRandomString(len) {
|
||||
len = len || 32;
|
||||
var $chars = 'ABCDEFGHJKMNPQRSTWXYZabcdefhijkmnprstwxyz2345678'; // 默认去掉了容易混淆的字符oOLl,9gq,Vv,Uu,I1
|
||||
var maxPos = $chars.length;
|
||||
var pwd = '';
|
||||
for (i = 0; i < len; i++) {
|
||||
pwd += $chars.charAt(Math.floor(Math.random() * maxPos));
|
||||
}
|
||||
return pwd;
|
||||
}
|
||||
function GetSrcList(){
|
||||
var loadT = layer.msg('正在获取列表 <img src="/static/img/ing.gif">',{icon:16,time:0,shade: [0.3, '#000']});
|
||||
var search = $(".searchInput").val();
|
||||
if(search != ''){
|
||||
search = '&search=' + search+'&';
|
||||
}
|
||||
var type = '';
|
||||
var istype = getCookie('depType');
|
||||
if(istype == 'undefined' || istype == 'null' || !istype) {
|
||||
istype = '0';
|
||||
}
|
||||
type = '&type='+istype;
|
||||
|
||||
$.get('/plugin?action=a&name=deployment&s=GetList' + search + type,function(mlist){
|
||||
layer.close(loadT);
|
||||
|
||||
tbody = '';
|
||||
for(var i=0;i<mlist.type.length;i++){
|
||||
var c = '';
|
||||
if(istype == mlist.type[i].type){
|
||||
c = 'class="on"';
|
||||
}
|
||||
tbody += '<span typeid="'+mlist.type[i].type+'" '+c+'>'+mlist.type[i].title+'</span>';
|
||||
}
|
||||
|
||||
$(".onekey-type").html(tbody);
|
||||
|
||||
var zbody = '';
|
||||
for(var i=mlist.data.length-1;i>=0;i--){
|
||||
zbody += '<tr>'
|
||||
+'<td>'+mlist.data[i].title+'</td>'
|
||||
+'<td>'+mlist.data[i].version+'</td>'
|
||||
+'<td>'+mlist.data[i].ps+'</td>'
|
||||
+'<td>'+mlist.data[i].php+'</td>'
|
||||
+'<td class="text-right"><a href="javascript:onekeyCodeSite(\''+mlist.data[i].name+'\',\''+mlist.data[i].php+'\');" class="btlink">一键部署</a></td>'
|
||||
+'</tr>'
|
||||
}
|
||||
$("#zipBody").html(zbody);
|
||||
$(".searchInput").val('');
|
||||
|
||||
$(".onekey-menu-sub span").click(function(){
|
||||
setCookie('depType',$(this).attr('typeid'));
|
||||
$(this).addClass("on").siblings().removeClass("on");
|
||||
GetSrcList();
|
||||
});
|
||||
});
|
||||
}
|
||||
GetSrcList();
|
||||
</script>
|
||||
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"title":"宝塔一键部署源码",
|
||||
"tip":"lib",
|
||||
"name":"deployment",
|
||||
"type":"扩展",
|
||||
"ps":"快速部署常用程序!",
|
||||
"versions":"1.1",
|
||||
"shell":"deployment.sh",
|
||||
"checks":"/www/server/panel/plugin/deployment",
|
||||
"author":"宝塔",
|
||||
"home":"http://www.bt.cn/bbs",
|
||||
"date":"2017-07-26",
|
||||
"default":false,
|
||||
"display":1,
|
||||
"pid":"2"
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
#!/bin/bash
|
||||
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
|
||||
export PATH
|
||||
install_tmp='/tmp/bt_install.pl'
|
||||
CN='125.88.182.172'
|
||||
HK='download.bt.cn'
|
||||
HK2='103.224.251.67'
|
||||
US='174.139.221.74'
|
||||
sleep 0.5;
|
||||
CN_PING=`ping -c 1 -w 1 $CN|grep time=|awk '{print $7}'|sed "s/time=//"`
|
||||
HK_PING=`ping -c 1 -w 1 $HK|grep time=|awk '{print $7}'|sed "s/time=//"`
|
||||
HK2_PING=`ping -c 1 -w 1 $HK2|grep time=|awk '{print $7}'|sed "s/time=//"`
|
||||
US_PING=`ping -c 1 -w 1 $US|grep time=|awk '{print $7}'|sed "s/time=//"`
|
||||
|
||||
echo "$HK_PING $HK" > ping.pl
|
||||
echo "$HK2_PING $HK2" >> ping.pl
|
||||
echo "$US_PING $US" >> ping.pl
|
||||
echo "$CN_PING $CN" >> ping.pl
|
||||
nodeAddr=`sort -V ping.pl|sed -n '1p'|awk '{print $2}'`
|
||||
if [ "$nodeAddr" == "" ];then
|
||||
nodeAddr=$HK2
|
||||
fi
|
||||
|
||||
download_Url=http://$nodeAddr:5880
|
||||
|
||||
Install_deployment()
|
||||
{
|
||||
mkdir -p /www/server/panel/plugin/deployment
|
||||
mkdir -p /www/server/panel/plugin/deployment/package
|
||||
echo '正在安装脚本文件...' > $install_tmp
|
||||
wget -O /www/server/panel/plugin/deployment/deployment_main.py $download_Url/install/lib/plugin/deployment/deployment_main.py -T 5
|
||||
wget -O /www/server/panel/plugin/deployment/index.html $download_Url/install/lib/plugin/deployment/index.html -T 5
|
||||
wget -O /www/server/panel/plugin/deployment/info.json $download_Url/install/lib/plugin/deployment/info.json -T 5
|
||||
wget -O /www/server/panel/plugin/deployment/list.json $download_Url/install/lib/plugin/deployment/list.json -T 5
|
||||
wget -O /www/server/panel/plugin/deployment/type.json $download_Url/install/lib/plugin/deployment/type.json -T 5
|
||||
wget -O /www/server/panel/plugin/deployment/icon.png $download_Url/install/lib/plugin/deployment/icon.png -T 5
|
||||
wget -O /www/server/panel/static/img/soft_ico/ico-deployment.png $download_Url/install/lib/plugin/deployment/icon.png -T 5
|
||||
echo '安装完成' > $install_tmp
|
||||
}
|
||||
|
||||
Uninstall_deployment()
|
||||
{
|
||||
rm -rf /www/server/panel/plugin/deployment
|
||||
}
|
||||
|
||||
|
||||
action=$1
|
||||
if [ "${1}" == 'install' ];then
|
||||
Install_deployment
|
||||
else
|
||||
Uninstall_deployment
|
||||
fi
|
||||
@@ -0,0 +1,495 @@
|
||||
[
|
||||
{
|
||||
"name":"niucms",
|
||||
"title":"NIUCMS",
|
||||
"type":1,
|
||||
"version":"3.6",
|
||||
"md5":"f5ea5a231a7d25d66ee324d60dcdd4f1",
|
||||
"ext":"pathinfo",
|
||||
"chmod":"",
|
||||
"install":"/install/index.php",
|
||||
"php":"53,54,55,56",
|
||||
"ps":"智慧城市生活系统(商业软件)",
|
||||
"config":"",
|
||||
"username":"",
|
||||
"password":"",
|
||||
"run":"/",
|
||||
"download":"{Download}/niucms.zip"
|
||||
},
|
||||
{
|
||||
"name":"tpt-blog",
|
||||
"title":"Tpt-Blog",
|
||||
"type":4,
|
||||
"version":"1.1",
|
||||
"md5":"aa195fece581582182e17143f53b2dfc",
|
||||
"ext":"pathinfo",
|
||||
"chmod":"",
|
||||
"install":"/admin.php",
|
||||
"php":"53,54,55,56,70,71,72",
|
||||
"ps":"免费开源的博客程序",
|
||||
"config":"/Application/Common/Conf/db.php",
|
||||
"username":"admin",
|
||||
"password":"admin<br>口令:123456",
|
||||
"run":"/",
|
||||
"download":"{Download}/tpt-blog_auto.zip"
|
||||
},
|
||||
{
|
||||
"name":"phpcms",
|
||||
"title":"PHPCMS",
|
||||
"type":1,
|
||||
"version":"9.6.3",
|
||||
"md5":"4de608ae7b74175dd4513911ef7ebf78",
|
||||
"ext":"pathinfo",
|
||||
"chmod":"",
|
||||
"install":"/install",
|
||||
"php":"52,53,54,55,56,70",
|
||||
"ps":"PHPCMS内容管理系统",
|
||||
"config":"",
|
||||
"username":"",
|
||||
"password":"",
|
||||
"run":"/",
|
||||
"download":"{Download}/phpcms.zip"
|
||||
},
|
||||
{
|
||||
"name":"ecshop_2.7",
|
||||
"title":"EcShop-2.7",
|
||||
"type":2,
|
||||
"version":"2.7.3",
|
||||
"md5":"9115294f5a458249a5f2c793eab3639c",
|
||||
"ext":"",
|
||||
"chmod":"",
|
||||
"install":"/install",
|
||||
"php":"52,53",
|
||||
"ps":"B2C独立网店系统",
|
||||
"config":"",
|
||||
"username":"",
|
||||
"password":"",
|
||||
"run":"/",
|
||||
"download":"{Download}/ecshop_2.7.zip"
|
||||
},
|
||||
{
|
||||
"name":"ecshop_3.6",
|
||||
"title":"EcShop-3.6",
|
||||
"type":2,
|
||||
"version":"3.6.0",
|
||||
"md5":"38fdc7fe8faf67edd91ebfbb8291cbc7",
|
||||
"ext":"",
|
||||
"chmod":"",
|
||||
"install":"/install",
|
||||
"php":"53,54,55,56",
|
||||
"ps":"B2C独立网店系统",
|
||||
"config":"",
|
||||
"username":"",
|
||||
"password":"",
|
||||
"run":"/ecshop",
|
||||
"download":"{Download}/ecshop_3.6.zip"
|
||||
},
|
||||
{
|
||||
"name":"emlog",
|
||||
"title":"Emlog",
|
||||
"type":4,
|
||||
"version":"5.3.1",
|
||||
"md5":"cecaa55d8d9644e8d8b7098c836af1ad",
|
||||
"ext":"",
|
||||
"chmod":"",
|
||||
"install":"",
|
||||
"php":"53,54,55,56",
|
||||
"ps":"博客程序",
|
||||
"config":"",
|
||||
"username":"",
|
||||
"password":"",
|
||||
"run":"/",
|
||||
"download":"{Download}/emlog.zip"
|
||||
},
|
||||
{
|
||||
"name":"empirecms",
|
||||
"title":"帝国CMS",
|
||||
"type":1,
|
||||
"version":"7.2",
|
||||
"md5":"07bb4ffe56c939170ff080744fc13dd8",
|
||||
"ext":"",
|
||||
"chmod":"",
|
||||
"install":"/e/install",
|
||||
"php":"52,53,54,55,56",
|
||||
"ps":"帝国内容管理系统",
|
||||
"config":"",
|
||||
"username":"",
|
||||
"password":"",
|
||||
"run":"/",
|
||||
"download":"{Download}/empirecms.zip"
|
||||
},
|
||||
{
|
||||
"name":"jieqiarticle170",
|
||||
"title":"杰奇1.7",
|
||||
"type":1,
|
||||
"version":"1.7",
|
||||
"md5":"e5168ea7b27f5c6de2a01dbf40f772b7",
|
||||
"ext":"",
|
||||
"chmod":"",
|
||||
"install":"/install",
|
||||
"php":"52",
|
||||
"ps":"小说连载程序",
|
||||
"config":"",
|
||||
"username":"",
|
||||
"password":"",
|
||||
"run":"/",
|
||||
"download":"{Download}/jieqiarticle170.zip"
|
||||
},
|
||||
{
|
||||
"name":"codeigniter",
|
||||
"title":"CI框架",
|
||||
"type":6,
|
||||
"version":"3.1.5",
|
||||
"md5":"47a615ef55910a908aff367dc5f59a70",
|
||||
"ext":"pathinfo",
|
||||
"chmod":"",
|
||||
"install":"",
|
||||
"php":"53,54,55,56,70,71,72",
|
||||
"ps":"知名PHP框架",
|
||||
"config":"",
|
||||
"username":"",
|
||||
"password":"",
|
||||
"run":"/public",
|
||||
"download":"{Download}/codeIgniter.zip"
|
||||
},
|
||||
{
|
||||
"name":"laravel",
|
||||
"title":"Laravel",
|
||||
"type":6,
|
||||
"version":"5.4",
|
||||
"md5":"988566e8982e835ffa6f7d9ad14b5330",
|
||||
"ext":"pathinfo",
|
||||
"chmod":"",
|
||||
"install":"",
|
||||
"php":"56,70,71,72",
|
||||
"ps":"优雅的PHP开发框架",
|
||||
"config":"",
|
||||
"username":"",
|
||||
"password":"",
|
||||
"run":"/public",
|
||||
"download":"{Download}/laravel.zip"
|
||||
},
|
||||
{
|
||||
"name":"thinkphp_3.2",
|
||||
"title":"ThinkPHP-3.2",
|
||||
"type":6,
|
||||
"version":"3.2.3",
|
||||
"md5":"6cd0e16bf478e7df2fc62b6819eebcce",
|
||||
"ext":"pathinfo",
|
||||
"chmod":"",
|
||||
"install":"",
|
||||
"php":"53,54,55,56",
|
||||
"ps":"国内知名PHP框架",
|
||||
"config":"",
|
||||
"username":"",
|
||||
"password":"",
|
||||
"run":"/",
|
||||
"download":"{Download}/thinkphp-3.2.zip"
|
||||
},
|
||||
{
|
||||
"name":"thinkphp_5.0",
|
||||
"title":"ThinkPHP-5.0",
|
||||
"type":6,
|
||||
"version":"5.0.15",
|
||||
"md5":"830fd878a802237a968ba36fceb1ae92",
|
||||
"ext":"pathinfo",
|
||||
"chmod":"",
|
||||
"install":"",
|
||||
"php":"54,55,56,70,71,72",
|
||||
"ps":"国内知名PHP框架",
|
||||
"config":"",
|
||||
"username":"",
|
||||
"password":"",
|
||||
"run":"/public",
|
||||
"download":"{Download}/thinkphp-5.0.zip"
|
||||
},
|
||||
{
|
||||
"name":"dedecms",
|
||||
"title":"织梦CMS",
|
||||
"type":1,
|
||||
"version":"v5.7SP2",
|
||||
"md5":"6f64be35597dbd2c51b1954f74cfb3ad",
|
||||
"ext":"",
|
||||
"chmod":"",
|
||||
"install":"/install",
|
||||
"php":"52,53,54,55,56,70,71,72",
|
||||
"ps":"国内知名内容管理系统",
|
||||
"config":"",
|
||||
"username":"",
|
||||
"password":"",
|
||||
"run":"/",
|
||||
"download":"{Download}/dedecms.zip"
|
||||
},
|
||||
{
|
||||
"name":"discuz_x3.4",
|
||||
"title":"Discuz 3.4",
|
||||
"type":3,
|
||||
"version":"x3.4",
|
||||
"md5":"44c4250ebdf5fa796712f336fbd2ef42",
|
||||
"ext":"pathinfo",
|
||||
"chmod":"",
|
||||
"install":"/install",
|
||||
"php":"52,53,54,55,56,70,71,72",
|
||||
"ps":"国内知名论坛程序",
|
||||
"config":"",
|
||||
"username":"",
|
||||
"password":"",
|
||||
"run":"/",
|
||||
"download":"{Download}/discuz_x3.4.zip"
|
||||
},
|
||||
{
|
||||
"name":"discuz",
|
||||
"title":"Discuz",
|
||||
"type":3,
|
||||
"version":"x3.3",
|
||||
"md5":"b31e43927daa00c7c3d92818298943fa",
|
||||
"ext":"pathinfo",
|
||||
"chmod":"",
|
||||
"install":"/install",
|
||||
"php":"52,53,54,55,56,70,71,72",
|
||||
"ps":"国内知名论坛程序",
|
||||
"config":"",
|
||||
"username":"",
|
||||
"password":"",
|
||||
"run":"/",
|
||||
"download":"{Download}/discuz.zip"
|
||||
},
|
||||
{
|
||||
"name":"typecho",
|
||||
"title":"Typecho",
|
||||
"type":4,
|
||||
"version":"1.1",
|
||||
"md5":"8b638551974e98994401cda253a3f836",
|
||||
"ext":"pathinfo",
|
||||
"chmod":"",
|
||||
"install":"",
|
||||
"php":"54,55,56,70,71,72",
|
||||
"ps":"轻量级博客程序",
|
||||
"config":"",
|
||||
"username":"",
|
||||
"password":"",
|
||||
"run":"/",
|
||||
"download":"{Download}/typecho.zip"
|
||||
},
|
||||
{
|
||||
"name":"wordpress",
|
||||
"title":"Wordpress",
|
||||
"type":4,
|
||||
"version":"4.9.2",
|
||||
"md5":"6d7285257c7df3791255d8c4975804b5",
|
||||
"ext":"pathinfo",
|
||||
"chmod":"",
|
||||
"install":"",
|
||||
"php":"52,53,54,55,56,70,71,72",
|
||||
"ps":"最多人用的博客程序",
|
||||
"config":"",
|
||||
"username":"",
|
||||
"password":"",
|
||||
"run":"/",
|
||||
"download":"{Download}/wordpress.zip"
|
||||
},
|
||||
{
|
||||
"name":"metInfo",
|
||||
"title":"<a href='https://www.bt.cn/redirect?name=metInfo' class='btlink' target='_blank'>MetInfo</a>",
|
||||
"type":1,
|
||||
"version":"6.0.0",
|
||||
"md5":"fff363bc0cbb13a5c2dc8c2a652da825",
|
||||
"ext":"",
|
||||
"chmod":"",
|
||||
"install":"",
|
||||
"php":"52,53,54,55,56,70",
|
||||
"ps":"企业建站系统<font style='color: #fff;padding: 1px 4px;position: relative;margin-left: 6px;border-radius: 3px;background-color: #fc6d26;'>推荐</font>",
|
||||
"config":"",
|
||||
"username":"",
|
||||
"password":"",
|
||||
"run":"/",
|
||||
"download":"{Download}/metInfo.zip"
|
||||
},
|
||||
{
|
||||
"name":"chanzhi",
|
||||
"title":"<a href='https://www.bt.cn/redirect?name=chanzhi' class='btlink' target='_blank'>蝉知CMS</a>",
|
||||
"type":1,
|
||||
"version":"6.5",
|
||||
"md5":"793af27f2984127c34490229124644b8",
|
||||
"ext":"pathinfo",
|
||||
"chmod":"",
|
||||
"install":"/admin.php",
|
||||
"php":"53,54,55,56,70,71",
|
||||
"ps":"开源免费的企业门户系统,CMS系统<font style='color: #fff;padding: 1px 4px;position: relative;margin-left: 6px;border-radius: 3px;background-color: #fc6d26;'>推荐</font>",
|
||||
"config":"/system/config/my.php",
|
||||
"username":"admin",
|
||||
"password":"admin",
|
||||
"run":"/www",
|
||||
"download":"{Download}/chanzhi_auto.zip"
|
||||
},
|
||||
{
|
||||
"name":"cscms",
|
||||
"title":"<a href='https://www.bt.cn/redirect?name=cscms' class='btlink' target='_blank'>CSCMS</a>",
|
||||
"type":1,
|
||||
"version":"4.1",
|
||||
"md5":"12b66ea05ad3b829be916fe541c1dedc",
|
||||
"ext":"",
|
||||
"chmod":"",
|
||||
"install":"/install.php",
|
||||
"php":"53,54,55,56",
|
||||
"ps":"多元化内容管理系统<font style='color: #fff;padding: 1px 4px;position: relative;margin-left: 6px;border-radius: 3px;background-color: #fc6d26;'>推荐</font>",
|
||||
"config":"",
|
||||
"username":"",
|
||||
"password":"",
|
||||
"run":"/",
|
||||
"download":"{Download}/cscms.zip"
|
||||
},
|
||||
{
|
||||
"name":"kodexplorer",
|
||||
"title":"<a href='https://www.bt.cn/redirect?name=kodexplorer' class='btlink' target='_blank'>可道云KodExplorer</a>",
|
||||
"type":1,
|
||||
"version":"4.06",
|
||||
"md5":"2907559eac6714394eb9f5a44e005eea",
|
||||
"ext":"",
|
||||
"chmod":"",
|
||||
"install":"/index.php",
|
||||
"php":"52,53,54,55,56,70,71,72",
|
||||
"ps":"强大易用的私有云/在线文档管理<font style='color: #fff;padding: 1px 4px;position: relative;margin-left: 6px;border-radius: 3px;background-color: #fc6d26;'>推荐</font>",
|
||||
"config":"",
|
||||
"username":"",
|
||||
"password":"",
|
||||
"run":"/",
|
||||
"download":"{Download}/kodexplorer.zip"
|
||||
},
|
||||
{
|
||||
"name":"tipask",
|
||||
"title":"<a href='https://www.bt.cn/redirect?name=tipask' class='btlink' target='_blank'>Tipask</a>",
|
||||
"type":1,
|
||||
"version":"3.2",
|
||||
"md5":"ae99702f944bf1c60b14065d00a3d676",
|
||||
"ext":"fileinfo,opcache,pathinfo",
|
||||
"chmod":"775|storage/app/,775|storage/framework/,775|storage/logs/,775|bootstrap/cache/,664|.env",
|
||||
"install":"",
|
||||
"php":"55,56,70,71,72",
|
||||
"ps":"国内知名问答程序<font style='color: #fff;padding: 1px 4px;position: relative;margin-left: 6px;border-radius: 3px;background-color: #fc6d26;'>推荐</font>",
|
||||
"config":".env",
|
||||
"username":"admin@tipask.com",
|
||||
"password":"tipask",
|
||||
"run":"/public",
|
||||
"download":"{Download}/tipask_auto.zip"
|
||||
},
|
||||
{
|
||||
"name":"maccms",
|
||||
"title":"<a href='https://www.bt.cn/redirect?name=maccms' class='btlink' target='_blank'>苹果CMS</a>",
|
||||
"type":1,
|
||||
"version":"8.0",
|
||||
"md5":"a9801d94d1c592b5bb6d2474e5c0c37e",
|
||||
"ext":"",
|
||||
"chmod":"",
|
||||
"install":"",
|
||||
"php":"52,53,54,55,56,70,71,72",
|
||||
"ps":"PHP开源建站系统<font style='color: #fff;padding: 1px 4px;position: relative;margin-left: 6px;border-radius: 3px;background-color: #fc6d26;'>推荐</font>",
|
||||
"config":"",
|
||||
"username":"",
|
||||
"password":"",
|
||||
"run":"/",
|
||||
"download":"{Download}/maccms.zip"
|
||||
},
|
||||
{
|
||||
"name":"seacms",
|
||||
"title":"<a href='https://www.bt.cn/redirect?name=seacms' class='btlink' target='_blank'>海洋CMS</a>",
|
||||
"type":1,
|
||||
"version":"6.54",
|
||||
"md5":"ca43469682b8e5e7cdceba020bf39375",
|
||||
"ext":"",
|
||||
"chmod":"",
|
||||
"install":"/admin",
|
||||
"php":"52,53,54,55,56.70",
|
||||
"ps":"完全开源免费的PHP视频管理系统<font style='color: #fff;padding: 1px 4px;position: relative;margin-left: 6px;border-radius: 3px;background-color: #fc6d26;'>推荐</font>",
|
||||
"config":"data/common.inc.php",
|
||||
"username":"admin",
|
||||
"password":"admin",
|
||||
"run":"/",
|
||||
"download":"{Download}/seacms_auto.zip"
|
||||
},
|
||||
{
|
||||
"name":"cxxcms",
|
||||
"title":"<a href='https://www.bt.cn/redirect?name=cxxcms' class='btlink' target='_blank'>程序侠CMS</a>",
|
||||
"type":1,
|
||||
"version":"1.1.2",
|
||||
"md5":"8ad9c0671e41eeed34f046bf2abc3f22",
|
||||
"ext":"pathinfo",
|
||||
"chmod":"777|runtime,777|data,777|application/extra,777|application/database.php",
|
||||
"install":"/admin",
|
||||
"php":"54,55,56,70,71,72",
|
||||
"ps":"专业、安全的淘宝客CMS系统<font style='color: #fff;padding: 1px 4px;position: relative;margin-left: 6px;border-radius: 3px;background-color: #fc6d26;'>推荐</font>",
|
||||
"config":"application/database.php",
|
||||
"username":"admin",
|
||||
"password":"chengxuxia.com",
|
||||
"run":"/",
|
||||
"download":"{Download}/cxxcms_auto.zip"
|
||||
},
|
||||
{
|
||||
"name":"z-blog",
|
||||
"title":"<a href='https://www.bt.cn/redirect?name=z-blog' class='btlink' target='_blank'>Z-Blog</a>",
|
||||
"type":4,
|
||||
"version":"1.5.2",
|
||||
"md5":"07faf7fbeecfe9cad10872fe03ab3127",
|
||||
"ext":"pathinfo",
|
||||
"chmod":"",
|
||||
"install":"",
|
||||
"php":"52,53,54,55,56,70,71,72",
|
||||
"ps":"国内知名博客程序<font style='color: #fff;padding: 1px 4px;position: relative;margin-left: 6px;border-radius: 3px;background-color: #fc6d26;'>推荐</font>",
|
||||
"config":"zb_users/c_option.php",
|
||||
"username":"admin",
|
||||
"password":"zblogger",
|
||||
"run":"/",
|
||||
"download":"{Download}/z-blog_auto.zip"
|
||||
},
|
||||
{
|
||||
"name":"dbshop",
|
||||
"title":"<a href='https://www.bt.cn/redirect?name=dbshop' class='btlink' target='_blank'>DBShop</a>",
|
||||
"type":2,
|
||||
"version":"v1.2",
|
||||
"md5":"f3c5559fa8eb04bdc9938382658f3a03",
|
||||
"ext":"pathinfo",
|
||||
"chmod":"",
|
||||
"install":"/admin",
|
||||
"php":"53,54,55,56,70,71,72",
|
||||
"ps":"专业且良心的商城系统<font style='color: #fff;padding: 1px 4px;position: relative;margin-left: 6px;border-radius: 3px;background-color: #fc6d26;'>推荐</font>",
|
||||
"config":"data/Database.ini.php",
|
||||
"username":"admin",
|
||||
"password":"1234567",
|
||||
"run":"/",
|
||||
"download":"{Download}/dbshop_auto.zip"
|
||||
},
|
||||
{
|
||||
"name":"niushop",
|
||||
"title":"<a href='https://www.bt.cn/redirect?name=niushop' class='btlink' target='_blank'>NiuShop</a>",
|
||||
"type":2,
|
||||
"version":"1.1.9",
|
||||
"md5":"637c6ad54fa4732df35857304e5eeb82",
|
||||
"ext":"pathinfo",
|
||||
"chmod":"",
|
||||
"install":"",
|
||||
"php":"54,55,56,70",
|
||||
"ps":"优秀的TP5.0开源商城系统<font style='color: #fff;padding: 1px 4px;position: relative;margin-left: 6px;border-radius: 3px;background-color: #fc6d26;'>推荐</font>",
|
||||
"config":"application/database.php",
|
||||
"username":"admin",
|
||||
"password":"123456",
|
||||
"run":"/",
|
||||
"download":"{Download}/niushop_auto.zip"
|
||||
},
|
||||
{
|
||||
"name":"weengine",
|
||||
"title":"<a href='https://www.bt.cn/redirect?name=weengine' class='btlink' target='_blank'>微擎</a>",
|
||||
"type":5,
|
||||
"version":"1.0",
|
||||
"md5":"8424881fe0e60042df5064ca4638f56c",
|
||||
"ext":"fileinfo,pathinfo,ioncube",
|
||||
"chmod":"755|addons,755|api,755|app,755|data,755|attachment,755|framework,755|payment,755|web",
|
||||
"install":"/index.php",
|
||||
"php":"53,54,55,56,70,71,72",
|
||||
"ps":"微信第三方管理引擎<font style='color: #fff;padding: 1px 4px;position: relative;margin-left: 6px;border-radius: 3px;background-color: #fc6d26;'>推荐</font>",
|
||||
"config":"data/config.php",
|
||||
"username":"admin",
|
||||
"password":"we7.cc",
|
||||
"run":"/",
|
||||
"download":"{Download}/weengine_auto.zip"
|
||||
}
|
||||
]
|
||||
@@ -0,0 +1,495 @@
|
||||
[
|
||||
{
|
||||
"name":"niucms",
|
||||
"title":"NIUCMS",
|
||||
"type":1,
|
||||
"version":"3.6",
|
||||
"md5":"f5ea5a231a7d25d66ee324d60dcdd4f1",
|
||||
"ext":"pathinfo",
|
||||
"chmod":"",
|
||||
"install":"/install/index.php",
|
||||
"php":"53,54,55,56",
|
||||
"ps":"智慧城市生活系统(商业软件)",
|
||||
"config":"",
|
||||
"username":"",
|
||||
"password":"",
|
||||
"run":"/",
|
||||
"download":"{Download}/niucms.zip"
|
||||
},
|
||||
{
|
||||
"name":"tpt-blog",
|
||||
"title":"Tpt-Blog",
|
||||
"type":4,
|
||||
"version":"1.1",
|
||||
"md5":"aa195fece581582182e17143f53b2dfc",
|
||||
"ext":"pathinfo",
|
||||
"chmod":"",
|
||||
"install":"/admin.php",
|
||||
"php":"53,54,55,56,70,71,72",
|
||||
"ps":"免费开源的博客程序",
|
||||
"config":"/Application/Common/Conf/db.php",
|
||||
"username":"admin",
|
||||
"password":"admin<br>口令:123456",
|
||||
"run":"/",
|
||||
"download":"{Download}/tpt-blog_auto.zip"
|
||||
},
|
||||
{
|
||||
"name":"phpcms",
|
||||
"title":"PHPCMS",
|
||||
"type":1,
|
||||
"version":"9.6.3",
|
||||
"md5":"4de608ae7b74175dd4513911ef7ebf78",
|
||||
"ext":"pathinfo",
|
||||
"chmod":"",
|
||||
"install":"/install",
|
||||
"php":"52,53,54,55,56,70",
|
||||
"ps":"PHPCMS内容管理系统",
|
||||
"config":"",
|
||||
"username":"",
|
||||
"password":"",
|
||||
"run":"/",
|
||||
"download":"{Download}/phpcms.zip"
|
||||
},
|
||||
{
|
||||
"name":"ecshop_2.7",
|
||||
"title":"EcShop-2.7",
|
||||
"type":2,
|
||||
"version":"2.7.3",
|
||||
"md5":"9115294f5a458249a5f2c793eab3639c",
|
||||
"ext":"",
|
||||
"chmod":"",
|
||||
"install":"/install",
|
||||
"php":"52,53",
|
||||
"ps":"B2C独立网店系统",
|
||||
"config":"",
|
||||
"username":"",
|
||||
"password":"",
|
||||
"run":"/",
|
||||
"download":"{Download}/ecshop_2.7.zip"
|
||||
},
|
||||
{
|
||||
"name":"ecshop_3.6",
|
||||
"title":"EcShop-3.6",
|
||||
"type":2,
|
||||
"version":"3.6.0",
|
||||
"md5":"38fdc7fe8faf67edd91ebfbb8291cbc7",
|
||||
"ext":"",
|
||||
"chmod":"",
|
||||
"install":"/install",
|
||||
"php":"53,54,55,56",
|
||||
"ps":"B2C独立网店系统",
|
||||
"config":"",
|
||||
"username":"",
|
||||
"password":"",
|
||||
"run":"/ecshop",
|
||||
"download":"{Download}/ecshop_3.6.zip"
|
||||
},
|
||||
{
|
||||
"name":"emlog",
|
||||
"title":"Emlog",
|
||||
"type":4,
|
||||
"version":"5.3.1",
|
||||
"md5":"cecaa55d8d9644e8d8b7098c836af1ad",
|
||||
"ext":"",
|
||||
"chmod":"",
|
||||
"install":"",
|
||||
"php":"53,54,55,56",
|
||||
"ps":"博客程序",
|
||||
"config":"",
|
||||
"username":"",
|
||||
"password":"",
|
||||
"run":"/",
|
||||
"download":"{Download}/emlog.zip"
|
||||
},
|
||||
{
|
||||
"name":"empirecms",
|
||||
"title":"帝国CMS",
|
||||
"type":1,
|
||||
"version":"7.2",
|
||||
"md5":"07bb4ffe56c939170ff080744fc13dd8",
|
||||
"ext":"",
|
||||
"chmod":"",
|
||||
"install":"/e/install",
|
||||
"php":"52,53,54,55,56",
|
||||
"ps":"帝国内容管理系统",
|
||||
"config":"",
|
||||
"username":"",
|
||||
"password":"",
|
||||
"run":"/",
|
||||
"download":"{Download}/empirecms.zip"
|
||||
},
|
||||
{
|
||||
"name":"jieqiarticle170",
|
||||
"title":"杰奇1.7",
|
||||
"type":1,
|
||||
"version":"1.7",
|
||||
"md5":"e5168ea7b27f5c6de2a01dbf40f772b7",
|
||||
"ext":"",
|
||||
"chmod":"",
|
||||
"install":"/install",
|
||||
"php":"52",
|
||||
"ps":"小说连载程序",
|
||||
"config":"",
|
||||
"username":"",
|
||||
"password":"",
|
||||
"run":"/",
|
||||
"download":"{Download}/jieqiarticle170.zip"
|
||||
},
|
||||
{
|
||||
"name":"codeigniter",
|
||||
"title":"CI框架",
|
||||
"type":6,
|
||||
"version":"3.1.5",
|
||||
"md5":"47a615ef55910a908aff367dc5f59a70",
|
||||
"ext":"pathinfo",
|
||||
"chmod":"",
|
||||
"install":"",
|
||||
"php":"53,54,55,56,70,71,72",
|
||||
"ps":"知名PHP框架",
|
||||
"config":"",
|
||||
"username":"",
|
||||
"password":"",
|
||||
"run":"/public",
|
||||
"download":"{Download}/codeIgniter.zip"
|
||||
},
|
||||
{
|
||||
"name":"laravel",
|
||||
"title":"Laravel",
|
||||
"type":6,
|
||||
"version":"5.4",
|
||||
"md5":"988566e8982e835ffa6f7d9ad14b5330",
|
||||
"ext":"pathinfo",
|
||||
"chmod":"",
|
||||
"install":"",
|
||||
"php":"56,70,71,72",
|
||||
"ps":"优雅的PHP开发框架",
|
||||
"config":"",
|
||||
"username":"",
|
||||
"password":"",
|
||||
"run":"/public",
|
||||
"download":"{Download}/laravel.zip"
|
||||
},
|
||||
{
|
||||
"name":"thinkphp_3.2",
|
||||
"title":"ThinkPHP-3.2",
|
||||
"type":6,
|
||||
"version":"3.2.3",
|
||||
"md5":"6cd0e16bf478e7df2fc62b6819eebcce",
|
||||
"ext":"pathinfo",
|
||||
"chmod":"",
|
||||
"install":"",
|
||||
"php":"53,54,55,56",
|
||||
"ps":"国内知名PHP框架",
|
||||
"config":"",
|
||||
"username":"",
|
||||
"password":"",
|
||||
"run":"/",
|
||||
"download":"{Download}/thinkphp-3.2.zip"
|
||||
},
|
||||
{
|
||||
"name":"thinkphp_5.0",
|
||||
"title":"ThinkPHP-5.0",
|
||||
"type":6,
|
||||
"version":"5.0.15",
|
||||
"md5":"830fd878a802237a968ba36fceb1ae92",
|
||||
"ext":"pathinfo",
|
||||
"chmod":"",
|
||||
"install":"",
|
||||
"php":"54,55,56,70,71,72",
|
||||
"ps":"国内知名PHP框架",
|
||||
"config":"",
|
||||
"username":"",
|
||||
"password":"",
|
||||
"run":"/public",
|
||||
"download":"{Download}/thinkphp-5.0.zip"
|
||||
},
|
||||
{
|
||||
"name":"dedecms",
|
||||
"title":"织梦CMS",
|
||||
"type":1,
|
||||
"version":"v5.7SP2",
|
||||
"md5":"6f64be35597dbd2c51b1954f74cfb3ad",
|
||||
"ext":"",
|
||||
"chmod":"",
|
||||
"install":"/install",
|
||||
"php":"52,53,54,55,56,70,71,72",
|
||||
"ps":"国内知名内容管理系统",
|
||||
"config":"",
|
||||
"username":"",
|
||||
"password":"",
|
||||
"run":"/",
|
||||
"download":"{Download}/dedecms.zip"
|
||||
},
|
||||
{
|
||||
"name":"discuz_x3.4",
|
||||
"title":"Discuz 3.4",
|
||||
"type":3,
|
||||
"version":"x3.4",
|
||||
"md5":"44c4250ebdf5fa796712f336fbd2ef42",
|
||||
"ext":"pathinfo",
|
||||
"chmod":"",
|
||||
"install":"/install",
|
||||
"php":"52,53,54,55,56,70,71,72",
|
||||
"ps":"国内知名论坛程序",
|
||||
"config":"",
|
||||
"username":"",
|
||||
"password":"",
|
||||
"run":"/",
|
||||
"download":"{Download}/discuz_x3.4.zip"
|
||||
},
|
||||
{
|
||||
"name":"discuz",
|
||||
"title":"Discuz",
|
||||
"type":3,
|
||||
"version":"x3.3",
|
||||
"md5":"b31e43927daa00c7c3d92818298943fa",
|
||||
"ext":"pathinfo",
|
||||
"chmod":"",
|
||||
"install":"/install",
|
||||
"php":"52,53,54,55,56,70,71,72",
|
||||
"ps":"国内知名论坛程序",
|
||||
"config":"",
|
||||
"username":"",
|
||||
"password":"",
|
||||
"run":"/",
|
||||
"download":"{Download}/discuz.zip"
|
||||
},
|
||||
{
|
||||
"name":"typecho",
|
||||
"title":"Typecho",
|
||||
"type":4,
|
||||
"version":"1.1",
|
||||
"md5":"8b638551974e98994401cda253a3f836",
|
||||
"ext":"pathinfo",
|
||||
"chmod":"",
|
||||
"install":"",
|
||||
"php":"54,55,56,70,71,72",
|
||||
"ps":"轻量级博客程序",
|
||||
"config":"",
|
||||
"username":"",
|
||||
"password":"",
|
||||
"run":"/",
|
||||
"download":"{Download}/typecho.zip"
|
||||
},
|
||||
{
|
||||
"name":"wordpress",
|
||||
"title":"Wordpress",
|
||||
"type":4,
|
||||
"version":"4.9.2",
|
||||
"md5":"6d7285257c7df3791255d8c4975804b5",
|
||||
"ext":"pathinfo",
|
||||
"chmod":"",
|
||||
"install":"",
|
||||
"php":"52,53,54,55,56,70,71,72",
|
||||
"ps":"最多人用的博客程序",
|
||||
"config":"",
|
||||
"username":"",
|
||||
"password":"",
|
||||
"run":"/",
|
||||
"download":"{Download}/wordpress.zip"
|
||||
},
|
||||
{
|
||||
"name":"metInfo",
|
||||
"title":"<a href='https://www.bt.cn/redirect?name=metInfo' class='btlink' target='_blank'>MetInfo</a>",
|
||||
"type":1,
|
||||
"version":"6.0.0",
|
||||
"md5":"fff363bc0cbb13a5c2dc8c2a652da825",
|
||||
"ext":"",
|
||||
"chmod":"",
|
||||
"install":"",
|
||||
"php":"52,53,54,55,56,70",
|
||||
"ps":"企业建站系统<font style='color: #fff;padding: 1px 4px;position: relative;margin-left: 6px;border-radius: 3px;background-color: #fc6d26;'>推荐</font>",
|
||||
"config":"",
|
||||
"username":"",
|
||||
"password":"",
|
||||
"run":"/",
|
||||
"download":"{Download}/metInfo.zip"
|
||||
},
|
||||
{
|
||||
"name":"chanzhi",
|
||||
"title":"<a href='https://www.bt.cn/redirect?name=chanzhi' class='btlink' target='_blank'>蝉知CMS</a>",
|
||||
"type":1,
|
||||
"version":"6.5",
|
||||
"md5":"793af27f2984127c34490229124644b8",
|
||||
"ext":"pathinfo",
|
||||
"chmod":"",
|
||||
"install":"/admin.php",
|
||||
"php":"53,54,55,56,70,71",
|
||||
"ps":"开源免费的企业门户系统,CMS系统<font style='color: #fff;padding: 1px 4px;position: relative;margin-left: 6px;border-radius: 3px;background-color: #fc6d26;'>推荐</font>",
|
||||
"config":"/system/config/my.php",
|
||||
"username":"admin",
|
||||
"password":"admin",
|
||||
"run":"/www",
|
||||
"download":"{Download}/chanzhi_auto.zip"
|
||||
},
|
||||
{
|
||||
"name":"cscms",
|
||||
"title":"<a href='https://www.bt.cn/redirect?name=cscms' class='btlink' target='_blank'>CSCMS</a>",
|
||||
"type":1,
|
||||
"version":"4.1",
|
||||
"md5":"12b66ea05ad3b829be916fe541c1dedc",
|
||||
"ext":"",
|
||||
"chmod":"",
|
||||
"install":"/install.php",
|
||||
"php":"53,54,55,56",
|
||||
"ps":"多元化内容管理系统<font style='color: #fff;padding: 1px 4px;position: relative;margin-left: 6px;border-radius: 3px;background-color: #fc6d26;'>推荐</font>",
|
||||
"config":"",
|
||||
"username":"",
|
||||
"password":"",
|
||||
"run":"/",
|
||||
"download":"{Download}/cscms.zip"
|
||||
},
|
||||
{
|
||||
"name":"kodexplorer",
|
||||
"title":"<a href='https://www.bt.cn/redirect?name=kodexplorer' class='btlink' target='_blank'>可道云KodExplorer</a>",
|
||||
"type":1,
|
||||
"version":"4.06",
|
||||
"md5":"2907559eac6714394eb9f5a44e005eea",
|
||||
"ext":"",
|
||||
"chmod":"",
|
||||
"install":"/index.php",
|
||||
"php":"52,53,54,55,56,70,71,72",
|
||||
"ps":"强大易用的私有云/在线文档管理<font style='color: #fff;padding: 1px 4px;position: relative;margin-left: 6px;border-radius: 3px;background-color: #fc6d26;'>推荐</font>",
|
||||
"config":"",
|
||||
"username":"",
|
||||
"password":"",
|
||||
"run":"/",
|
||||
"download":"{Download}/kodexplorer.zip"
|
||||
},
|
||||
{
|
||||
"name":"tipask",
|
||||
"title":"<a href='https://www.bt.cn/redirect?name=tipask' class='btlink' target='_blank'>Tipask</a>",
|
||||
"type":1,
|
||||
"version":"3.2",
|
||||
"md5":"ae99702f944bf1c60b14065d00a3d676",
|
||||
"ext":"fileinfo,opcache,pathinfo",
|
||||
"chmod":"775|storage/app/,775|storage/framework/,775|storage/logs/,775|bootstrap/cache/,664|.env",
|
||||
"install":"",
|
||||
"php":"55,56,70,71,72",
|
||||
"ps":"国内知名问答程序<font style='color: #fff;padding: 1px 4px;position: relative;margin-left: 6px;border-radius: 3px;background-color: #fc6d26;'>推荐</font>",
|
||||
"config":".env",
|
||||
"username":"admin@tipask.com",
|
||||
"password":"tipask",
|
||||
"run":"/public",
|
||||
"download":"{Download}/tipask_auto.zip"
|
||||
},
|
||||
{
|
||||
"name":"maccms",
|
||||
"title":"<a href='https://www.bt.cn/redirect?name=maccms' class='btlink' target='_blank'>苹果CMS</a>",
|
||||
"type":1,
|
||||
"version":"8.0",
|
||||
"md5":"a9801d94d1c592b5bb6d2474e5c0c37e",
|
||||
"ext":"",
|
||||
"chmod":"",
|
||||
"install":"",
|
||||
"php":"52,53,54,55,56,70,71,72",
|
||||
"ps":"PHP开源建站系统<font style='color: #fff;padding: 1px 4px;position: relative;margin-left: 6px;border-radius: 3px;background-color: #fc6d26;'>推荐</font>",
|
||||
"config":"",
|
||||
"username":"",
|
||||
"password":"",
|
||||
"run":"/",
|
||||
"download":"{Download}/maccms.zip"
|
||||
},
|
||||
{
|
||||
"name":"seacms",
|
||||
"title":"<a href='https://www.bt.cn/redirect?name=seacms' class='btlink' target='_blank'>海洋CMS</a>",
|
||||
"type":1,
|
||||
"version":"6.54",
|
||||
"md5":"ca43469682b8e5e7cdceba020bf39375",
|
||||
"ext":"",
|
||||
"chmod":"",
|
||||
"install":"/admin",
|
||||
"php":"52,53,54,55,56.70",
|
||||
"ps":"完全开源免费的PHP视频管理系统<font style='color: #fff;padding: 1px 4px;position: relative;margin-left: 6px;border-radius: 3px;background-color: #fc6d26;'>推荐</font>",
|
||||
"config":"data/common.inc.php",
|
||||
"username":"admin",
|
||||
"password":"admin",
|
||||
"run":"/",
|
||||
"download":"{Download}/seacms_auto.zip"
|
||||
},
|
||||
{
|
||||
"name":"cxxcms",
|
||||
"title":"<a href='https://www.bt.cn/redirect?name=cxxcms' class='btlink' target='_blank'>程序侠CMS</a>",
|
||||
"type":1,
|
||||
"version":"1.1.2",
|
||||
"md5":"8ad9c0671e41eeed34f046bf2abc3f22",
|
||||
"ext":"pathinfo",
|
||||
"chmod":"777|runtime,777|data,777|application/extra,777|application/database.php",
|
||||
"install":"/admin",
|
||||
"php":"54,55,56,70,71,72",
|
||||
"ps":"专业、安全的淘宝客CMS系统<font style='color: #fff;padding: 1px 4px;position: relative;margin-left: 6px;border-radius: 3px;background-color: #fc6d26;'>推荐</font>",
|
||||
"config":"application/database.php",
|
||||
"username":"admin",
|
||||
"password":"chengxuxia.com",
|
||||
"run":"/",
|
||||
"download":"{Download}/cxxcms_auto.zip"
|
||||
},
|
||||
{
|
||||
"name":"z-blog",
|
||||
"title":"<a href='https://www.bt.cn/redirect?name=z-blog' class='btlink' target='_blank'>Z-Blog</a>",
|
||||
"type":4,
|
||||
"version":"1.5.2",
|
||||
"md5":"07faf7fbeecfe9cad10872fe03ab3127",
|
||||
"ext":"pathinfo",
|
||||
"chmod":"",
|
||||
"install":"",
|
||||
"php":"52,53,54,55,56,70,71,72",
|
||||
"ps":"国内知名博客程序<font style='color: #fff;padding: 1px 4px;position: relative;margin-left: 6px;border-radius: 3px;background-color: #fc6d26;'>推荐</font>",
|
||||
"config":"zb_users/c_option.php",
|
||||
"username":"admin",
|
||||
"password":"zblogger",
|
||||
"run":"/",
|
||||
"download":"{Download}/z-blog_auto.zip"
|
||||
},
|
||||
{
|
||||
"name":"dbshop",
|
||||
"title":"<a href='https://www.bt.cn/redirect?name=dbshop' class='btlink' target='_blank'>DBShop</a>",
|
||||
"type":2,
|
||||
"version":"v1.2",
|
||||
"md5":"f3c5559fa8eb04bdc9938382658f3a03",
|
||||
"ext":"pathinfo",
|
||||
"chmod":"",
|
||||
"install":"/admin",
|
||||
"php":"53,54,55,56,70,71,72",
|
||||
"ps":"专业且良心的商城系统<font style='color: #fff;padding: 1px 4px;position: relative;margin-left: 6px;border-radius: 3px;background-color: #fc6d26;'>推荐</font>",
|
||||
"config":"data/Database.ini.php",
|
||||
"username":"admin",
|
||||
"password":"1234567",
|
||||
"run":"/",
|
||||
"download":"{Download}/dbshop_auto.zip"
|
||||
},
|
||||
{
|
||||
"name":"niushop",
|
||||
"title":"<a href='https://www.bt.cn/redirect?name=niushop' class='btlink' target='_blank'>NiuShop</a>",
|
||||
"type":2,
|
||||
"version":"1.1.9",
|
||||
"md5":"637c6ad54fa4732df35857304e5eeb82",
|
||||
"ext":"pathinfo",
|
||||
"chmod":"",
|
||||
"install":"",
|
||||
"php":"54,55,56,70",
|
||||
"ps":"优秀的TP5.0开源商城系统<font style='color: #fff;padding: 1px 4px;position: relative;margin-left: 6px;border-radius: 3px;background-color: #fc6d26;'>推荐</font>",
|
||||
"config":"application/database.php",
|
||||
"username":"admin",
|
||||
"password":"123456",
|
||||
"run":"/",
|
||||
"download":"{Download}/niushop_auto.zip"
|
||||
},
|
||||
{
|
||||
"name":"weengine",
|
||||
"title":"<a href='https://www.bt.cn/redirect?name=weengine' class='btlink' target='_blank'>微擎</a>",
|
||||
"type":5,
|
||||
"version":"1.0",
|
||||
"md5":"8424881fe0e60042df5064ca4638f56c",
|
||||
"ext":"fileinfo,pathinfo,ioncube",
|
||||
"chmod":"755|addons,755|api,755|app,755|data,755|attachment,755|framework,755|payment,755|web",
|
||||
"install":"/index.php",
|
||||
"php":"53,54,55,56,70,71,72",
|
||||
"ps":"微信第三方管理引擎<font style='color: #fff;padding: 1px 4px;position: relative;margin-left: 6px;border-radius: 3px;background-color: #fc6d26;'>推荐</font>",
|
||||
"config":"data/config.php",
|
||||
"username":"admin",
|
||||
"password":"we7.cc",
|
||||
"run":"/",
|
||||
"download":"{Download}/weengine_auto.zip"
|
||||
}
|
||||
]
|
||||
@@ -0,0 +1,37 @@
|
||||
[
|
||||
{
|
||||
"title":"推荐",
|
||||
"type":0,
|
||||
"ps":""
|
||||
},
|
||||
{
|
||||
"title":"建站",
|
||||
"type":1,
|
||||
"ps":""
|
||||
},
|
||||
{
|
||||
"title":"商城",
|
||||
"type":2,
|
||||
"ps":""
|
||||
},
|
||||
{
|
||||
"title":"论坛",
|
||||
"type":3,
|
||||
"ps":""
|
||||
},
|
||||
{
|
||||
"title":"博客",
|
||||
"type":4,
|
||||
"ps":""
|
||||
},
|
||||
{
|
||||
"title":"微信",
|
||||
"type":5,
|
||||
"ps":""
|
||||
},
|
||||
{
|
||||
"title":"框架",
|
||||
"type":6,
|
||||
"ps":""
|
||||
}
|
||||
]
|
||||
@@ -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>
|
||||
@@ -1,868 +0,0 @@
|
||||
# coding:utf-8
|
||||
|
||||
import sys
|
||||
import io
|
||||
import os
|
||||
import time
|
||||
import re
|
||||
import json
|
||||
|
||||
web_dir = os.getcwd() + "/web"
|
||||
if os.path.exists(web_dir):
|
||||
sys.path.append(web_dir)
|
||||
os.chdir(web_dir)
|
||||
|
||||
import core.mw as mw
|
||||
|
||||
try:
|
||||
import docker
|
||||
except Exception as e:
|
||||
pass
|
||||
|
||||
|
||||
|
||||
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.getFatherDir() + '/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.getFatherDir() + '/backup/docker'
|
||||
return mw.returnJson(True, 'ok', bkDir)
|
||||
|
||||
|
||||
def dockerImagePickList():
|
||||
|
||||
bkDir = mw.getFatherDir() + '/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.getFatherDir() + '/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:
|
||||
from utils.firewall import Firewall as MwFirewall
|
||||
MwFirewall.instance().addAcceptPort(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')
|
||||
@@ -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"
|
||||
}
|
||||
@@ -1,104 +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
|
||||
|
||||
VERSION=$2
|
||||
|
||||
if [ -f ${rootPath}/bin/activate ];then
|
||||
source ${rootPath}/bin/activate
|
||||
fi
|
||||
|
||||
# {
|
||||
# "registry-mirrors": [
|
||||
# "http://hub-mirror.c.163.com",
|
||||
# "https://docker.mirrors.ustc.edu.cn",
|
||||
# "https://registry.docker-cn.com"
|
||||
# ]
|
||||
# }
|
||||
|
||||
# sudo mkdir -p /etc/docker
|
||||
# sudo tee /etc/docker/daemon.json <<-'EOF'
|
||||
# {
|
||||
# "registry-mirrors": [
|
||||
# "http://hub-mirror.c.163.com",
|
||||
# "https://docker.mirrors.ustc.edu.cn",
|
||||
# "https://registry.docker-cn.com"
|
||||
# ]
|
||||
# }
|
||||
# EOF
|
||||
# sudo systemctl daemon-reload
|
||||
# sudo systemctl restart docker
|
||||
|
||||
Install_Docker()
|
||||
{
|
||||
# which docker
|
||||
# if [ "$?" == "0" ];then
|
||||
# echo '安装已经完成docker'
|
||||
# exit 0
|
||||
# fi
|
||||
|
||||
echo '正在安装脚本文件...'
|
||||
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 '安装Docker完成'
|
||||
|
||||
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 "卸载Docker"
|
||||
}
|
||||
|
||||
action=$1
|
||||
if [ "${1}" == 'install' ];then
|
||||
Install_Docker
|
||||
else
|
||||
Uninstall_Docker
|
||||
fi
|
||||