Compare commits

..
42 Commits
Author SHA1 Message Date
Mr Chen 5cbca9232c Update app.py 2018-10-08 11:52:55 +08:00
midoks 742bb6d86d u 2018-10-07 23:51:22 +08:00
midoks b14accd83e u 2018-10-07 20:54:10 +08:00
midoks 8ae01d8d36 u 2018-10-07 20:38:30 +08:00
midoks 5db1e58e6a u 2018-10-07 18:51:52 +08:00
midoks a2e6d2f280 u 2018-10-07 18:25:51 +08:00
midoks 83f3d224ed d 2018-10-07 17:57:08 +08:00
midoks 73693620df u 2018-10-07 17:42:46 +08:00
midoks 72eb67dfda u 2018-10-07 16:29:42 +08:00
midoks c55b4a02c8 插件 2018-10-02 21:50:10 +08:00
midoks 721efd6a16 up 2018-10-02 18:00:48 +08:00
midoks 61c8ce4b6f up 2018-10-02 17:24:26 +08:00
midoks ce95ca6905 up 2018-10-02 16:59:56 +08:00
Mr Chen 01dee5fe1f up 2018-09-26 20:03:12 +08:00
Mr Chen 1a72edee9a 更新 2018-09-26 20:02:11 +08:00
Mr Chen 27b6ad0280 up 2018-09-26 14:59:06 +08:00
Mr Chen ae9e6921c3 up 2018-09-26 13:32:57 +08:00
Mr Chen b51eb99027 up 2018-09-25 18:43:26 +08:00
Mr Chen 2e584c540d up 2018-09-25 17:01:20 +08:00
Mr Chen bbf258e1a7 up 2018-09-25 16:37:56 +08:00
Mr Chen 53d57716aa o 2018-09-25 14:16:10 +08:00
Mr Chen e9a6e5477d up 2018-09-25 13:59:20 +08:00
Mr Chen 46ca8a6b44 up 2018-09-25 12:50:06 +08:00
Mr Chen d6f88caf3d up 2018-09-21 14:18:29 +08:00
midoks abc85902a2 up 2018-09-15 16:45:14 +08:00
Mr Chen 9edc8bbea2 up 2018-09-14 19:18:36 +08:00
Mr Chen 443d3efd37 up 2018-09-14 15:23:04 +08:00
midoks 948bcde358 模版 2018-09-13 22:30:37 +08:00
midoks 83aa3cfca6 up 2018-09-13 22:26:34 +08:00
Mr Chen 976c88ed44 Delete .DS_Store 2018-09-13 22:24:06 +08:00
midoks 0db16797b5 up 2018-09-13 22:23:05 +08:00
midoks b7802fc898 更新说明 2018-09-13 22:22:23 +08:00
midoks 741dffd5db up 2018-09-13 22:20:52 +08:00
Mr Chen 2c9f11bcfe static 2018-09-13 13:36:11 +08:00
midoks 16893e51e6 up 2018-09-11 23:02:52 +08:00
midoks 34a43a6af9 up 2018-09-11 22:42:43 +08:00
midoks 4dd5d946d9 up 2018-09-11 22:39:22 +08:00
Mr Chen 7e63f686b9 up 2018-09-11 16:27:09 +08:00
Mr Chen 7b57972179 up 2018-09-11 14:50:44 +08:00
Mr Chen a758a36bdc test 2018-09-11 14:48:31 +08:00
Mr Chen 84123c603d Create README.md 2018-09-11 14:44:23 +08:00
Mr Chen 33f9c09a07 Initial commit 2018-09-11 14:44:15 +08:00
1782 changed files with 26558 additions and 231631 deletions
-5
View File
@@ -1,5 +0,0 @@
# These are supported funding model platforms
github: midoks
custom: https://afdian.net/a/mdserver-web
@@ -1,29 +0,0 @@
---
name: BUG提交
about: 问题描述
---
## 什么系统 ?
Debian x, Centos x, Centos x stream, Ubuntu x?
## 错误信息 ?
Ex:
```bash
一个issue,一个问题。多余问题,视情况而答。
```
## 错误截图 ?
...
## 浏览器版本 ?
...
## 软件版本(插件/面板) ?
...
@@ -1,14 +0,0 @@
---
name: 需求提交
about: 需求描述
---
## 需求
想开发一个什么插件?
想有什么功能?
## 提供参考
给开发者提供参考!
-24
View File
@@ -1,24 +0,0 @@
name: Bug提交
description: 提交错误报告以帮助我们改进
labels: ["\U0001F48A bug"]
body:
- type: markdown
attributes:
value: |
感谢您花时间填写此错误报告!
- 如有疑问,请讨论[Discussions](https://github.com/midoks/mdserver-web/discussions).
- 检查以确保尚未有人打开类似的[issue](https://github.com/midoks/mdserver-web/issues).
- type: input
attributes:
label: 操作系统
description: |
请指定您报告的确切操作系统名称和版本,例如"CentOS 7"、"Ubuntu 20.04"。
validations:
required: true
- type: textarea
attributes:
label: 描述错误
description: 对bug的清晰而简洁的描述。
validations:
required: true
-8
View File
@@ -1,8 +0,0 @@
blank_issues_enabled: true
contact_links:
- name: 提出问题
url: https://github.com/midoks/mdserver-web/discussions
about: 请在讨论中提出问题。
- name: 提出建议
url: https://github.com/midoks/mdserver-web/discussions/categories/proposal
about: 请在讨论中提出建议。
-24
View File
@@ -1,24 +0,0 @@
name: Bug提交
description: 提交错误报告以帮助我们改进
labels: ["\U0001F48A bug"]
body:
- type: markdown
attributes:
value: |
感谢您花时间填写此错误报告!
- 如有疑问,请讨论[Discussions](https://github.com/midoks/mdserver-web/discussions).
- 检查以确保尚未有人打开类似的[issue](https://github.com/midoks/mdserver-web/issues).
- type: input
attributes:
label: 操作系统
description: |
请指定您报告的确切操作系统名称和版本,例如"CentOS 7"、"Ubuntu 20.04"。
validations:
required: true
- type: textarea
attributes:
label: 描述错误
description: 对bug的清晰而简洁的描述。
validations:
required: true
@@ -1,24 +0,0 @@
name: Bug提交
description: 提交错误报告以帮助我们改进
labels: ["\U0001F48A bug"]
body:
- type: markdown
attributes:
value: |
感谢您花时间填写此错误报告!
- 如有疑问,请讨论[Discussions](https://github.com/midoks/mdserver-web/discussions).
- 检查以确保尚未有人打开类似的[issue](https://github.com/midoks/mdserver-web/issues).
- type: input
attributes:
label: 操作系统
description: |
请指定您报告的确切操作系统名称和版本,例如"CentOS 7"、"Ubuntu 20.04"。
validations:
required: true
- type: textarea
attributes:
label: 描述错误
description: 对bug的清晰而简洁的描述。
validations:
required: true
-3
View File
@@ -1,3 +0,0 @@
### 合并描述
- 我添加了测试用例来覆盖新代码。
-38
View File
@@ -1,38 +0,0 @@
# This workflow will install Python dependencies, run tests and lint with a single version of Python
# For more information see: https://help.github.com/actions/language-and-framework-guides/using-python-with-github-actions
name: MW APP MySQL55
on:
push:
branches: [ "run_test" ]
pull_request:
branches: [ "run_test" ]
permissions:
contents: read
jobs:
build:
strategy:
matrix:
os: [ubuntu-20.04, ubuntu-18.04, ubuntu-22.04]
runs-on: ${{ matrix.os }}
#runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Set up Python 3.10
uses: actions/setup-python@v3
with:
python-version: "3.10"
- name: Install MW
run: |
sudo bash scripts/install_dev.sh
- name: Install MySQL55
run: |
cd /www/server/mdserver-web/plugins/mysql && sudo bash install.sh install 5.5
- name: Start DEBUG
run: |
cd web && gunicorn -c setting.py app:app
python3 panel_task.py &
-38
View File
@@ -1,38 +0,0 @@
# This workflow will install Python dependencies, run tests and lint with a single version of Python
# For more information see: https://help.github.com/actions/language-and-framework-guides/using-python-with-github-actions
name: MW APP MySQL56
on:
push:
branches: [ "run_test" ]
pull_request:
branches: [ "run_test" ]
permissions:
contents: read
jobs:
build:
strategy:
matrix:
os: [ubuntu-20.04, ubuntu-18.04, ubuntu-22.04]
runs-on: ${{ matrix.os }}
#runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Set up Python 3.10
uses: actions/setup-python@v3
with:
python-version: "3.10"
- name: Install MW
run: |
sudo bash scripts/install_dev.sh
- name: Install MySQL56
run: |
cd /www/server/mdserver-web/plugins/mysql && sudo bash install.sh install 5.6
- name: Start DEBUG
run: |
cd web && gunicorn -c setting.py app:app
python3 panel_task.py &
-38
View File
@@ -1,38 +0,0 @@
# This workflow will install Python dependencies, run tests and lint with a single version of Python
# For more information see: https://help.github.com/actions/language-and-framework-guides/using-python-with-github-actions
name: MW APP MySQL57
on:
push:
branches: [ "run_test" ]
pull_request:
branches: [ "run_test" ]
permissions:
contents: read
jobs:
build:
strategy:
matrix:
os: [ubuntu-20.04, ubuntu-18.04, ubuntu-22.04]
runs-on: ${{ matrix.os }}
#runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Set up Python 3.10
uses: actions/setup-python@v3
with:
python-version: "3.10"
- name: Install MW
run: |
sudo bash scripts/install_dev.sh
- name: Install MySQL57
run: |
cd /www/server/mdserver-web/plugins/mysql && sudo bash install.sh install 5.7
- name: Start DEBUG
run: |
cd web && gunicorn -c setting.py app:app
python3 panel_task.py &
-38
View File
@@ -1,38 +0,0 @@
# This workflow will install Python dependencies, run tests and lint with a single version of Python
# For more information see: https://help.github.com/actions/language-and-framework-guides/using-python-with-github-actions
name: MW APP MySQL80
on:
push:
branches: [ "run_test" ]
pull_request:
branches: [ "run_test" ]
permissions:
contents: read
jobs:
build:
strategy:
matrix:
os: [ubuntu-20.04, ubuntu-18.04, ubuntu-22.04]
runs-on: ${{ matrix.os }}
#runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Set up Python 3.10
uses: actions/setup-python@v3
with:
python-version: "3.10"
- name: Install MW
run: |
sudo bash scripts/install_dev.sh
- name: Install MySQL80
run: |
cd /www/server/mdserver-web/plugins/mysql && sudo bash install.sh install 8.0
- name: Start DEBUG
run: |
cd web && gunicorn -c setting.py app:app
python3 panel_task.py &
-38
View File
@@ -1,38 +0,0 @@
# This workflow will install Python dependencies, run tests and lint with a single version of Python
# For more information see: https://help.github.com/actions/language-and-framework-guides/using-python-with-github-actions
name: MW APP OpenResty
on:
push:
branches: [ "master", "dev" ]
pull_request:
branches: [ "master", "dev" ]
permissions:
contents: read
jobs:
build:
strategy:
matrix:
os: [ubuntu-latest]
runs-on: ${{ matrix.os }}
#runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Set up Python 3.10
uses: actions/setup-python@v3
with:
python-version: "3.10"
- name: Install MW
run: |
sudo bash scripts/install_dev.sh
- name: Install OpenResty
run: |
cd /www/server/mdserver-web/plugins/openresty && sudo bash install.sh install 1.27.1
- name: Start DEBUG
run: |
source /www/server/mdserver-web/bin/activate && cd web && gunicorn -c setting.py app:app
cd /www/server/mdserver-web && python3 panel_task.py &
-68
View File
@@ -1,68 +0,0 @@
# This workflow will install Python dependencies, run tests and lint with a single version of Python
# For more information see: https://help.github.com/actions/language-and-framework-guides/using-python-with-github-actions
name: MW APP PHP
on:
push:
branches: [ "run_test" ]
pull_request:
branches: [ "run_test" ]
permissions:
contents: read
jobs:
build:
strategy:
matrix:
os: [ubuntu-latest]
runs-on: ${{ matrix.os }}
#runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Set up Python 3.10
uses: actions/setup-python@v3
with:
python-version: "3.10"
- name: Install MW
run: |
sudo bash scripts/install_dev.sh
- name: Install PHP53
run: |
cd /www/server/mdserver-web/plugins/php && sudo bash install.sh install 53
- name: Install PHP54
run: |
cd /www/server/mdserver-web/plugins/php && sudo bash install.sh install 54
- name: Install PHP55
run: |
cd /www/server/mdserver-web/plugins/php && sudo bash install.sh install 55
- name: Install PHP56
run: |
cd /www/server/mdserver-web/plugins/php && sudo bash install.sh install 56
- name: Install PHP70
run: |
cd /www/server/mdserver-web/plugins/php && sudo bash install.sh install 70
- name: Install PHP71
run: |
cd /www/server/mdserver-web/plugins/php && sudo bash install.sh install 71
- name: Install PHP72
run: |
cd /www/server/mdserver-web/plugins/php && sudo bash install.sh install 72
- name: Install PHP73
run: |
cd /www/server/mdserver-web/plugins/php && sudo bash install.sh install 73
- name: Install PHP74
run: |
cd /www/server/mdserver-web/plugins/php && sudo bash install.sh install 74
- name: Install PHP80
run: |
cd /www/server/mdserver-web/plugins/php && sudo bash install.sh install 80
- name: Install PHP81
run: |
cd /www/server/mdserver-web/plugins/php && sudo bash install.sh install 81
- name: Start DEBUG
run: |
source /www/server/mdserver-web/bin/activate && cd web && gunicorn -c setting.py app:app
cd /www/server/mdserver-web && python3 panel_task.py &
-16
View File
@@ -1,16 +0,0 @@
name: sync2gitee
on: [push]
jobs:
repo-sync:
runs-on: ubuntu-latest
steps:
- name: Mirror the Github organization repos to Gitee.
uses: Yikun/hub-mirror-action@master
with:
src: 'github/midoks'
dst: 'gitee/midoks'
dst_key: ${{ secrets.GITEE_PRIVATE_KEY }}
dst_token: ${{ secrets.GITEE_TOKEN }}
static_list: "mdserver-web"
force_update: true
+2 -89
View File
@@ -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
Generated
+6
View File
@@ -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>
+1 -1
View File
@@ -186,7 +186,7 @@
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [midoks] [midoks of copyright owner]
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
+10 -221
View File
@@ -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管理软件。我一看到,就知道这是我一直想要的页面化管理方式。
复制了后台管理界面,按照自己想要的方式写了一版。
![EulerOS](https://img.shields.io/badge/LINUX-EulerOS-blue?style=for-the-badge&logo=EulerOS)
![Debian](https://img.shields.io/badge/LINUX-Debian-blue?style=for-the-badge&logo=Debian)
![Ubuntu](https://img.shields.io/badge/LINUX-Ubuntu-blue?style=for-the-badge&logo=Ubuntu)
![Fedora](https://img.shields.io/badge/LINUX-Fedora-blue?style=for-the-badge&logo=Fedora)
![CentOS](https://img.shields.io/badge/LINUX-CentOS-blue?style=for-the-badge&logo=CentOS)
[![Wiki](https://img.shields.io/badge/MW-Wiki-red?style=for-the-badge&logo=wiki)](https://github.com/midoks/mdserver-web/wiki)
[![jsdelivr](https://data.jsdelivr.com/v1/package/gh/midoks/mdserver-web/badge)](https://www.jsdelivr.com/package/gh/midoks/mdserver-web)
* SSH终端工具
* 面板收藏功能
* 网站子目录绑定
* 网站备份功能
* 插件方式管理
基本上可以使用,后续会继续优化!欢迎提供意见!
- 吹水组 - https://t.me/mdserver_web
- 交流论坛 - https://bbs.midoks.icu
# 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 |[![digitalvirt](https://digitalvirt.com/templates/BlueWhite/img/logo-dark.svg)](https://digitalvirt.com/aff.php?aff=154) | https://digitalvirt.com/aff.php?aff=154 | mdserver-web |
# Docker
- 由[DDS-Derek](https://github.com/DDS-Derek)开发维护。
- https://github.com/DDS-Derek/mdserver-web-Docker
```
docker run -itd --name mw-server --privileged=true -p 7200:7200 -p 80:80 -p 443:443 -p 888:888 ddsderek/mw-server:latest
```
### 版本更新 0.18.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/img/alipay_zz.png)
### 无图不真相
[![截图](https://cdn.jsdelivr.net/gh/midoks/mdserver-web@latest/route/static/mdw.jpg)](https://cdn.jsdelivr.net/gh/midoks/mdserver-web@latest/route/static/mdw.jpg)
### Stargazers over time
[![Stargazers over time](https://starchart.cc/midoks/mdserver-web.svg)](https://starchart.cc/midoks/mdserver-web)
### 感谢开发赞助
[![digitalvirt](https://digitalvirt.com/templates/BlueWhite/img/logo-dark.svg)](https://digitalvirt.com/aff.php?aff=154)
### 授权许可
本项目采用 Apache 开源授权许可证,完整的授权说明已放置在 [LICENSE](https://github.com/midoks/mdserver-web/blob/master/LICENSE) 文件中。
### 手动操作
- pip install pip==9.0.3
- pip install flask
- pip install gunicorn
- pip install psutil
- pip install -r requirements.txt
+36
View File
@@ -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()
+841
View File
@@ -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
-8
View File
@@ -1,8 +0,0 @@
#!/bin/bash
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin:/usr/local/lib/python2.7/bin
find . -name .DS_Store -print0 | xargs -0 git rm -f --ignore-unmatch
find . -name .DS_Store | xargs rm -rf
find . -type d -name "*.pyc" | xargs rm -rf
-117
View File
@@ -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
-82
View File
@@ -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
```
-71
View File
@@ -1,71 +0,0 @@
### 兼容性测试报告
快速测试命令
```
cd /www/server/mdserver-web/scripts/quick && bash debug.sh
cd /www/server/mdserver-web/plugins/php/versions && bash all_test.sh
```
| 系统名称 | 面板 | OpenResty |
| ----------------- |---------------|---------------|
| CentOS 7.9 |✅ |✅ |
| CentOS 8.4 |✅ |✅ |
| CentOS 8 Stream |✅ |✅ |
| CentOS 9 Stream |✅ |✅ |
| Debian 10.3 |✅ |✅ |
| Debian 11.3 |✅ |✅ |
| Debian 12.0 |✅ |✅ |
| Ubuntu 18.04 |✅ |✅ |
| Ubuntu 20.04 |✅ |✅ |
| Ubuntu 22.04 |✅ |✅ |
| Fedora 31 |✅ |✅ |
| Fedora 32 |✅ |✅ |
| AlmaLinux 9 |✅ |✅ |
| RockyLinux 8.6 |✅ |✅ |
| Arch Linux  |✅ |✅ |
| openSUSE 15.4 |✅ |✅ |
| 系统名称 |PHP53 |PHP54 |PHP55 |PHP56 |PHP70 |PHP71 |PHP72 |PHP73 |PHP74 |PHP80 |PHP81 |PHP82 |PHP83 |PHP84 |
| ----------------- |-------|-------|-------|-------|-------|-------|-------|-------|-------|-------|-------|-------|-------|-------|
| CentOS 7.9 |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |
| CentOS 8.4 |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |
| CentOS 8 Stream |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |
| CentOS 9 Stream |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |
| Debian 10.3 |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |
| Debian 11.3 |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |
| Debian 12 |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |
| Ubuntu 18.04 |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |
| Ubuntu 20.04 |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |
| Ubuntu 22.04 |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |
| Fedora 31 |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |
| Fedora 32 |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |
| AlmaLinux 9 |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |
| RockyLinux 8.6 |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |
| Arch Linux |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |
| openSUSE 15.4 |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |
| 系统名称 | MySQL55|MySQL56|MySQL57|MySQL80|
| ----------------- |-------|--------|-------|-------|
| CentOS 7.9 |✅ |✅ |✅ |✅ |
| CentOS 8.4 |✅ |✅ |✅ |✅ |
| CentOS 8 Stream |✅ |✅ |✅ |✅ |
| CentOS 9 Stream |✅ |✅ |✅ |✅ |
| Debian 10.3 |✅ |✅ |✅ |✅ |
| Debian 11.3 |✅ |✅ |✅ |✅ |
| Debian 12 |✅ |✅ |✅ |✅ |
| Ubuntu 18.04 |✅ |✅ |✅ |✅ |
| Ubuntu 20.04 |✅ |✅ |✅ |✅ |
| Ubuntu 22.04 |✅ |✅ |✅ |✅ |
| Fedora 31 |✅ |✅ |✅ |✅ |
| Fedora 32 |✅ |✅ |✅ |✅ |
| AlmaLinux 9 |✅ |✅ |✅ |✅ |
| RockyLinux 8.6 |✅ |✅ |✅ |✅ |
| Arch Linux |✅ |✅ |✅ |✅ |
| openSUSE 15.4 |✅ |✅ |✅ |✅ |
-4
View File
@@ -1,4 +0,0 @@
# 自动生成
certificate.pem - 证书信息
privateKey.pem - 私钥
Executable
+22
View File
@@ -0,0 +1,22 @@
[
{
"title":"全部",
"type":0,
"ps":""
},
{
"title":"运行环境",
"type":1,
"ps":""
},
{
"title":"系统工具",
"type":2,
"ps":""
},
{
"title":"其他插件",
"type":3,
"ps":""
}
]
-62
View File
@@ -1,62 +0,0 @@
FROM debian:10.12-slim
STOPSIGNAL SIGRTMIN+3
RUN rm -f /lib/systemd/system/multi-user.target.wants/* \
/etc/systemd/system/*.wants/* \
/lib/systemd/system/local-fs.target.wants/* \
/lib/systemd/system/sockets.target.wants/*udev* \
/lib/systemd/system/sockets.target.wants/*initctl* \
/lib/systemd/system/sysinit.target.wants/systemd-tmpfiles-setup* \
/lib/systemd/system/systemd-update-utmp*
RUN apt update -y && \
apt install -y devscripts && \
apt install -y wget zip unzip && \
apt-get install -y locales && \
localedef -i en_US -c -f UTF-8 -A /usr/share/locale/locale.alias en_US.UTF-8 && \
locale-gen en_US.UTF-8 && \
export LC_ALL=en_US.UTF-8 && \
export LANG=en_US.UTF-8 && \
export LANGUAGE=en_US.UTF-8 && \
localedef -i en_US -f UTF-8 en_US.UTF-8 && \
mkdir -p /www/server && \
mkdir -p /www/wwwroot && \
mkdir -p /www/wwwlogs && \
mkdir -p /www/backup/database && \
mkdir -p /www/backup/site && \
wget -O /tmp/dev.zip https://github.com/midoks/mdserver-web/archive/refs/heads/dev.zip && \
cd /tmp && unzip /tmp/dev.zip && \
mv -f /tmp/mdserver-web-dev /www/server/mdserver-web && \
rm -rf /tmp/dev.zip && \
rm -rf /tmp/mdserver-web-dev && \
cd /www/server/mdserver-web && \
bash scripts/install/debian.sh && \
cd /www/server/mdserver-web/ && \
/www/server/mdserver-web/bin/python tools.py username username && \
cd /www/server/mdserver-web/ && \
/www/server/mdserver-web/bin/python tools.py panel password
RUN cd /www/server/mdserver-web/plugins/php && \
bash install.sh install 74 && \
cd /www/server/mdserver-web/plugins/openresty && \
bash install.sh install 1.21.4 && \
cd /www/server/mdserver-web/plugins/mysql && \
bash install.sh install 5.6 && \
cd /www/server/mdserver-web/plugins/phpmyadmin && \
bash install.sh install 4.4.15 && \
systemctl enable openresty && \
systemctl enable php74 && \
systemctl enable mysql
RUN rm -rf /www/server/mysql/data
ADD ./start.sh /start.sh
ADD start.service /usr/lib/systemd/system/start.service
#RUN systemctl enable start
CMD [ "/lib/systemd/systemd", "log-level=info", "unit=sysinit.target" ]
EXPOSE 7200 80 443 888
VOLUME [ "/www" ]
-9
View File
@@ -1,9 +0,0 @@
[Unit]
Description=Customize the script
After=network.target
[Service]
ExecStart=/start.sh
[Install]
WantedBy=multi-user.target
-1
View File
@@ -1 +0,0 @@
#!/bin/bash
+13
View File
@@ -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
View File
@@ -1 +0,0 @@
logs
-359
View File
@@ -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()
-367
View File
@@ -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)
Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.0 KiB

@@ -1,34 +0,0 @@
<style>
.overflow_hide {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
vertical-align: middle;
}
</style>
<div class="bt-form">
<div class='plugin_version'></div>
<div class="bt-w-main">
<div class="bt-w-menu">
<p class="bgw" onclick="pluginService('acme_pandominassl_apply');">服务</p>
<p onclick="dnsapiList();">DNSAPI *</p>
<p onclick="emailList();">邮件地址 </p>
<p onclick="domainList()">域名SSL *</p>
<p onclick="pluginConfigTpl('acme_pandominassl_apply',$('.plugin_version').attr('version'));">HOOK</p>
<p onclick="pluginLogs('acme_pandominassl_apply','','run_log');">日志</p>
<p onclick="apaReadme();">相关说明</p>
</div>
<div class="bt-w-con pd15">
<div class="soft-man-con"></div>
</div>
</div>
</div>
<script type="text/javascript">
resetPluginWinWidth(1000);
resetPluginWinHeight(550);
$.getScript( "/plugins/file?name=acme_pandominassl_apply&f=js/common.js", function(){
pluginService('acme_pandominassl_apply', $('.plugin_version').attr('version'));
});
</script>
-860
View File
@@ -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')
-17
View File
@@ -1,17 +0,0 @@
{
"sort": 7,
"ps": "ACME泛域名SSL申请/管理",
"name": "acme_pandominassl_apply",
"title": "ACME泛域名SSL",
"shell": "install.sh",
"versions":["1.0"],
"tip": "soft",
"checks": "server/acme_pandominassl_apply",
"path": "server/acme_pandominassl_apply",
"display": 1,
"author": "ACME",
"date": "2024-09-12",
"home": "https://github.com/acmesh-official/acme.sh",
"type": 0,
"pid": "5"
}
@@ -1,54 +0,0 @@
#!/bin/bash
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin:/opt/homebrew/bin
export PATH
curPath=`pwd`
rootPath=$(dirname "$curPath")
rootPath=$(dirname "$rootPath")
serverPath=$(dirname "$rootPath")
VERSION=$2
# curl https://get.acme.sh | sh
# acme.sh --uninstall
# source /Users/xxx/.zshrc
# https://github.com/acmesh-official/acme.sh/wiki/dnsapi
# https://docs.dnspod.com/api-legacy/domains.html#get-the-domain-list
# cd /www/server/mdserver-web && python3 plugins/acme_pandominassl_apply/index.py run_hook
# cd /www/server/mdserver-web && python3 plugins/acme_pandominassl_apply/index.py run_sync_cf_data
# cd /www/server/mdserver-web && python3 plugins/acme_pandominassl_apply/index.py run_sync_dnspod_data
if [ -f ${rootPath}/bin/activate ];then
source ${rootPath}/bin/activate
fi
# pip install cloudflare
Install_App()
{
echo '正在安装脚本文件...'
mkdir -p $serverPath/source
mkdir -p $serverPath/acme_pandominassl_apply
echo "${VERSION}" > $serverPath/acme_pandominassl_apply/version.pl
cd ${rootPath} && python3 ${rootPath}/plugins/acme_pandominassl_apply/index.py start
echo '安装[ACME泛域名SSL]完成'
}
Uninstall_App()
{
cd ${rootPath} && python3 ${rootPath}/plugins/acme_pandominassl_apply/index.py stop
rm -rf $serverPath/acme_pandominassl_apply
echo "卸载[ACME泛域名SSL]成功"
}
action=$1
if [ "${1}" == 'install' ];then
Install_App
else
Uninstall_App
fi
@@ -1,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()
Binary file not shown.

Before

Width:  |  Height:  |  Size: 42 KiB

-31
View File
@@ -1,31 +0,0 @@
<style>
.overflow_hide {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
vertical-align: middle;
}
</style>
<div class="bt-form">
<div class='plugin_version'></div>
<div class="bt-w-main">
<div class="bt-w-menu">
<p class="bgw" onclick="pluginService('alist');">服务</p>
<p onclick="pluginInitD('alist');">自启动</p>
<p onclick="alistCommonFunc();">常用功能</p>
<p onclick="pluginConfigTpl('alist',$('.plugin_version').attr('version'));">配置修改</p>
<p onclick="pluginLogs('alist','','run_log');">运行日志</p>
<p onclick="alistReadme();">相关说明</p>
</div>
<div class="bt-w-con pd15">
<div class="soft-man-con" style="height: 520px; overflow: auto;"></div>
</div>
</div>
</div>
<script type="text/javascript">
$.getScript( "/plugins/file?name=alist&f=js/alist.js", function(){
pluginService('alist', $('.plugin_version').attr('version'));
});
</script>
-319
View File
@@ -1,319 +0,0 @@
# coding:utf-8
import sys
import io
import os
import time
import re
import json
web_dir = os.getcwd() + "/web"
if os.path.exists(web_dir):
sys.path.append(web_dir)
os.chdir(web_dir)
import core.mw as mw
app_debug = False
if mw.isAppleSystem():
app_debug = True
def getPluginName():
return 'alist'
def getPluginDir():
return mw.getPluginDir() + '/' + getPluginName()
def getServerDir():
return mw.getServerDir() + '/' + getPluginName()
def getInitDFile():
current_os = mw.getOs()
if current_os == 'darwin':
return '/tmp/' + getPluginName()
if current_os.startswith('freebsd'):
return '/etc/rc.d/' + getPluginName()
return '/etc/init.d/' + getPluginName()
def getInitDTpl():
path = getPluginDir() + "/init.d/" + getPluginName() + ".tpl"
return path
def getArgs():
args = sys.argv[3:]
tmp = {}
args_len = len(args)
if args_len == 1:
t = args[0].strip('{').strip('}')
if t.strip() == '':
tmp = []
else:
t = t.split(':')
tmp[t[0]] = t[1]
tmp[t[0]] = t[1]
elif args_len > 1:
for i in range(len(args)):
t = args[i].split(':')
tmp[t[0]] = t[1]
return tmp
def checkArgs(data, ck=[]):
for i in range(len(ck)):
if not ck[i] in data:
return (False, mw.returnJson(False, '参数:(' + ck[i] + ')没有!'))
return (True, mw.returnJson(True, 'ok'))
def getConf():
path = getServerDir() + "/data/config.json"
return path
def configTpl():
path = getPluginDir() + '/tpl'
pathFile = os.listdir(path)
tmp = []
for one in pathFile:
file = path + '/' + one
tmp.append(file)
return mw.getJson(tmp)
def readConfigTpl():
args = getArgs()
data = checkArgs(args, ['file'])
if not data[0]:
return data[1]
content = mw.readFile(args['file'])
content = contentReplace(content)
return mw.returnJson(True, 'ok', content)
def __release_port(port, ps = '开启端口'):
try:
from utils.firewall import Firewall as MwFirewall
MwFirewall.instance().addAcceptPort(port, ps, 'port')
return port
except Exception as e:
return "Release failed {}".format(e)
def __delete_port(port):
try:
from utils.firewall import Firewall as MwFirewall
MwFirewall.instance().delAcceptPortCmd(port, 'tcp')
return port
except Exception as e:
return "Delete failed {}".format(e)
def openPort():
content = mw.readFile(getConf())
data = json.loads(content)
http_port = data['scheme']['http_port']
__release_port(http_port, 'alist')
return True
def status():
cmd = "ps aux|grep alist |grep -v grep | grep -v python | grep -v mdserver-web | awk '{print $2}'"
data = mw.execShell(cmd)
if data[0] == '':
return 'stop'
return 'start'
def contentReplace(content):
service_path = mw.getServerDir()
content = content.replace('{$ROOT_PATH}', mw.getFatherDir())
content = content.replace('{$SERVER_PATH}', service_path)
content = content.replace('{$SERVER_APP}', service_path + '/'+getPluginName())
return content
def initDreplace():
file_tpl = getInitDTpl()
service_path = mw.getServerDir()
initD_path = getServerDir() + '/init.d'
if not os.path.exists(initD_path):
os.mkdir(initD_path)
file_bin = initD_path + '/' + getPluginName()
# initd replace
if not os.path.exists(file_bin):
content = mw.readFile(file_tpl)
content = content.replace('{$SERVER_PATH}', service_path)
mw.writeFile(file_bin, content)
mw.execShell('chmod +x ' + file_bin)
# systemd
systemDir = mw.systemdCfgDir()
systemService = systemDir + '/' + getPluginName() + '.service'
if os.path.exists(systemDir) and not os.path.exists(systemService):
systemServiceTpl = getPluginDir() + '/init.d/' + getPluginName() + '.service.tpl'
service_path = mw.getServerDir()
se_content = mw.readFile(systemServiceTpl)
se_content = se_content.replace('{$SERVER_PATH}', service_path)
mw.writeFile(systemService, se_content)
mw.execShell('systemctl daemon-reload')
time.sleep(1)
return file_bin
def alistOp(method):
file = initDreplace()
current_os = mw.getOs()
if current_os == "darwin":
data = mw.execShell(file + ' ' + method)
if data[1] == '':
return 'ok'
return data[1]
if current_os.startswith("freebsd"):
data = mw.execShell('service ' + getPluginName() + ' ' + method)
if data[1] == '':
return 'ok'
return data[1]
data = mw.execShell('systemctl ' + method + ' ' + getPluginName())
if data[1] == '':
return 'ok'
return data[1]
def start():
return alistOp('start')
def stop():
return alistOp('stop')
def restart():
status = alistOp('restart')
return status
def reload():
return alistOp('reload')
def initdStatus():
current_os = mw.getOs()
if current_os == 'darwin':
return "Apple Computer does not support"
if current_os.startswith('freebsd'):
initd_bin = getInitDFile()
if os.path.exists(initd_bin):
return 'ok'
shell_cmd = 'systemctl status ' + \
getPluginName() + ' | grep loaded | grep "enabled;"'
data = mw.execShell(shell_cmd)
if data[0] == '':
return 'fail'
return 'ok'
def initdInstall():
current_os = mw.getOs()
if current_os == 'darwin':
return "Apple Computer does not support"
# freebsd initd install
if current_os.startswith('freebsd'):
import shutil
source_bin = initDreplace()
initd_bin = getInitDFile()
shutil.copyfile(source_bin, initd_bin)
mw.execShell('chmod +x ' + initd_bin)
mw.execShell('sysrc ' + getPluginName() + '_enable="YES"')
return 'ok'
mw.execShell('systemctl enable ' + getPluginName())
return 'ok'
def initdUinstall():
current_os = mw.getOs()
if current_os == 'darwin':
return "Apple Computer does not support"
if current_os.startswith('freebsd'):
initd_bin = getInitDFile()
os.remove(initd_bin)
mw.execShell('sysrc ' + getPluginName() + '_enable="NO"')
return 'ok'
mw.execShell('systemctl disable ' + getPluginName())
return 'ok'
def runLog():
return getServerDir() + '/data/log/log.log'
def pSqliteDb(dbname='databases'):
pos_file = getServerDir() + '/data/'
file = pos_file + '/data.db'
name = 'data'
conn = mw.M(dbname).dbPos(pos_file, name)
return conn
def clearCopyTask():
conn = pSqliteDb('x_task_items')
conn.where('key=?', ('copy',)).setField('persist_data','[]')
restart()
return mw.returnJson(True, '清空成功并重启服务!')
def homePage():
content = mw.readFile(getConf())
data = json.loads(content)
http_port = data['scheme']['http_port']
ip = mw.getLocalIp()
if mw.isAppleSystem():
ip = '127.0.0.1'
url = 'http://'+ip+":"+str(http_port)
# print(url)
return mw.returnJson(True, 'ok!', url)
if __name__ == "__main__":
func = sys.argv[1]
if func == 'status':
print(status())
elif func == 'start':
print(start())
elif func == 'stop':
print(stop())
elif func == 'restart':
print(restart())
elif func == 'reload':
print(reload())
elif func == 'initd_status':
print(initdStatus())
elif func == 'initd_install':
print(initdInstall())
elif func == 'initd_uninstall':
print(initdUinstall())
elif func == 'conf':
print(getConf())
elif func == 'run_log':
print(runLog())
elif func == 'config_tpl':
print(configTpl())
elif func == 'read_config_tpl':
print(readConfigTpl())
elif func == 'clear_copy_task':
print(clearCopyTask())
elif func == 'home_page':
print(homePage())
else:
print('error')
-17
View File
@@ -1,17 +0,0 @@
{
"sort": 7,
"ps": "一个支持多种存储的文件列表程序",
"name": "alist",
"title": "Alist",
"shell": "install.sh",
"versions":["3.37.4","3.38.0"],
"tip": "soft",
"checks": "server/alist",
"path": "server/alist",
"display": 1,
"author": "alist",
"date": "2022-10-09",
"home": "https://alist.nn.ci",
"type": 0,
"pid": "5"
}
-13
View File
@@ -1,13 +0,0 @@
[Unit]
Description=A file list program that supports multiple storage
After=network.target
[Service]
Type=simple
WorkingDirectory={$SERVER_PATH}/alist
ExecStart={$SERVER_PATH}/alist/alist server
ExecReload=/bin/kill -USR2 $MAINPID
Restart=on-failure
[Install]
WantedBy=multi-user.target
-46
View File
@@ -1,46 +0,0 @@
#!/bin/sh
# chkconfig: 2345 55 25
# description: alist Service
### BEGIN INIT INFO
# Provides: alist
# Required-Start: $all
# Required-Stop: $all
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
# Short-Description: starts alist
# Description: starts the MDW-Web
### END INIT INFO
# Simple alist init.d script conceived to work on Linux systems
# as it does use of the /proc filesystem.
app_start(){
echo "Starting alist server..."
cd {$SERVER_PATH}/alist
./alist server >> {$SERVER_PATH}/alist/logs.pl 2>&1 &
}
app_stop(){
echo "dztasks stopped"
ps -ef| grep alist | grep -v grep | grep -v python | grep -v sh | awk '{print $2}'| xargs kill
}
case "$1" in
start)
app_start
;;
stop)
app_stop
;;
restart|reload)
app_stop
sleep 0.3
app_start
;;
*)
echo "Please use start or stop as first argument"
;;
esac
-93
View File
@@ -1,93 +0,0 @@
#!/bin/bash
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin:/opt/homebrew/bin
export PATH
curPath=`pwd`
rootPath=$(dirname "$curPath")
rootPath=$(dirname "$rootPath")
serverPath=$(dirname "$rootPath")
# cd /Users/midoks/Desktop/mwdev/server/mdserver-web/plugins/alist && bash install.sh install 3.37.4
# cd /www/server/mdserver-web/plugins/alist && bash install.sh install 3.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
-93
View File
@@ -1,93 +0,0 @@
function alistPost(method, version, args,callback){
var loadT = layer.msg('正在获取...', { icon: 16, time: 0, shade: 0.3 });
var req_data = {};
req_data['name'] = 'alist';
req_data['func'] = method;
req_data['version'] = version;
if (typeof(args) == 'string'){
req_data['args'] = JSON.stringify(toArrayObject(args));
} else {
req_data['args'] = JSON.stringify(args);
}
$.post('/plugins/run', req_data, function(data) {
layer.close(loadT);
if (!data.status){
//错误展示10S
layer.msg(data.msg,{icon:0,time:2000,shade: [10, '#000']});
return;
}
if(typeof(callback) == 'function'){
callback(data);
}
},'json');
}
function alistPostCallbak(method, version, args,callback){
var loadT = layer.msg('正在获取...', { icon: 16, time: 0, shade: 0.3 });
var req_data = {};
req_data['name'] = 'alist';
req_data['func'] = method;
args['version'] = version;
if (typeof(args) == 'string'){
req_data['args'] = JSON.stringify(toArrayObject(args));
} else {
req_data['args'] = JSON.stringify(args);
}
$.post('/plugins/callback', req_data, function(data) {
layer.close(loadT);
if (!data.status){
layer.msg(data.msg,{icon:0,time:2000,shade: [0.3, '#000']});
return;
}
if(typeof(callback) == 'function'){
callback(data);
}
},'json');
}
function clearTaskCopy(){
layer.confirm('您真的要清空复制任务吗?', { icon: 3, closeBtn: 2 }, function() {
alistPost('clear_copy_task', '', {}, function(data){
var rdata = $.parseJSON(data.data);
showMsg(rdata.msg, function(){},{ icon: rdata.status ? 1 : 2 });
});
});
}
function commonHomePage(){
alistPost('home_page', '', {}, function(data){
var rdata = $.parseJSON(data.data);
window.open(rdata.data);
});
}
function alistCommonFunc(){
var con = '';
con += '<hr/><p class="conf_p" style="text-align:center;">\
<button class="btn btn-default btn-sm" onclick="commonHomePage()">主页</button>\
<button class="btn btn-default btn-sm" onclick="clearTaskCopy()">清空复制任务</button>\
</p>';
$(".soft-man-con").html(con);
}
function alistReadme(){
var readme = '<ul class="help-info-text c7">';
readme += '<li>手动开启默认端口:5244</li>';
readme += '<li>默认admin:admin</li>';
readme += '<li>手动改密码: cd /www/server/alist && ./alist admin set NEW_PASSWORD</li>';
readme += '</ul>';
$('.soft-man-con').html(readme);
}
-424
View File
@@ -1,424 +0,0 @@
# coding:utf-8
'''
doc: https://docs.python.org/zh-cn/3/library/ftplib.html
'''
import sys
import io
import os
import time
import re
import json
import paramiko
import ftplib
web_dir = os.getcwd() + "/web"
if os.path.exists(web_dir):
sys.path.append(web_dir)
os.chdir(web_dir)
import core.mw as mw
DEBUG = True
BLOCK_SIZE = 1024 * 1024 * 2
# BLOCK_SIZE = 50
PROGRESS_FILE_NAME = "PROGRESS_FILE_NAME"
"""
=============自定义异常===================
"""
class OsError(Exception):
"""OS端异常"""
class ObjectNotFound(OsError):
"""对象不存在时抛出的异常"""
def __init__(self, *args, **kwargs):
message = "文件对象不存在。"
super(ObjectNotFound, self).__init__(message, *args, **kwargs)
class APIError(Exception):
"""API参数错误异常"""
def __init__(self, *args, **kwargs):
_api_error_msg = 'API资料校验失败,请核实!'
super(APIError, self).__init__(_api_error_msg, *args, **kwargs)
class FtpPSClient:
_title = "FTP"
_name = "ftp"
__host = None
__port = None
__user = None
__password = None
default_port = 21
default_backup_path = "/backup/"
config_file = "cfg.json"
def __init__(self, load_config=True, timeout=600):
self.timeout = timeout
if load_config:
data = self.get_config()
self.injection_config(data)
def get_config(self):
default_config = {
"ftp_host": '',
"ftp_user": '',
"ftp_pass": '',
"backup_path": self.default_backup_path
}
cfg = mw.getServerDir() + "/backup_ftp/" + self.config_file
if os.path.exists(cfg):
data = mw.readFile(cfg)
return json.loads(data)
else:
return default_config
def injection_config(self, data):
self.__host = host = data["ftp_host"].strip()
if host.find(':') == -1:
self.__port = self.default_port
else:
self.__host = host.split(':')[0]
self.__port = host.split(':')[1]
self.__user = data['ftp_user'].strip()
self.__password = data['ftp_pass'].strip()
bp = data['backup_path'].strip()
if bp:
self.backup_path = self.getPath(bp)
else:
self.backup_path = self.getPath(self.default_backup_path)
def authorize(self):
try:
if self.timeout is not None:
ftp = ftplib.FTP(timeout=self.timeout)
else:
ftp = ftplib.FTP()
debuglevel = 0
# if DEBUG:
# debuglevel = 3
ftp.set_debuglevel(debuglevel)
# ftp.set_pasv(True)
ftp.connect(self.__host, int(self.__port))
ftp.login(self.__user, self.__password)
return ftp
except Exception as e:
raise OsError("无法连接FTP客户端,请检查配置参数是否正确!")
# 取目录路径
def getPath(self, path):
if path[-1:] != '/':
path += '/'
if path[:1] != '/':
path = '/' + path
return path.replace('//', '/')
def generateDownloadUrl(self, object_name):
return 'ftp://' + \
self.__user + ':' + \
self.__password + '@' + \
self.__host + ':' + \
"/" + object_name
def buildDirName(self, data_type, file_name):
import re
prefix_dict = {
"site": "web",
"database": "db",
"path": "path",
}
file_regx = prefix_dict.get(data_type) + r"_(.+)_20\d+_\d+\."
sub_search = re.search(file_regx, file_name)
sub_path_name = ""
if sub_search:
sub_path_name = sub_search.groups()[0]
sub_path_name += '/'
# 构建OS存储路径
object_name = self.backup_path + \
data_type + '/' + \
sub_path_name + \
file_name
return object_name
def uploadFile(self, filename, data_type=None, *args, **kwargs):
client = self.authorize()
local_file_name = filename
filename = os.path.abspath(filename)
dirname = os.path.dirname(filename)
temp_name = os.path.split(filename)[1]
object_name = self.buildDirName(data_type, temp_name)
upload_tmp_dir = os.path.join(dirname, ".upload_tmp")
if not os.path.exists(upload_tmp_dir):
os.mkdir(upload_tmp_dir)
print("|-正在上传文件到 {}".format(object_name))
total_bytes = os.path.getsize(filename)
object_md5_name = mw.md5(object_name)
pg_file = os.path.join(upload_tmp_dir, object_md5_name + ".pl")
block_size = BLOCK_SIZE
if kwargs.get("block_size"):
try:
block_size = float(kwargs.get("block_size"))
except:
pass
remote_file_size = None
if not os.path.exists(pg_file):
# import uuid
# uid = str(uuid.uuid1())
progress_info = {
"filename": local_file_name,
"total_bytes": total_bytes,
"uploaded_bytes": 0,
}
mw.writeFile(pg_file, json.dumps(progress_info))
else:
progress_info = json.loads(mw.readFile(pg_file))
if total_bytes == progress_info.get("total_bytes"):
# 取远程文件大小
_max_loop = 10
while _max_loop > 0:
try:
time.sleep(1)
remote_file_size = client.size(object_name)
if remote_file_size > total_bytes:
remote_file_size = None
break
except Exception as e:
if DEBUG:
print(type(e))
print(e)
_max_loop -= 1
else:
remote_file_size = None
uploaded_bytes = 0 if remote_file_size is None else remote_file_size
dir_name = os.path.split(object_name)[0]
if dir_name:
self.createDirP(dir_name)
upload_start = time.time()
try:
if total_bytes > 1024 * 1024 * 1024:
with open(local_file_name, 'rb') as file_handler:
if remote_file_size is not None:
file_handler.seek(remote_file_size)
client.voidcmd("TYPE I")
datasock = ''
esize = ''
datasock, esize = client.ntransfercmd(
"STOR " + object_name, remote_file_size)
while True:
buf = file_handler.read(block_size)
if not len(buf):
break
datasock.sendall(buf)
uploaded_bytes += len(buf)
if DEBUG:
print('\ruploading %.2f%%' %
(float(uploaded_bytes) / total_bytes * 100))
print("uploaded_bytes", uploaded_bytes)
if uploaded_bytes == total_bytes:
break
datasock.close()
if DEBUG:
print('close data handle')
try:
client.voidcmd('NOOP')
except Exception as e:
if DEBUG:
print("Send NOOP command error:")
print(e)
else:
if DEBUG:
print('keep alive cmd success')
client.voidresp()
if DEBUG:
print('No loop cmd')
else:
# 小于1G文件直接上传
file_handler = open(local_file_name, "rb")
client.storbinary('STOR %s' % object_name,
file_handler, blocksize=block_size)
file_handler.close()
except Exception as e:
print(str(e))
completed_file_size = None
_max_loop = 10
while _max_loop > 0:
try:
time.sleep(1)
completed_file_size = client.size(object_name)
break
except Exception as e:
_max_loop -= 1
if DEBUG:
print("size error:" + str(e))
# 上传完成
if completed_file_size == total_bytes:
if DEBUG:
upload_completed = time.time()
upload_diff = upload_completed - upload_start
print("文件上传成功, 耗时: {}s。".format(upload_diff))
if os.path.exists(pg_file):
os.remove(pg_file)
return True
else:
if os.path.exists(pg_file):
os.remove(pg_file)
print("文件上传后大小不一致!")
print("completed_file_size:" + str(completed_file_size))
print("total_bytes:", total_bytes)
print("object_md5_name:", object_md5_name)
print("pg_file:", pg_file)
print("filename:", filename)
print("dirname:", dirname)
print("object_name:", object_name)
return False
def createDirP(self, dir_name):
"""创建远程目录
:param dir_name: 目录名称
:return:
"""
try:
dirnames = dir_name.split('/')
ftp = self.authorize()
# ftp.cwd(get.path);
for dirname in dirnames:
if not dirname or not dirname.strip():
continue
try:
flist = ftp.nlst()
if not dirname in flist:
ftp.mkd(dirname)
except:
# print("mlsd mode.")
try:
flist = list(ftp.mlsd())[1:]
for f in flist:
if dirname == f[0]:
break
else:
ftp.mkd(dirname)
except:
return False
ftp.cwd(dirname)
return True
except:
return False
def createDir(self, path, name):
ftp = self.authorize()
path = self.getPath(path)
ftp.cwd(path)
try:
ftp.mkd(name)
ftp.close()
return True
except Exception as e:
print(str(e))
ftp.close()
return False
def deleteDir(self, path, dir_name):
try:
ftp = self.authorize()
ftp.rmd(dir_name)
return True
except ftplib.error_perm as e:
print("deleteDir:" + str(e) + ":" + dir_name)
except Exception as e:
print(e)
return False
def deleteFile(self, filename):
try:
ftp = self.authorize()
ftp.delete(filename)
return True
except Exception as e:
print(str(e))
return False
def getList(self, path="/"):
ftp = self.authorize()
path = self.getPath(path)
ftp.cwd(path)
mlsd = False
try:
files = list(ftp.mlsd())
mlsd = True
except:
try:
files = ftp.nlst(path)
mlsd = False
except:
raise RuntimeError("FTP服务器数据返回异常!")
ftp.close()
# print(files)
f_list = []
dirs = []
data = []
default_time = '1971/01/01 01:01:01'
for dt in files:
# print(dt)
if mlsd:
dt_name = dt[0]
dt_info = dt[1]
else:
if dt.find("/") >= 0:
dt = dt.split("/")[-1]
tmp = {}
tmp['name'] = dt_name
if dt_name == '.' or dt_name == '..':
continue
tmp['time'] = dt_info['modify']
try:
tmp['size'] = dt_info['size']
tmp['type'] = "File"
tmp['download'] = self.generateDownloadUrl(path + dt_name)
f_list.append(tmp)
except:
tmp['size'] = dt_info['sizd']
tmp['type'] = None
tmp['download'] = ''
dirs.append(tmp)
data = dirs + f_list
mlist = {}
mlist['path'] = path
mlist['list'] = data
return mlist
Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

-101
View File
@@ -1,101 +0,0 @@
<style>
.upyunCon {
height: 428px;
}
.up-place {
height: 62px;
border-bottom: 1px solid #ddd;
}
.up-place .btn {
border-radius: 0;
}
.up-place .place-input {
background-color: #f3f3f3;
border: 1px solid #ccc;
height: 30px;
line-height: 28px;
overflow: hidden;
margin: 1px 0 0 -1px;
width: 340px;
}
.place-input ul {
display: inline-block;
position: relative;
width: auto;
}
.place-input ul li {
background: url("/static/img/ico/ico-ltr.png") no-repeat right center;
float: left;
padding-left: 10px;
padding-right: 18px;
}
.place-input ul li a {
height: 28px;
cursor: pointer;
display: inline-block;
}
.upyunlist {
height: 516px;
overflow: auto;
}
.up-bottom {
background-color: #fafafa;
border-top: 1px solid #eee;
bottom: 0;
position: absolute;
width: 100%;
}
.up-use {
line-height: 50px
}
.list-list .cursor span {
line-height: 30px;
}
.btn-title {
margin-top: 1px
}
.tip {
font-size: 10px;
font-style: oblique;
color: green;
}
</style>
<div class="upyunCon">
<div class="up-place pd15">
<button id="backBtn" class="btn btn-default btn-sm glyphicon glyphicon-arrow-left pull-left" title="后退"></button>
<input id="myPath" style="display:none;" type="text" value="">
<div class="place-input pull-left">
<div style="width:1400px;height:28px"><ul></ul></div>
</div>
<button class="refreshBtn btn btn-default btn-sm glyphicon glyphicon-refresh pull-left mr20" title="刷新" style="margin-left:-1px;"></button>
<button class="btn btn-default btn-sm pull-right btn-title" onclick="upyunApi()">帐户设置</button>
<button class="btn btn-default btn-sm pull-right mr20 btn-title" onclick="createDir()">新建文件夹</button>
</div>
<div class="upyunlist pd15">
<div class="divtable" style="margin-bottom:15px">
<table class="table table-hover">
<thead><tr><th>名称</th><th>大小</th><th>更新时间</th><th class="text-right">操作</th></tr></thead>
<tbody class="list-list"></tbody>
</table>
</div>
</div>
</div>
<script type="text/javascript">
$.getScript( "/plugins/file?name=backup_ftp&f=js/backup_ftp.js", function(){
osList('/');
});
</script>
-318
View File
@@ -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')
-17
View File
@@ -1,17 +0,0 @@
{
"title":"FTP存储空间",
"hook":["backup"],
"tip":"soft",
"name":"backup_ftp",
"type":"运行环境",
"ps":"将网站或数据库打包备份到FTP存储空间",
"versions":["1.0"],
"install_pre_inspection":false,
"shell":"install.sh",
"checks":"server/backup_ftp",
"path": "server/backup_ftp",
"author":"midoks",
"home":"https://github.com/midoks/mdserver-web",
"date":"2022-10-23",
"pid": "4"
}
-31
View File
@@ -1,31 +0,0 @@
#!/bin/bash
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH
curPath=`pwd`
rootPath=$(dirname "$curPath")
rootPath=$(dirname "$rootPath")
serverPath=$(dirname "$rootPath")
sys_os=`uname`
Install_App()
{
mkdir -p ${serverPath}/backup_ftp
echo "${1}" > ${serverPath}/backup_ftp/version.pl
echo '安装完成'
}
Uninstall_App()
{
rm -rf ${serverPath}/backup_ftp
}
action=$1
if [ "${1}" == 'install' ];then
Install_App $2
else
Uninstall_App $2
fi
-258
View File
@@ -1,258 +0,0 @@
function bkfPost(method,args,callback){
var _args = null;
if (typeof(args) == 'string'){
_args = JSON.stringify(toArrayObject(args));
} else {
_args = JSON.stringify(args);
}
var loadT = layer.msg('正在获取...', { icon: 16, time: 0, shade: 0.3 });
$.post('/plugins/run', {name:'backup_ftp', func:method, args:_args}, function(data) {
layer.close(loadT);
if (!data.status){
layer.msg(data.msg,{icon:0,time:2000,shade: [0.3, '#000']});
return;
}
if(typeof(callback) == 'function'){
callback(data);
}
},'json');
}
function getFtpLocalTime(data){
var str = data.slice(0,4)+"/"+data.slice(4,6)+"/"+data.slice(6,8)
+ " " + data.slice(8,10)+":"+data.slice(10,12)+":"+data.slice(12,14);
return str;
}
// 自定义部分
var i = null;
//设置API
function upyunApi(){
bkfPost('conf', {}, function(rdata){
var rdata = $.parseJSON(rdata.data);
var token = rdata.data;
var check_status = token.use_sftp;
var sftp_checked = check_status === "true" ? " checked=\"checked\"" : "";
if (typeof(token.ftp_host) == 'undefined'){
token.ftp_host = '';
}
if (typeof(token.ftp_user) == 'undefined'){
token.ftp_user = '';
}
if (typeof(token.ftp_pass) == 'undefined'){
token.ftp_pass = '';
}
if (typeof(token.backup_path) == 'undefined'){
token.backup_path = '';
}
var apicon = '<div class="bingfa mtb15" style="padding-bottom:0px;">\
<p>\
<span class="span_tit">使用SFTP:</span>\ <input style="width: 20px; vertical-align:middle;" type="checkbox" name="use_sftp"'+sftp_checked+'> 是否使用SFTP进行数据传输 \
</p>\
<p>\
<span class="span_tit">Host:</span>\
<input placeholder="请输入主机地址" style="width: 200px;" type="text" name="upyun_service" value="'+token.ftp_host+'"> *服务器地址,FTP默认端口21, SFTP默认端口22\
</p>\
<p>\
<span class="span_tit">用户名:</span>\
<input style="width: 200px;" type="text" name="ftp_username" value="'+token.ftp_user+'"> *指定用户名\
</p>\
<p>\
<span class="span_tit">密码:</span>\
<input style="width: 200px;" type="password" name="ftp_password" value="'+token.ftp_pass+'"> *登录密码\
</p>\
<p>\
<span class="span_tit">存储位置:</span>\
<input placeholder="请输入存储位置" style="width: 200px;" type="text" name="backup_path" value="'+token.backup_path+'"> *相对于根目录的路径,默认是/backup\
</p>\
</div>';
layer.open({
type: 1,
area: "600px",
title: "FTP/SFTP帐户设置",
closeBtn: 1,
shift: 5,
shadeClose: false,
btn: ['确定','取消'],
content:apicon,
yes:function(index,layero){
var data = {
use_sftp:$("input[name='use_sftp']").prop('checked'),
ftp_user:$("input[name='ftp_username']").val(),
ftp_pass:$("input[name='ftp_password']").val(),
ftp_host:$("input[name='upyun_service']").val(),
backup_path:$("input[name='backup_path']").val()
}
bkfPost('set_config', data, function(rdata){
var rdata = $.parseJSON(rdata.data);
if (rdata.status){
showMsg(rdata.msg,function(){
layer.close(index);
osList("/");
},{icon:1},2000);
} else{
layer.msg(rdata.msg,{icon:2});
}
})
},
});
});
}
function createDir(){
layer.open({
type: 1,
area: "400px",
title: "创建目录",
closeBtn: 1,
shift: 5,
shadeClose: false,
btn: ['确定','取消'],
content:'<div class="bingfa bt-form c6" style="padding-bottom: 10px;">\
<p>\
<span class="span_tit">目录名称:</span>\
<input style="width: 200px;" type="text" name="newPath" value="">\
</p>\
</div>',
success:function(){
$("input[name='newPath']").focus().keyup(function(e){
if(e.keyCode == 13) $(".layui-layer-btn0").click();
});
},
yes:function(index,layero){
var name = $("input[name='newPath']").val();
if(name == ''){
layer.msg('目录名称不能为空!',{icon:2});
return;
}
var path = $("#myPath").val();
var dirname = name;
// var loadT = layer.msg('正在创建目录['+dirname+']...',{icon:16,time:0,shade: [0.3, '#000']});
bkfPost('create_dir', {path:path,name:dirname}, function(data){
var rdata = $.parseJSON(data.data);
if(rdata.status) {
showMsg(rdata.msg, function(){
layer.close(index);
osList(path);
} ,{icon:1}, 2000);
} else{
layer.msg(rdata.msg,{icon:2});
}
});
}
});
}
//删除文件
function deleteFile(name, is_dir){
if (is_dir === false){
safeMessage('删除文件','删除后将无法恢复,真的要删除['+name+']吗?',function(){
var path = $("#myPath").val();
var filename = name;
bkfPost('delete_file', {filename:filename,path:path}, function(rdata){
var rdata = $.parseJSON(rdata.data);
showMsg(rdata.msg,function(){
osList(path);
},{icon:rdata.status?1:2},2000);
});
});
} else {
safeMessage('删除文件夹','删除后将无法恢复,真的要删除['+name+']吗?',function(){
var path = $("#myPath").val();
bkfPost('delete_dir', {dir_name:name,path:path}, function(rdata){
var rdata = $.parseJSON(rdata.data);
showMsg(rdata.msg,function(){
osList(path);
},{icon:rdata.status?1:2},2000);
});
});
}
}
function osList(path){
bkfPost('get_list', {path:path}, function(rdata){
var rdata = $.parseJSON(rdata.data);
if(rdata.status === false){
showMsg(rdata.msg,function(){
upyunApi();
},{icon:2},2000);
return;
}
var mlist = rdata.data;
// console.log(mlist);
var listBody = ''
var listFiles = ''
for(var i=0;i<mlist.list.length;i++){
if(mlist.list[i].type == null){
listBody += '<tr><td class="cursor" onclick="osList(\''+(path+'/'+mlist.list[i].name).replace('//','/')+'\')"><span class="ico ico-folder"></span>\<span>'+mlist.list[i].name+'</span></td>\
<td>-</td>\
<td>-</td>\
<td class="text-right"><a class="btlink" onclick="deleteFile(\''+mlist.list[i].name+'\', true)">删除</a></td></tr>'
}else{
listFiles += '<tr><td class="cursor"><span class="ico ico-file"></span>\<span>'+mlist.list[i].name+'</span></td>\
<td>'+toSize(mlist.list[i].size)+'</td>\
<td>'+getFtpLocalTime(mlist.list[i].time)+'</td>\
<td class="text-right"><a target="_blank" href="'+mlist.list[i].download+'" class="btlink">下载</a> | <a class="btlink" onclick="deleteFile(\''+mlist.list[i].name+'\', false)">删除</a></td></tr>'
}
}
listBody += listFiles;
var pathLi='';
var tmp = path.split('/')
var pathname = '';
var n = 0;
for(var i=0;i<tmp.length;i++){
if(n > 0 && tmp[i] == '') continue;
var dirname = tmp[i];
if(dirname == '') {
dirname = '根目录';
n++;
}
pathname += '/' + tmp[i];
pathname = pathname.replace('//','/');
pathLi += '<li><a title="'+pathname+'" onclick="osList(\''+pathname+'\')">'+dirname+'</a></li>';
}
var um = 1;
if(tmp[tmp.length-1] == '') um = 2;
var backPath = tmp.slice(0,tmp.length-um).join('/') || '/';
$('#myPath').val(path);
$(".upyunCon .place-input ul").html(pathLi);
$(".upyunlist .list-list").html(listBody);
upPathLeft();
$('#backBtn').unbind().click(function() {
osList(backPath);
});
$('.upyunCon .refreshBtn').unbind().click(function(){
osList(path);
});
});
}
//计算当前目录偏移
function upPathLeft(){
var UlWidth = $(".place-input ul").width();
var SpanPathWidth = $(".place-input").width() - 20;
var Ml = UlWidth - SpanPathWidth;
if(UlWidth > SpanPathWidth ){
$(".place-input ul").css("left",-Ml)
}
else{
$(".place-input ul").css("left",0)
}
}
// $('.layui-layer-page').css('height','670px');
Binary file not shown.

Before

Width:  |  Height:  |  Size: 254 B

-74
View File
@@ -1,74 +0,0 @@
<div class="bt-form">
<div class="bt-w-main">
<div class="bt-w-menu">
<p class="bgw" onclick="pluginService('clean');">服务</p>
<p onclick="pluginConfig('clean');">配置修改</p>
<p onclick="commonFunc();">常用功能</p>
<p onclick="pluginLogs('clean',null,'run_log');">运行日志</p>
<p onclick="pRead()">说明</p>
</div>
<div class="bt-w-con pd15">
<div class="soft-man-con"></div>
</div>
</div>
</div>
<script type="text/javascript">
pluginService('clean');
function cleanPost(method, version, args, callback){
var loadT = layer.msg('正在获取...', { icon: 16, time: 0, shade: 0.3 });
var req_data = {};
req_data['name'] = 'clean';
req_data['func'] = method;
req_data['version'] = version;
if (typeof(args) == 'string'){
req_data['args'] = JSON.stringify(toArrayObject(args));
} else {
req_data['args'] = JSON.stringify(args);
}
$.post('/plugins/run', req_data, function(data) {
layer.close(loadT);
if (!data.status){
layer.msg(data.msg,{icon:0,time:2000,shade: [10, '#000']});
return;
}
if(typeof(callback) == 'function'){
callback(data);
}
},'json');
}
function cleanRun(){
cleanPost('clean_run', '', {}, function(rdata){
// var rdata = $.parseJSON(rdata.data);
// layer.msg(rdata.msg, { icon: rdata.status ? 1 : 2 });
});
layer.msg("执行成功!", { icon: 1});
}
function commonFunc(){
con = '<hr/><p class="conf_p" style="text-align:center;">\
<button class="btn btn-default btn-sm" onclick="cleanRun()">手动执行</button> \
</p>';
$(".soft-man-con").html(con);
}
function pRead(){
var readme = '<ul class="help-info-text c7">';
readme += '<li>启动后,加入到计划任务每隔7天[0:15]执行。</li>';
readme += '<li>【*】代表要删除。</li>';
readme += '<li>添加/删除清除日志,可在`配置修改`中添加/删除。</li>';
readme += '<li>如果是目录,会寻找【.log】后缀的文件清空。</li>';
readme += '</ul>';
$('.soft-man-con').html(readme);
}
</script>
-294
View File
@@ -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')
-16
View File
@@ -1,16 +0,0 @@
{
"title":"日志清理",
"tip":"soft",
"name":"clean",
"type":"运行环境",
"ps":"日志清理",
"versions":"1.0",
"updates":"1.0",
"shell":"install.sh",
"checks":"server/clean",
"path": "server/clean",
"author":"clean",
"home":"https://github.com/midoks",
"date":"2021-11-23",
"pid": "5"
}
-42
View File
@@ -1,42 +0,0 @@
#!/bin/bash
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH
# cd /Users/midoks/Desktop/mwdev/server/mdserver-web/plugins/mongodb && /bin/bash install.sh install 5.0.4
# cd /www/server/mdserver-web/plugins/mongodb && /bin/bash install.sh install 5.0.4
curPath=`pwd`
rootPath=$(dirname "$curPath")
rootPath=$(dirname "$rootPath")
serverPath=$(dirname "$rootPath")
VERSION=$2
if [ -f ${rootPath}/bin/activate ];then
source ${rootPath}/bin/activate
fi
Install_app()
{
echo '正在安装脚本文件...'
mkdir -p $serverPath/clean
cd ${rootPath} && python3 ${rootPath}/plugins/clean/index.py start
echo "${VERSION}" > $serverPath/clean/version.pl
echo '安装完成'
}
Uninstall_app()
{
cd ${rootPath} && python3 ${rootPath}/plugins/clean/index.py stop
rm -rf $serverPath/clean
echo "Uninstall_clean"
}
action=$1
if [ "${1}" == 'install' ];then
Install_app
else
Uninstall_app
fi
-129
View File
@@ -1,129 +0,0 @@
# coding:utf-8
import sys
import io
import os
import time
import json
web_dir = os.getcwd() + "/web"
if os.path.exists(web_dir):
sys.path.append(web_dir)
os.chdir(web_dir)
import core.mw as mw
from utils.crontab import crontab as MwCrontab
app_debug = False
if mw.isAppleSystem():
app_debug = True
def getPluginName():
return 'clean'
def getPluginDir():
return mw.getPluginDir() + '/' + getPluginName()
def getServerDir():
return mw.getServerDir() + '/' + getPluginName()
def getTaskConf():
conf = getServerDir() + "/task_config.json"
return conf
def getConfigData():
conf = getTaskConf()
if os.path.exists(conf):
return json.loads(mw.readFile(getTaskConf()))
return {
"task_id": -1,
"period": "day-n",
"where1": "7",
"hour": "0",
"minute": "15",
}
def createBgTask():
removeBgTask()
createBgTaskByName(getPluginName())
def createBgTaskByName(name):
args = getConfigData()
_name = "[勿删]日志清理[" + name + "]"
res = mw.M("crontab").field("id, name").where("name=?", (_name,)).find()
if res:
return True
if "task_id" in args and args["task_id"] > 0:
res = mw.M("crontab").field("id, name").where(
"id=?", (args["task_id"],)).find()
if res and res["id"] == args["task_id"]:
print("计划任务已经存在!")
return True
mw_dir = mw.getPanelDir()
cmd = '''
mw_dir=%s
rname=%s
plugin_path=%s
script_path=%s
logs_file=$plugin_path/${rname}.log
''' % (mw_dir, name, getServerDir(), getPluginDir())
cmd += 'echo "★【`date +"%Y-%m-%d %H:%M:%S"`】 STSRT★" >> $logs_file' + "\n"
cmd += 'echo ">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>" >> $logs_file' + "\n"
cmd += 'echo "python3 $script_path/index.py clean >> $logs_file 2>&1"' + "\n"
cmd += 'cd $mw_dir && python3 $script_path/index.py clean >> $logs_file 2>&1' + "\n"
cmd += 'echo "【`date +"%Y-%m-%d %H:%M:%S"`】 END★" >> $logs_file' + "\n"
cmd += 'echo "<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<" >> $logs_file' + "\n"
params = {
'name': _name,
'type': args['period'],
'week': "",
'where1': args['where1'],
'hour': args['hour'],
'minute': args['minute'],
'save': "",
'backup_to': "",
'stype': "toShell",
'sname': '',
'sbody': cmd,
'url_address': '',
}
task_id = MwCrontab.instance().add(params)
if task_id > 0:
args["task_id"] = task_id
args["name"] = name
mw.writeFile(getTaskConf(), json.dumps(args))
def removeBgTask():
cfg = getConfigData()
if "task_id" in cfg and cfg["task_id"] > 0:
res = mw.M("crontab").field("id, name").where(
"id=?", (cfg["task_id"],)).find()
if res and res["id"] == cfg["task_id"]:
data = MwCrontab.instance().delete(cfg["task_id"])
if data[0]:
cfg["task_id"] = -1
mw.writeFile(getTaskConf(), json.dumps(cfg))
return True
return False
if __name__ == "__main__":
if len(sys.argv) > 1:
action = sys.argv[1]
if action == "remove":
removeBgTask()
elif action == "add":
createBgTask()
Binary file not shown.

Before

Width:  |  Height:  |  Size: 31 KiB

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

Before

Width:  |  Height:  |  Size: 2.3 KiB

-35
View File
@@ -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>
-101
View File
@@ -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')
-36
View File
@@ -1,36 +0,0 @@
{
"hook":[
{
"tag":"menu",
"menu": {
"title":"数据管理",
"name":"data_query",
"path":"static/html/index.html",
"css_path":"static/css/data.css",
"js_path":"static/js/app.js"
}
},
{
"tag":"global_static",
"global_static": {
"title":"数据管理",
"name":"data_query",
"css_path":"static/css/ico.css"
}
}
],
"sort": 7,
"ps": "数据简单管理(memcached,redis,mongodb)",
"name": "data_query",
"title": "数据管理",
"shell": "install.sh",
"versions":"1.0",
"tip": "soft",
"checks": "server/data_query",
"path": "server/data_query",
"display": 1,
"author": "midoks",
"date": "2024-01-22",
"type": 0,
"pid": "2"
}
-33
View File
@@ -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
-258
View File
@@ -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 ----------------------------------
-360
View File
@@ -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'
-349
View File
@@ -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 ----------------------------------
-751
View File
@@ -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'
-48
View File
@@ -1,48 +0,0 @@
.main-content .safe {
position: relative;
}
.mask_layer {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgb(255 255 255 / 60%);
z-index: 9998;
}
.mask_layer .prompt_description {
width: 350px;
height: 60px;
line-height: 60px;
text-align: center;
position: absolute;
left: 50%;
top: 50%;
margin-top: -25px;
margin-left: -175px;
background: #fff;
box-shadow: 1px 1px 50px rgb(0 0 0 / 30%);
border: 1px solid #eee;
border-radius: 2px;
font-size: 13px;
}
#mongodb_search xm-select {
min-height: 30px;
line-height: 30px;
}
#mongodb_search xm-select .xm-label .xm-label-block{
height: 20px;
line-height: 20px;
}
#mongodb td:last-child, th:last-child {
width: 44px;
position:sticky;
right:0; /* 首行永远固定在左侧 */
z-index:1;
background-color:white;
}
-11
View File
@@ -1,11 +0,0 @@
/* menu start */
.menu .current .menu_plugin_data_query:hover {
background-image: url("/plugins/file?name=data_query&f=ico.png");
}
.menu .menu_plugin_data_query {
background-image: url("/plugins/file?name=data_query&f=ico.png");
}
/* menu end */
-305
View File
@@ -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>
File diff suppressed because it is too large Load Diff
+350
View File
@@ -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');
Binary file not shown.

After

Width:  |  Height:  |  Size: 553 B

BIN
View File
Binary file not shown.

After

Width:  |  Height:  |  Size: 553 B

+415
View File
@@ -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>
+16
View File
@@ -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"
}
+52
View File
@@ -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
+495
View File
@@ -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"
}
]
+495
View File
@@ -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"
}
]
+37
View File
@@ -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":""
}
]
-58
View File
@@ -1,58 +0,0 @@
<style type="text/css">
.docker-sub {
border-bottom: 1px solid #ccc;
height: 30px;
line-height: 30px;
margin-bottom: 15px;
}
.docker-sub .on {
border-bottom: 2px solid #20a53a;
color: #20a53a;
font-weight: bold;
font-family: "宋体";
}
.docker-sub span {
display: inline-block;
font-size: 14px;
height: 30px;
padding: 0 25px;
cursor: pointer;
}
.docker-environments {
resize: none;
width: 330px;
height: 80px;
line-height: 20px;
padding: 5px 8px;
border: 1px solid #ddd;
font-size: 12px;
}
</style>
<div class="bt-form">
<div class='plugin_version'></div>
<div class="bt-w-main">
<div class="bt-w-menu">
<p class="bgw" onclick="pluginService('docker');">服务</p>
<p onclick="pluginInitD('docker');">自启动</p>
<p onclick="dockerConList();">容器列表</p>
<p onclick="dockerImageList();">镜像列表</p>
<p onclick="dockerImageOutput();">镜像导出</p>
<p onclick="dockerIpList();">IP地址池</p>
<p onclick="repoList();">仓库</p>
</div>
<div class="bt-w-con pd15">
<div class="soft-man-con"></div>
</div>
</div>
</div>
<script type="text/javascript">
resetPluginWinWidth(800);
resetPluginWinHeight(400);
$.getScript( "/plugins/file?name=docker&f=js/docker.js", function(){
pluginService('docker', $('.plugin_version').attr('version'));
});
</script>
-868
View File
@@ -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')
-19
View File
@@ -1,19 +0,0 @@
{
"sort": 7,
"ps": "Docker是一个开源的应用容器引擎",
"name": "docker",
"title": "Docker",
"shell": "install.sh",
"versions":["1.0"],
"updates":["1.0"],
"tip": "soft",
"checks": "server/docker",
"path": "server/docker",
"display": 1,
"author": "Zend",
"date": "2017-04-01",
"home": "https://docker.io",
"doc1":"https://docker-py.readthedocs.io/",
"type": 0,
"pid": "4"
}
-104
View File
@@ -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

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