Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
894c98702e | ||
|
|
b39d95d29e | ||
|
|
1c66f691a0 | ||
|
|
aaaea5cb44 | ||
|
|
349a7b9496 | ||
|
|
2c6d70f1d9 | ||
|
|
12b8afb7e9 | ||
|
|
01664afc0c | ||
|
|
5c059ffca5 | ||
|
|
d122f7812a | ||
|
|
fc5174ba64 |
@@ -34,5 +34,5 @@ jobs:
|
||||
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 &
|
||||
gunicorn -c setting.py app:app
|
||||
python3 task.py &
|
||||
|
||||
@@ -34,5 +34,5 @@ jobs:
|
||||
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 &
|
||||
gunicorn -c setting.py app:app
|
||||
python3 task.py &
|
||||
|
||||
@@ -34,5 +34,5 @@ jobs:
|
||||
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 &
|
||||
gunicorn -c setting.py app:app
|
||||
python3 task.py &
|
||||
|
||||
@@ -34,5 +34,5 @@ jobs:
|
||||
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 &
|
||||
gunicorn -c setting.py app:app
|
||||
python3 task.py &
|
||||
|
||||
@@ -31,8 +31,8 @@ jobs:
|
||||
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
|
||||
cd /www/server/mdserver-web/plugins/openresty && sudo bash install.sh install 1.21.4.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 &
|
||||
gunicorn -c setting.py app:app
|
||||
python3 task.py &
|
||||
|
||||
@@ -64,5 +64,5 @@ jobs:
|
||||
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 &
|
||||
gunicorn -c setting.py app:app
|
||||
python3 task.py &
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
name: sync2gitee
|
||||
on: [push]
|
||||
|
||||
jobs:
|
||||
repo-sync:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Mirror the Github organization repos to Gitee.
|
||||
uses: Yikun/hub-mirror-action@master
|
||||
with:
|
||||
src: 'github/midoks'
|
||||
dst: 'gitee/midoks'
|
||||
dst_key: ${{ secrets.GITEE_PRIVATE_KEY }}
|
||||
dst_token: ${{ secrets.GITEE_TOKEN }}
|
||||
static_list: "mdserver-web"
|
||||
force_update: true
|
||||
@@ -87,7 +87,6 @@ venv/
|
||||
ENV/
|
||||
env.bak/
|
||||
venv.bak/
|
||||
lib/python*
|
||||
|
||||
# Spyder project settings
|
||||
.spyderproject
|
||||
@@ -99,12 +98,6 @@ lib/python*
|
||||
# mkdocs documentation
|
||||
/site
|
||||
|
||||
bin
|
||||
pyvenv.cfg
|
||||
include
|
||||
share
|
||||
pip-selfcheck.json
|
||||
|
||||
# mypy
|
||||
.mypy_cache/
|
||||
|
||||
@@ -115,29 +108,9 @@ 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
|
||||
|
||||
logs/*
|
||||
data/*.db
|
||||
data/control.conf
|
||||
data/iplist.txt
|
||||
data/json/index.json
|
||||
data/json/config.json
|
||||
@@ -150,11 +123,27 @@ data/default.pl
|
||||
data/backup.pl
|
||||
data/debug.pl
|
||||
data/default_site.pl
|
||||
data/ssl.pl
|
||||
|
||||
ssl/input.pl
|
||||
|
||||
lib/python*
|
||||
|
||||
bin
|
||||
pyvenv.cfg
|
||||
include
|
||||
share
|
||||
pip-selfcheck.json
|
||||
|
||||
scripts/init.d/mw
|
||||
scripts/mdserver-web
|
||||
|
||||
data/api_login.txt
|
||||
data/sessions
|
||||
data/ssl.pl
|
||||
data/port.pl
|
||||
data/ipv6.pl
|
||||
data/restart.pl
|
||||
data/ssl.pl
|
||||
data/edate.pl
|
||||
data/osname.pl
|
||||
data/only_netio_counters.pl
|
||||
@@ -166,16 +155,19 @@ 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/system_safe
|
||||
plugins/tamper_proof
|
||||
plugins/tamper_proof_*
|
||||
plugins/cryptocurrency_trade
|
||||
plugins/op_load_balance
|
||||
plugins/gdrive
|
||||
plugins/mtproxy
|
||||
plugins/zimg
|
||||
plugins/bk_demo
|
||||
plugins/mail
|
||||
@@ -183,16 +175,8 @@ plugins/fastdfs
|
||||
plugins/v2ray
|
||||
plugins/frp
|
||||
plugins/file_search
|
||||
plugins/proxysql
|
||||
plugins/tidb
|
||||
plugins/goedge-admin
|
||||
plugins/goedge-node
|
||||
plugins/goedge-happy
|
||||
debug.out
|
||||
|
||||
|
||||
|
||||
|
||||
/logs
|
||||
/data
|
||||
/plugins/flexcdn
|
||||
/plugins/tools
|
||||
/plugins/choose-linux-python
|
||||
*.md5
|
||||
/bak
|
||||
|
||||
@@ -1,201 +0,0 @@
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [midoks] [midoks of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
@@ -1,235 +0,0 @@
|
||||
<p align="center">
|
||||
<img alt="logo" src="https://cdn.jsdelivr.net/gh/midoks/mdserver-web@latest/route/static/logo.png" height="140" />
|
||||
<h3 align="center">mdserver-web</h3>
|
||||
<p align="center">一款简单Linux面板服务</p>
|
||||
<p align="center">强烈推荐系统:debian</p>
|
||||
</p>
|
||||
|
||||
### 简介
|
||||
|
||||
简单的Linux面板,感谢BT.CN写出如此好的web管理软件。我一看到,就知道这是我一直想要的页面化管理方式。
|
||||
复制了后台管理界面,按照自己想要的方式写了一版。
|
||||
|
||||

|
||||

|
||||

|
||||

|
||||

|
||||
|
||||
|
||||
[](https://github.com/midoks/mdserver-web/wiki)
|
||||
[](https://www.jsdelivr.com/package/gh/midoks/mdserver-web)
|
||||
|
||||
* SSH终端工具
|
||||
* 面板收藏功能
|
||||
* 网站备份功能
|
||||
* 插件方式管理
|
||||
|
||||
基本上可以使用,后续会继续优化!欢迎提供意见!
|
||||
|
||||
- 吹水组 - https://t.me/mdserver_web
|
||||
- 交流论坛 - https://bbs.midoks.icu
|
||||
|
||||
```
|
||||
如果出现问题,最好私给我面板信息。不要让我猜。如果不提供,不要提出问题,自行解决。 — 座右铭
|
||||
Talk is cheap, show me the code. -- linus
|
||||
```
|
||||
|
||||
- [兼容性测试报告](/compatibility.md)
|
||||
- [常用命令说明](/cmd.md) [ mw default ] [ mw dev ]
|
||||
|
||||
### 特别赞助
|
||||
|
||||
- [虚位以待](https://bbs.midoks.icu)
|
||||
|
||||
### 主要插件介绍
|
||||
|
||||
* OpenResty - 轻量级,占有内存少,并发能力强。
|
||||
* PHP[53-85] - PHP是世界上最好的编程语言。
|
||||
* MySQL - 一种关系数据库管理系统。
|
||||
* MariaDB - 是MySQL的一个重要分支。
|
||||
* MySQL[community] - 一种关系数据库管理系统。
|
||||
* MongoDB - 一种非关系NOSQL数据库管理系统。
|
||||
* PostgreSQL - 功能强大的开源数据库。
|
||||
* phpMyAdmin - 著名Web端MySQL管理工具。
|
||||
* Memcached - 一个高性能的分布式内存对象缓存系统。
|
||||
* Redis - 一个高性能的KV数据库。
|
||||
* PureFtpd - 一款专注于程序健壮和软件安全的免费FTP服务器软件。
|
||||
* Gogs - 一款极易搭建的自助Git服务。
|
||||
* Rsyncd - 通用同步服务。
|
||||
|
||||
|
||||
### 插件开发相关
|
||||
|
||||
```
|
||||
插件文档还不完善,如果有不明白的地方提Issue! 我会尽力完善。
|
||||
如果你自己写了插件,想分享出来,也可以提Issue。
|
||||
```
|
||||
|
||||
- 简单例子 - https://github.com/mw-plugin/simple-plugin
|
||||
- 插件地址 - https://github.com/mw-plugin
|
||||
- 开发文档 - https://github.com/midoks/mdserver-web/wiki/插件开发
|
||||
|
||||
## 其他插件
|
||||
|
||||
- OP鉴权 - https://github.com/mw-plugin/op_auth
|
||||
|
||||
|
||||
# Note
|
||||
|
||||
```
|
||||
phpMyAdmin[4.4.15]支持MySQL[5.5-5.7]
|
||||
phpMyAdmin[5.2.1]支持MySQL[8.0+]
|
||||
|
||||
PHP[53-72]支持phpMyAdmin[4.4.15]
|
||||
PHP[72-84]支持phpMyAdmin[5.2.1]
|
||||
```
|
||||
|
||||
# 郑重声明
|
||||
|
||||
不卖、不会监控(统计使用除外)、更不会注入病毒,大家使用方便(望大家不吝捐赠)。
|
||||
|
||||
- https://www.youtube.com/watch?v=2taa5K-Jmmw
|
||||
|
||||
|
||||
# AD - VPS推荐 - 🙏
|
||||
|
||||
| 服务商 | LOGO | 推广地址 | 优惠码 |
|
||||
| ------------- |----------|-----------|-------|
|
||||
| digitalvirt |[](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.6
|
||||
|
||||
- 常规优化。
|
||||
|
||||
### JSDelivr安装地址
|
||||
|
||||
- 初始安装
|
||||
|
||||
```
|
||||
bash <(curl --insecure -fsSL https://cdn.jsdelivr.net/gh/midoks/mdserver-web@latest/scripts/install.sh)
|
||||
```
|
||||
|
||||
- 直接更新
|
||||
|
||||
```
|
||||
bash <(curl --insecure -fsSL https://cdn.jsdelivr.net/gh/midoks/mdserver-web@latest/scripts/update.sh)
|
||||
```
|
||||
|
||||
- 卸载脚本
|
||||
|
||||
```
|
||||
wget --no-check-certificate -O uninstall.sh https://cdn.jsdelivr.net/gh/midoks/mdserver-web@latest/scripts/uninstall.sh && bash uninstall.sh
|
||||
```
|
||||
|
||||
### 备用地址
|
||||
|
||||
- 初始安装
|
||||
|
||||
```
|
||||
|
||||
bash <(curl --insecure -fsSL https://raw.githubusercontent.com/midoks/mdserver-web/dev/scripts/install.sh)
|
||||
bash <(curl --insecure -fsSL https://code.midoks.icu/midoks/mdserver-web/raw/branch/master/scripts/install.sh)
|
||||
```
|
||||
|
||||
- 直接更新
|
||||
|
||||
```
|
||||
bash <(curl --insecure -fsSL https://raw.githubusercontent.com/midoks/mdserver-web/master/scripts/update.sh)
|
||||
```
|
||||
|
||||
- 卸载脚本
|
||||
|
||||
```
|
||||
wget --no-check-certificate -O uninstall.sh https://raw.githubusercontent.com/midoks/mdserver-web/master/scripts/uninstall.sh && bash uninstall.sh
|
||||
```
|
||||
|
||||
|
||||
### 通用软件安装[命令行安装]
|
||||
|
||||
- 需已经安装mdserver-web
|
||||
|
||||
```
|
||||
bash <(curl --insecure -fsSL https://raw.githubusercontent.com/midoks/mdserver-web/dev/scripts/quick/app.sh)
|
||||
```
|
||||
|
||||
|
||||
### DEV使用
|
||||
|
||||
```
|
||||
bash <(curl --insecure -fsSL https://raw.githubusercontent.com/midoks/mdserver-web/dev/scripts/install.sh)
|
||||
bash <(curl --insecure -fsSL https://raw.githubusercontent.com/midoks/mdserver-web/dev/scripts/update.sh)
|
||||
|
||||
bash <(curl --insecure -fsSL https://raw.githubusercontent.com/midoks/mdserver-web/dev/scripts/install_dev.sh)
|
||||
bash <(curl --insecure -fsSL https://raw.githubusercontent.com/midoks/mdserver-web/dev/scripts/update_dev.sh)
|
||||
|
||||
wget --no-check-certificate -O install.sh https://raw.githubusercontent.com/midoks/mdserver-web/dev/scripts/install.sh && bash install.sh
|
||||
|
||||
wget --no-check-certificate -O uninstall.sh https://raw.githubusercontent.com/midoks/mdserver-web/dev/scripts/uninstall.sh && bash uninstall.sh
|
||||
|
||||
bash <(curl --insecure -fsSL https://raw.githubusercontent.com/midoks/mdserver-web/dev/scripts/quick/debug.sh)
|
||||
|
||||
bash <(curl --insecure -fsSL https://code.midoks.icu/midoks/mdserver-web/raw/branch/dev/scripts/install.sh)
|
||||
bash <(curl --insecure -fsSL https://code.midoks.icu/midoks/mdserver-web/raw/branch/dev/scripts/update.sh)
|
||||
|
||||
bash <(curl --insecure -fsSL https://code.midoks.icu/midoks/mdserver-web/raw/branch/dev/scripts/install_dev.sh)
|
||||
bash <(curl --insecure -fsSL https://code.midoks.icu/midoks/mdserver-web/raw/branch/dev/scripts/update_dev.sh)
|
||||
```
|
||||
|
||||
### 旧版安装/更新
|
||||
```
|
||||
curl --insecure -fsSL https://raw.githubusercontent.com/midoks/mdserver-web/dev/scripts/old/install.sh | bash
|
||||
curl --insecure -fsSL https://raw.githubusercontent.com/midoks/mdserver-web/dev/scripts/old/update.sh | bash
|
||||
```
|
||||
|
||||
### 偶然问题
|
||||
- 在安装/更新时,突然出现python插件pip,psutil,可尝试如下解决方案
|
||||
```
|
||||
cd /www/server/mdserver-web rm -rf lib
|
||||
cd /www/server/mdserver-web && rm -rf lib64
|
||||
cd /www/server/mdserver-web && rm -rf bin
|
||||
cd /www/server/mdserver-web && rm -rf include
|
||||
|
||||
mw update/mw update_dev/mw dev
|
||||
```
|
||||
|
||||
### 捐赠地址 USDT(TRC20)
|
||||
|
||||
TVbNgrpeGBGZVm5gTLa21ADP7RpnPFhjya
|
||||
|
||||
日行一善,以后必定大富大贵
|
||||
|
||||
|
||||
### 支付宝赞助
|
||||
|
||||
[](https://cdn.jsdelivr.net/gh/midoks/mdserver-web@latest/route/static/img/alipay_zz.png)
|
||||
|
||||
|
||||
### 无图不真相
|
||||
|
||||
[](https://cdn.jsdelivr.net/gh/midoks/mdserver-web@latest/route/static/mdw.jpg)
|
||||
|
||||
|
||||
### Stargazers over time
|
||||
|
||||
[](https://starchart.cc/midoks/mdserver-web)
|
||||
|
||||
|
||||
### 感谢开发赞助
|
||||
|
||||
[](https://digitalvirt.com/aff.php?aff=154)
|
||||
|
||||
### 授权许可
|
||||
|
||||
本项目采用 Apache 开源授权许可证,完整的授权说明已放置在 [LICENSE](https://github.com/midoks/mdserver-web/blob/master/LICENSE) 文件中。
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
#!/bin/bash
|
||||
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin:/usr/local/lib/python2.7/bin
|
||||
|
||||
|
||||
|
||||
find . -name .DS_Store -print0 | xargs -0 git rm -f --ignore-unmatch
|
||||
find . -name .DS_Store | xargs rm -rf
|
||||
find . -type d -name "*.pyc" | xargs rm -rf
|
||||
@@ -1,126 +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 gthread -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 gthread -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()
|
||||
{
|
||||
APP_LIST=`ps -ef|grep app:app |grep -v grep|awk '{print $2}'`
|
||||
APP_LIST=($APP_LIST)
|
||||
for p in ${APP_LIST[@]}
|
||||
do
|
||||
kill -9 $p > /dev/null 2>&1
|
||||
done
|
||||
|
||||
TASK_LIST=`ps -ef|grep panel_task.py | grep -v grep |awk '{print $2}'`
|
||||
TASK_LIST=($TASK_LIST)
|
||||
for p in ${TASK_LIST[@]}
|
||||
do
|
||||
kill -9 $p > /dev/null 2>&1
|
||||
done
|
||||
|
||||
zzpids=`ps -A -o stat,ppid,pid | grep -e '^[Zz]' | awk '{print $2}'`
|
||||
zzpids=($zzpids)
|
||||
for p in ${zzpids[@]}
|
||||
do
|
||||
kill -9 ${p} > /dev/null 2>&1
|
||||
done
|
||||
}
|
||||
|
||||
case "$1" in
|
||||
'start') mw_start;;
|
||||
'stop') mw_stop;;
|
||||
'restart')
|
||||
mw_stop
|
||||
mw_start
|
||||
;;
|
||||
'debug')
|
||||
mw_stop
|
||||
mw_start_debug
|
||||
;;
|
||||
'panel')
|
||||
mw_stop
|
||||
mw_start_panel
|
||||
;;
|
||||
'task')
|
||||
# mw_stop
|
||||
mw_start_bgtask
|
||||
;;
|
||||
esac
|
||||
@@ -1,87 +0,0 @@
|
||||
### 常用命令说明
|
||||
|
||||
|
||||
- 面板相关命令
|
||||
|
||||
```
|
||||
mw start | 启动面板服务器
|
||||
mw stop | 停止面板服务器
|
||||
mw restart | 重启面板服务器
|
||||
mw default | 显示登录信息
|
||||
mw db | 快捷连接MySQL
|
||||
mw redis | 快捷连接Redis
|
||||
mw valkey | 快捷连接valkey
|
||||
mw mongodb | 快捷连接MongoDB
|
||||
mw pgdb | 快捷连接PostgreSQL
|
||||
mw ssh | 快捷连接ssh
|
||||
----------------------------------------
|
||||
mw open | 开启面板
|
||||
mw close | 关闭面板
|
||||
|
||||
mw debug | 开发测试
|
||||
mw venv | 进入虚拟环境
|
||||
mw mirror | 切换镜像
|
||||
mw install_app | 快捷安装常用软件
|
||||
mw update | 更新到正式
|
||||
mw dev/update_dev | 更新到开发
|
||||
|
||||
service mw [start|stop|reload|restart|status]
|
||||
```
|
||||
|
||||
- OpenResty
|
||||
|
||||
```
|
||||
|
||||
systemctl [start|stop|reload|restart|status] openresty
|
||||
|
||||
```
|
||||
|
||||
- MySQL
|
||||
|
||||
```
|
||||
|
||||
systemctl [start|stop|reload|restart|status] mysql
|
||||
|
||||
```
|
||||
|
||||
- MariaDB
|
||||
|
||||
```
|
||||
|
||||
systemctl [start|stop|reload|restart|status] mariadb
|
||||
|
||||
```
|
||||
|
||||
- PHP
|
||||
|
||||
```
|
||||
|
||||
systemctl [start|stop|reload|restart|status] php[54-81]
|
||||
|
||||
systemctl start php71
|
||||
```
|
||||
|
||||
- Redis
|
||||
|
||||
```
|
||||
|
||||
systemctl [start|stop|reload|restart|status] redis
|
||||
|
||||
```
|
||||
|
||||
- Memcached
|
||||
|
||||
```
|
||||
|
||||
systemctl [start|stop|reload|restart|status] memcached
|
||||
|
||||
```
|
||||
|
||||
|
||||
- sphinx
|
||||
|
||||
```
|
||||
|
||||
systemctl [start|stop|reload|restart|status] sphinx
|
||||
|
||||
```
|
||||
@@ -1,71 +0,0 @@
|
||||
### 兼容性测试报告
|
||||
|
||||
快速测试命令
|
||||
|
||||
```
|
||||
cd /www/server/mdserver-web/scripts/quick && bash debug.sh
|
||||
cd /www/server/mdserver-web/plugins/php/versions && bash all_test.sh
|
||||
```
|
||||
|
||||
|
||||
| 系统名称 | 面板 | OpenResty |
|
||||
| ----------------- |---------------|---------------|
|
||||
| CentOS 7.9 |✅ |✅ |
|
||||
| CentOS 8.4 |✅ |✅ |
|
||||
| CentOS 8 Stream |✅ |✅ |
|
||||
| CentOS 9 Stream |✅ |✅ |
|
||||
| Debian 10.3 |✅ |✅ |
|
||||
| Debian 11.3 |✅ |✅ |
|
||||
| Debian 12.0 |✅ |✅ |
|
||||
| Ubuntu 18.04 |✅ |✅ |
|
||||
| Ubuntu 20.04 |✅ |✅ |
|
||||
| Ubuntu 22.04 |✅ |✅ |
|
||||
| Fedora 31 |✅ |✅ |
|
||||
| Fedora 32 |✅ |✅ |
|
||||
| AlmaLinux 9 |✅ |✅ |
|
||||
| RockyLinux 8.6 |✅ |✅ |
|
||||
| Arch Linux |✅ |✅ |
|
||||
| openSUSE 15.4 |✅ |✅ |
|
||||
|
||||
|
||||
|
||||
| 系统名称 |PHP53 |PHP54 |PHP55 |PHP56 |PHP70 |PHP71 |PHP72 |PHP73 |PHP74 |PHP80 |PHP81 |PHP82 |PHP83 |PHP84 |
|
||||
| ----------------- |-------|-------|-------|-------|-------|-------|-------|-------|-------|-------|-------|-------|-------|-------|
|
||||
| CentOS 7.9 |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |
|
||||
| CentOS 8.4 |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |
|
||||
| CentOS 8 Stream |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |
|
||||
| CentOS 9 Stream |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |
|
||||
| Debian 10.3 |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |
|
||||
| Debian 11.3 |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |
|
||||
| Debian 12 |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |
|
||||
| Ubuntu 18.04 |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |
|
||||
| Ubuntu 20.04 |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |
|
||||
| Ubuntu 22.04 |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |
|
||||
| Fedora 31 |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |
|
||||
| Fedora 32 |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |
|
||||
| AlmaLinux 9 |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |
|
||||
| RockyLinux 8.6 |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |
|
||||
| Arch Linux |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |
|
||||
| openSUSE 15.4 |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |
|
||||
|
||||
|
||||
|
||||
| 系统名称 | MySQL55|MySQL56|MySQL57|MySQL80|
|
||||
| ----------------- |-------|--------|-------|-------|
|
||||
| CentOS 7.9 |✅ |✅ |✅ |✅ |
|
||||
| CentOS 8.4 |✅ |✅ |✅ |✅ |
|
||||
| CentOS 8 Stream |✅ |✅ |✅ |✅ |
|
||||
| CentOS 9 Stream |✅ |✅ |✅ |✅ |
|
||||
| Debian 10.3 |✅ |✅ |✅ |✅ |
|
||||
| Debian 11.3 |✅ |✅ |✅ |✅ |
|
||||
| Debian 12 |✅ |✅ |✅ |✅ |
|
||||
| Ubuntu 18.04 |✅ |✅ |✅ |✅ |
|
||||
| Ubuntu 20.04 |✅ |✅ |✅ |✅ |
|
||||
| Ubuntu 22.04 |✅ |✅ |✅ |✅ |
|
||||
| Fedora 31 |✅ |✅ |✅ |✅ |
|
||||
| Fedora 32 |✅ |✅ |✅ |✅ |
|
||||
| AlmaLinux 9 |✅ |✅ |✅ |✅ |
|
||||
| RockyLinux 8.6 |✅ |✅ |✅ |✅ |
|
||||
| Arch Linux |✅ |✅ |✅ |✅ |
|
||||
| openSUSE 15.4 |✅ |✅ |✅ |✅ |
|
||||
|
||||
@@ -1,4 +0,0 @@
|
||||
# 自动生成
|
||||
|
||||
certificate.pem - 证书信息
|
||||
privateKey.pem - 私钥
|
||||
@@ -1,62 +0,0 @@
|
||||
FROM debian:10.12-slim
|
||||
|
||||
STOPSIGNAL SIGRTMIN+3
|
||||
|
||||
RUN rm -f /lib/systemd/system/multi-user.target.wants/* \
|
||||
/etc/systemd/system/*.wants/* \
|
||||
/lib/systemd/system/local-fs.target.wants/* \
|
||||
/lib/systemd/system/sockets.target.wants/*udev* \
|
||||
/lib/systemd/system/sockets.target.wants/*initctl* \
|
||||
/lib/systemd/system/sysinit.target.wants/systemd-tmpfiles-setup* \
|
||||
/lib/systemd/system/systemd-update-utmp*
|
||||
|
||||
RUN apt update -y && \
|
||||
apt install -y devscripts && \
|
||||
apt install -y wget zip unzip && \
|
||||
apt-get install -y locales && \
|
||||
localedef -i en_US -c -f UTF-8 -A /usr/share/locale/locale.alias en_US.UTF-8 && \
|
||||
locale-gen en_US.UTF-8 && \
|
||||
export LC_ALL=en_US.UTF-8 && \
|
||||
export LANG=en_US.UTF-8 && \
|
||||
export LANGUAGE=en_US.UTF-8 && \
|
||||
localedef -i en_US -f UTF-8 en_US.UTF-8 && \
|
||||
mkdir -p /www/server && \
|
||||
mkdir -p /www/wwwroot && \
|
||||
mkdir -p /www/wwwlogs && \
|
||||
mkdir -p /www/backup/database && \
|
||||
mkdir -p /www/backup/site && \
|
||||
wget -O /tmp/dev.zip https://github.com/midoks/mdserver-web/archive/refs/heads/dev.zip && \
|
||||
cd /tmp && unzip /tmp/dev.zip && \
|
||||
mv -f /tmp/mdserver-web-dev /www/server/mdserver-web && \
|
||||
rm -rf /tmp/dev.zip && \
|
||||
rm -rf /tmp/mdserver-web-dev && \
|
||||
cd /www/server/mdserver-web && \
|
||||
bash scripts/install/debian.sh && \
|
||||
cd /www/server/mdserver-web/ && \
|
||||
/www/server/mdserver-web/bin/python tools.py username username && \
|
||||
cd /www/server/mdserver-web/ && \
|
||||
/www/server/mdserver-web/bin/python tools.py panel password
|
||||
|
||||
RUN cd /www/server/mdserver-web/plugins/php && \
|
||||
bash install.sh install 74 && \
|
||||
cd /www/server/mdserver-web/plugins/openresty && \
|
||||
bash install.sh install 1.21.4 && \
|
||||
cd /www/server/mdserver-web/plugins/mysql && \
|
||||
bash install.sh install 5.6 && \
|
||||
cd /www/server/mdserver-web/plugins/phpmyadmin && \
|
||||
bash install.sh install 4.4.15 && \
|
||||
systemctl enable openresty && \
|
||||
systemctl enable php74 && \
|
||||
systemctl enable mysql
|
||||
|
||||
RUN rm -rf /www/server/mysql/data
|
||||
|
||||
ADD ./start.sh /start.sh
|
||||
ADD start.service /usr/lib/systemd/system/start.service
|
||||
#RUN systemctl enable start
|
||||
|
||||
CMD [ "/lib/systemd/systemd", "log-level=info", "unit=sysinit.target" ]
|
||||
|
||||
EXPOSE 7200 80 443 888
|
||||
|
||||
VOLUME [ "/www" ]
|
||||
@@ -1,9 +0,0 @@
|
||||
[Unit]
|
||||
Description=Customize the script
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
ExecStart=/start.sh
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
@@ -1 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
After Width: | Height: | Size: 55 KiB |
@@ -0,0 +1,96 @@
|
||||
<!DOCTYPE html>
|
||||
|
||||
<html lang="en">
|
||||
<head>
|
||||
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
|
||||
<link rel="icon" type="image/x-icon" href="./static/images/logo.png">
|
||||
<link type="text/css" href="./static/css/pages.css" media="all" rel="stylesheet">
|
||||
|
||||
<title>mdserver-web | Linux开源面板</title>
|
||||
<meta name="generator" content="Jekyll v3.9.3">
|
||||
<meta property="og:title" content="Linux开源面板">
|
||||
<meta property="og:locale" content="en_US">
|
||||
<meta name="description" content="Linux开源面板">
|
||||
<meta property="og:description" content="Linux开源面板">
|
||||
<link rel="canonical" href="https://www.midoks.me/">
|
||||
<meta property="og:url" content="https://www.midoks.me/">
|
||||
<meta property="og:site_name" content="Linux开源面板">
|
||||
<meta property="og:type" content="website">
|
||||
<meta name="twitter:card" content="summary">
|
||||
<meta property="twitter:title" content="Linux开源面板">
|
||||
<meta name="twitter:site" content="@github">
|
||||
<script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-7736695762519033"
|
||||
crossorigin="anonymous"></script>
|
||||
</head>
|
||||
|
||||
<body class="home" style="">
|
||||
|
||||
<section id="hero-spot" class="hero-spot">
|
||||
<a href="https://pages.github.com/">
|
||||
<img src="./static/images/logo.png" alt="Linux开源面板" class="logo">
|
||||
</a>
|
||||
|
||||
<h1>管理你的服务器</h1>
|
||||
|
||||
<a href="https://github.com/midoks/mdserver-web" class="help-link">Github</a>
|
||||
|
||||
<div id="slideshow">
|
||||
<img src="./static/images/mw.jpg" alt="mdserver-web" class="slide active" width="893">
|
||||
</div>
|
||||
</section>
|
||||
|
||||
|
||||
<section id="tutorial" class="tutorial">
|
||||
<h1>准备好开始了吗?</h1>
|
||||
|
||||
|
||||
<!-- ### Start of tutorials -->
|
||||
<ul id="user-site" class="tutorial-list wrapper active">
|
||||
|
||||
|
||||
<li id="terminal-step-1" class="option-terminal">
|
||||
<h4>安装面板</h4>
|
||||
|
||||
<div class="terminal">
|
||||
<div class="header"></div>
|
||||
<div class="shell">
|
||||
<p><span class="path">~</span><span class="prompt">$</span>curl -fsSL https://cdn.jsdelivr.net/gh/midoks/mdserver-web@latest/scripts/install.sh | bash</p>
|
||||
<p><span class="path">~</span><span class="prompt">$</span>mw default</p>
|
||||
<p>==================================================================</p>
|
||||
<p>MW-Panel default info!</p>
|
||||
<p>==================================================================</p>
|
||||
<p>MW-Panel-Url: http://0.0.0.0:13148/xxx</p>
|
||||
<p>username: yrajbw9q</p>
|
||||
<p>password: oqxotjgb</p>
|
||||
<p>Warning:</p>
|
||||
<p>If you cannot access the panel. </p>
|
||||
<p>release the following port (44010|888|80|443|22) in the security group.</p>
|
||||
<p>==================================================================</p>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
|
||||
</ul>
|
||||
<!-- End of user site tutorial -->
|
||||
|
||||
</section>
|
||||
<!-- End of tutorial section -->
|
||||
|
||||
<script src="./static/js/jquery.js"></script>
|
||||
<script>window.slides = {"mdserver-web":"mw","mw-php":"php",}</script>
|
||||
<script src="./static/js/application.js"></script>
|
||||
|
||||
<footer class="page-footer">
|
||||
<ul class="site-footer-links right">
|
||||
<li><a href="https://bbs.midoks.icu">论坛</a></li>
|
||||
</ul>
|
||||
|
||||
<ul class="site-footer-links">
|
||||
<li>mdserver-web ©2018-∞ Linux面板</li>
|
||||
<li><a href="https://github.com/midoks/mdserver-web">Github</a></li>
|
||||
</ul>
|
||||
</footer>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
@@ -1 +0,0 @@
|
||||
logs
|
||||
@@ -1,362 +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()
|
||||
|
||||
# echo "True" > /www/server/mdserver-web/logs/panel_task.lock
|
||||
lock_file = mw.getTriggerTaskLockFile()
|
||||
try:
|
||||
if os.path.exists(lock_file):
|
||||
bash_list = thisdb.getTaskList(status=-1)
|
||||
for task in bash_list:
|
||||
thisdb.setTaskStatus(task['id'], 0)
|
||||
|
||||
run_list = thisdb.getTaskList(status=0)
|
||||
for run_task in run_list:
|
||||
start = int(time.time())
|
||||
thisdb.setTaskData(run_task['id'], start=start)
|
||||
thisdb.setTaskStatus(run_task['id'], -1)
|
||||
|
||||
if run_task['type'] == 'download':
|
||||
argv = run_task['cmd'].split('|mw|')
|
||||
downloadFile(argv[0], argv[1])
|
||||
elif run_task['type'] == 'execshell':
|
||||
execShell(run_task['cmd'])
|
||||
|
||||
end = int(time.time())
|
||||
thisdb.setTaskData(run_task['id'], end=end)
|
||||
thisdb.setTaskStatus(run_task['id'], 1)
|
||||
|
||||
if thisdb.getTaskUnexecutedCount() < 1:
|
||||
os.remove(lock_file)
|
||||
except Exception as e:
|
||||
print('runPanelTask:',mw.getTracebackInfo())
|
||||
|
||||
# 网站到期处理
|
||||
def siteEdateCheck():
|
||||
try:
|
||||
from utils.site import sites as MwSites
|
||||
website_edate = thisdb.getOption('website_edate', default='0000-00-00')
|
||||
now_time_ymd = time.strftime('%Y-%m-%d', time.localtime())
|
||||
|
||||
if website_edate == now_time_ymd:
|
||||
return False
|
||||
site_list = thisdb.getSitesEdateList(now_time_ymd)
|
||||
for site in site_list:
|
||||
MwSites.instance().stop(site['id'])
|
||||
thisdb.setOption('website_edate', now_time_ymd)
|
||||
except Exception as e:
|
||||
print('siteEdateCheck:',mw.getTracebackInfo())
|
||||
|
||||
# 任务队列
|
||||
def startPanelTask():
|
||||
try:
|
||||
while True:
|
||||
runPanelTask()
|
||||
time.sleep(5)
|
||||
except Exception as e:
|
||||
print('startPanelTask:',mw.getTracebackInfo())
|
||||
time.sleep(30)
|
||||
startPanelTask()
|
||||
|
||||
def systemTask():
|
||||
# 系统监控任务
|
||||
from utils.system import monitor
|
||||
try:
|
||||
while True:
|
||||
monitor_status = thisdb.getOption('monitor_status',type='monitor',default='open')
|
||||
if monitor_status == 'open':
|
||||
monitor.instance().run()
|
||||
time.sleep(5)
|
||||
except Exception as ex:
|
||||
print('systemTask:',mw.getTracebackInfo())
|
||||
time.sleep(30)
|
||||
systemTask()
|
||||
|
||||
|
||||
def panelPluginStatusCheck():
|
||||
# 系统监控任务
|
||||
from utils.plugin import plugin
|
||||
try:
|
||||
while True:
|
||||
# start_t = time.time()
|
||||
plugin.instance().autoCachePluginStatus()
|
||||
# end_t = time.time()
|
||||
time.sleep(60)
|
||||
except Exception as ex:
|
||||
print('panelPluginStatusCheck:',mw.getTracebackInfo())
|
||||
time.sleep(120)
|
||||
panelPluginStatusCheck()
|
||||
|
||||
# -------------------------------------- PHP监控 start --------------------------------------------- #
|
||||
# 502错误检查线程
|
||||
def check502Task():
|
||||
try:
|
||||
check_file = mw.getPanelDir() + '/data/502Task.pl'
|
||||
while True:
|
||||
if os.path.exists(check_file):
|
||||
check502()
|
||||
time.sleep(10)
|
||||
except:
|
||||
time.sleep(30)
|
||||
check502Task()
|
||||
|
||||
|
||||
def check502():
|
||||
try:
|
||||
verlist = [
|
||||
'52', '53', '54', '55', '56', '70',
|
||||
'71', '72', '73', '74', '80', '81',
|
||||
'82', '83', '84', '85'
|
||||
]
|
||||
for ver in verlist:
|
||||
server_dir = mw.getServerDir()
|
||||
php_path = server_dir + '/php/' + ver + '/sbin/php-fpm'
|
||||
if not os.path.exists(php_path):
|
||||
continue
|
||||
if checkPHPVersion(ver):
|
||||
continue
|
||||
if startPHPVersion(ver):
|
||||
print('检测到PHP-' + ver + '处理异常,已自动修复!')
|
||||
mw.writeLog('PHP守护程序', '检测到PHP-' + ver + '处理异常,已自动修复!')
|
||||
|
||||
except Exception as e:
|
||||
mw.writeLog('PHP守护程序', '自动修复异常:'+str(e))
|
||||
|
||||
|
||||
# 处理指定PHP版本
|
||||
def startPHPVersion(version):
|
||||
server_dir = mw.getServerDir()
|
||||
try:
|
||||
# system
|
||||
phpService = mw.systemdCfgDir() + '/php' + version + '.service'
|
||||
if os.path.exists(phpService):
|
||||
mw.execShell("systemctl restart php" + version)
|
||||
if checkPHPVersion(version):
|
||||
return True
|
||||
|
||||
# initd
|
||||
fpm = server_dir + '/php/init.d/php' + version
|
||||
php_path = server_dir + '/php/' + version + '/sbin/php-fpm'
|
||||
if not os.path.exists(php_path):
|
||||
if os.path.exists(fpm):
|
||||
os.remove(fpm)
|
||||
return False
|
||||
|
||||
if not os.path.exists(fpm):
|
||||
return False
|
||||
|
||||
# 尝试重载服务
|
||||
os.system(fpm + ' reload')
|
||||
if checkPHPVersion(version):
|
||||
return True
|
||||
# 尝试重启服务
|
||||
cgi = '/tmp/php-cgi-' + version + '.sock'
|
||||
pid = server_dir + '/php/' + version + '/var/run/php-fpm.pid'
|
||||
data = mw.execShell("ps -ef | grep php/" + version +" | grep -v grep|grep -v python |awk '{print $2}'")
|
||||
if data[0] != '':
|
||||
os.system("ps -ef | grep php/" + version + " | grep -v grep|grep -v python |awk '{print $2}' | xargs kill ")
|
||||
time.sleep(0.5)
|
||||
if not os.path.exists(cgi):
|
||||
os.system('rm -f ' + cgi)
|
||||
if not os.path.exists(pid):
|
||||
os.system('rm -f ' + pid)
|
||||
os.system(fpm + ' start')
|
||||
if checkPHPVersion(version):
|
||||
return True
|
||||
|
||||
# 检查是否正确启动
|
||||
if os.path.exists(cgi):
|
||||
return True
|
||||
except Exception as e:
|
||||
print('startPHPVersion:',mw.getTracebackInfo())
|
||||
mw.writeLog('PHP守护程序', '自动修复异常:'+str(e))
|
||||
return True
|
||||
|
||||
|
||||
def checkPHPVersion(version):
|
||||
# 检查指定PHP版本
|
||||
try:
|
||||
sock = mw.getFpmAddress(version)
|
||||
data = mw.requestFcgiPHP(sock, '/phpfpm_status_' + version + '?json')
|
||||
result = str(data, encoding='utf-8')
|
||||
except Exception as e:
|
||||
result = 'Bad Gateway'
|
||||
# 检查openresty
|
||||
if result.find('Bad Gateway') != -1:
|
||||
return False
|
||||
if result.find('HTTP Error 404: Not Found') != -1:
|
||||
return False
|
||||
|
||||
# 检查Web服务是否启动
|
||||
if result.find('Connection refused') != -1:
|
||||
return False
|
||||
return True
|
||||
|
||||
# -------------------------------------- PHP监控 end --------------------------------------------- #
|
||||
|
||||
|
||||
# --------------------------------------OpenResty Auto Restart Start --------------------------------------------- #
|
||||
# 解决acme.sh续签后,未起效。
|
||||
def openrestyAutoRestart():
|
||||
try:
|
||||
while True:
|
||||
# 检查是否安装
|
||||
odir = mw.getServerDir() + '/openresty'
|
||||
if not os.path.exists(odir):
|
||||
time.sleep(86400)
|
||||
continue
|
||||
mw.opWeb('reload')
|
||||
time.sleep(86400)
|
||||
except Exception as e:
|
||||
mw.writeLog('OpenResty检测', '自动修复异常:'+str(e))
|
||||
time.sleep(86400)
|
||||
|
||||
# --------------------------------------OpenResty Auto Restart End --------------------------------------------- #
|
||||
|
||||
# ------------------------------------ OpenResty Restart At Once Start ------------------------------------------ #
|
||||
|
||||
|
||||
def openrestyRestartAtOnce():
|
||||
restart_nginx_tip = mw.getPanelDir()+'/data/restart_nginx.pl'
|
||||
while True:
|
||||
if os.path.exists(restart_nginx_tip):
|
||||
os.remove(restart_nginx_tip)
|
||||
mw.opWeb('reload')
|
||||
time.sleep(1)
|
||||
# ----------------------------------- OpenResty Restart At Once End ------------------------------------------ #
|
||||
|
||||
|
||||
# --------------------------------------Panel Restart Start --------------------------------------------- #
|
||||
def restartPanelService():
|
||||
restart_tip = mw.getPanelDir()+'/data/restart.pl'
|
||||
while True:
|
||||
if os.path.exists(restart_tip):
|
||||
print("restart panel")
|
||||
os.remove(restart_tip)
|
||||
mw.panelCmd('restart_panel')
|
||||
time.sleep(1)
|
||||
# --------------------------------------Panel Restart End --------------------------------------------- #
|
||||
|
||||
|
||||
def setDaemon(t):
|
||||
if sys.version_info.major == 3 and sys.version_info.minor >= 10:
|
||||
t.daemon = True
|
||||
else:
|
||||
t.setDaemon(True)
|
||||
return t
|
||||
|
||||
def run():
|
||||
# 系统监控
|
||||
sysTask = threading.Thread(target=systemTask)
|
||||
sysTask = setDaemon(sysTask)
|
||||
sysTask.start()
|
||||
|
||||
# PHP 502错误检查线程
|
||||
php502 = threading.Thread(target=check502Task)
|
||||
php502 = setDaemon(php502)
|
||||
php502.start()
|
||||
|
||||
# OpenResty Restart At Once Start
|
||||
oraos = threading.Thread(target=openrestyRestartAtOnce)
|
||||
oraos = setDaemon(oraos)
|
||||
oraos.start()
|
||||
|
||||
# OpenResty Auto Restart Start
|
||||
oar = threading.Thread(target=openrestyAutoRestart)
|
||||
oar = setDaemon(oar)
|
||||
oar.start()
|
||||
|
||||
# Panel Plugin Status Check
|
||||
pps = threading.Thread(target=panelPluginStatusCheck)
|
||||
pps = setDaemon(pps)
|
||||
pps.start()
|
||||
|
||||
# Panel Restart Start
|
||||
rps = threading.Thread(target=restartPanelService)
|
||||
rps = setDaemon(rps)
|
||||
rps.start()
|
||||
|
||||
# 面板后台任务
|
||||
startPanelTask()
|
||||
|
||||
if __name__ == "__main__":
|
||||
run()
|
||||
|
||||
@@ -1,433 +0,0 @@
|
||||
# coding=utf-8
|
||||
|
||||
# ---------------------------------------------------------------------------------
|
||||
# MW-Linux面板
|
||||
# ---------------------------------------------------------------------------------
|
||||
# copyright (c) 2018-∞(https://github.com/midoks/mdserver-web) All rights reserved.
|
||||
# ---------------------------------------------------------------------------------
|
||||
# Author: midoks <midoks@163.com>
|
||||
# ---------------------------------------------------------------------------------
|
||||
|
||||
# ---------------------------------------------------------------------------------
|
||||
# 工具箱
|
||||
# ---------------------------------------------------------------------------------
|
||||
|
||||
|
||||
import sys
|
||||
import os
|
||||
import json
|
||||
import time
|
||||
import re
|
||||
|
||||
web_dir = os.getcwd() + "/web"
|
||||
os.chdir(web_dir)
|
||||
sys.path.append(web_dir)
|
||||
|
||||
from utils.firewall import Firewall as MwFirewall
|
||||
import core.mw as mw
|
||||
import thisdb
|
||||
|
||||
INIT_DIR = "/etc/rc.d/init.d"
|
||||
if mw.isAppleSystem():
|
||||
INIT_DIR = mw.getPanelDir() + "/scripts/init.d"
|
||||
|
||||
INIT_CMD = INIT_DIR + "/mw"
|
||||
|
||||
|
||||
def mw_input_cmd(msg):
|
||||
if sys.version_info[0] == 2:
|
||||
in_val = raw_input(msg)
|
||||
else:
|
||||
in_val = input(msg)
|
||||
return in_val
|
||||
|
||||
def getRemainLen(cmd, max_length=100):
|
||||
cmd_len = len(cmd)
|
||||
cmd_u8_len = len(cmd.encode('utf-8'))
|
||||
return max_length-int((cmd_u8_len - cmd_len)/2+cmd_len)
|
||||
|
||||
def mwcli(mw_input=0):
|
||||
panel_dir = mw.getPanelDir()
|
||||
|
||||
raw_tip = "========================================================================"
|
||||
if not mw_input:
|
||||
print("========================mdserver-web cli tools==========================")
|
||||
cmd_list = [
|
||||
'(1) 重启面板服务',
|
||||
'(2) 停止面板服务',
|
||||
'(3) 启动面板服务',
|
||||
'(4) 重载面板服务',
|
||||
'(5) 修改面板IP',
|
||||
'(6) 修改面板端口',
|
||||
'(7) 关闭安全入口',
|
||||
'(10) 查看面板默认信息',
|
||||
'(11) 修改面板密码',
|
||||
'(12) 修改面板用户名',
|
||||
'(13) 显示面板错误日志',
|
||||
'(14) 关闭面板访问',
|
||||
'(15) 开启面板访问',
|
||||
'(20) 关闭BasicAuth认证',
|
||||
'(21) 解除域名绑定',
|
||||
'(22) 解除面板SSL绑定',
|
||||
'(23) 开启IPV6支持',
|
||||
'(24) 关闭IPV6支持',
|
||||
'(25) 开启防火墙SSH端口',
|
||||
'(26) 关闭二次验证',
|
||||
'(27) 查看防火墙信息',
|
||||
'(28) 自动识别防火墙端口到面板',
|
||||
'(29) 自动识别配置站点信息',
|
||||
'(100) 开启PHP52显示',
|
||||
'(101) 关闭PHP52显示',
|
||||
'(200) 切换Linux系统软件源',
|
||||
'(201) 简单速度测试',
|
||||
'(202) SSH终端管理',
|
||||
'(0) 取消'
|
||||
]
|
||||
cmd_list_num = len(cmd_list)
|
||||
for index in range(cmd_list_num):
|
||||
cmd = cmd_list[index]
|
||||
if index % 2 == 0:
|
||||
if index == (cmd_list_num-1):
|
||||
print(cmd)
|
||||
else:
|
||||
print(cmd + " " * getRemainLen(cmd, 40), end="")
|
||||
if index % 2 == 1:
|
||||
print(cmd)
|
||||
print(raw_tip)
|
||||
try:
|
||||
mw_input = input("请输入命令编号:")
|
||||
if sys.version_info[0] == 3:
|
||||
mw_input = int(mw_input)
|
||||
except:
|
||||
mw_input = 0
|
||||
|
||||
nums = [
|
||||
1, 2, 3, 4, 5, 6, 7,
|
||||
10, 11, 12, 13, 14, 15,
|
||||
20, 21, 22, 23, 24, 25, 26, 27, 28, 29,
|
||||
100, 101,
|
||||
200, 201, 202
|
||||
]
|
||||
if not mw_input in nums:
|
||||
print(raw_tip)
|
||||
print("已取消!")
|
||||
exit()
|
||||
|
||||
if mw_input == 1:
|
||||
os.system(INIT_CMD + " restart")
|
||||
elif mw_input == 2:
|
||||
os.system(INIT_CMD + " stop")
|
||||
elif mw_input == 3:
|
||||
os.system(INIT_CMD + " start")
|
||||
elif mw_input == 4:
|
||||
os.system(INIT_CMD + " reload")
|
||||
elif mw_input == 5:
|
||||
in_ip = mw_input_cmd("请输入设置的面板IP:")
|
||||
in_ip = in_ip.strip()
|
||||
ip_text = panel_dir + '/data/iplist.txt'
|
||||
if not mw.isVaildIp(in_ip):
|
||||
mw.echoInfo("【"+in_ip+"】: IP不合法")
|
||||
return
|
||||
mw.writeFile(ip_text, in_ip)
|
||||
thisdb.setOption('server_ip', in_ip)
|
||||
mw.echoInfo("设置面板IP: " + in_ip)
|
||||
elif mw_input == 6:
|
||||
in_port = mw_input_cmd("请输入新的面板端口:")
|
||||
in_port_int = int(in_port.strip())
|
||||
if in_port_int < 65536 and in_port_int > 0:
|
||||
MwFirewall.instance().addAcceptPort(in_port, 'WEB面板[TOOLS修改]', 'port')
|
||||
panel_port = panel_dir + '/data/port.pl'
|
||||
mw.writeFile(panel_port, in_port)
|
||||
os.system(INIT_CMD + " restart_panel")
|
||||
os.system(INIT_CMD + " default")
|
||||
else:
|
||||
mw.echoInfo("端口范围在0-65536之间")
|
||||
return
|
||||
elif mw_input == 7:
|
||||
thisdb.setOption('admin_path', '')
|
||||
mw.echoInfo("关闭安全入口成功!")
|
||||
elif mw_input == 10:
|
||||
os.system(INIT_CMD + " default")
|
||||
elif mw_input == 11:
|
||||
input_pwd = mw_input_cmd("请输入新的面板密码:")
|
||||
if len(input_pwd.strip()) < 5:
|
||||
mw.echoInfo("错误,密码长度不能小于5位")
|
||||
return
|
||||
set_panel_pwd(input_pwd.strip(), True)
|
||||
elif mw_input == 12:
|
||||
input_user = mw_input_cmd("请输入新的面板用户名(>=5位):")
|
||||
set_panel_username(input_user.strip())
|
||||
elif mw_input == 13:
|
||||
os.system('tail -100 ' + panel_dir + '/logs/panel_error.log')
|
||||
elif mw_input == 14:
|
||||
admin_close = thisdb.getOption('admin_close')
|
||||
if admin_close == 'no':
|
||||
thisdb.setOption('admin_close', 'yes')
|
||||
mw.echoInfo("关闭面板访问成功!")
|
||||
else:
|
||||
mw.echoInfo("已关闭面板访问!")
|
||||
elif mw_input == 15:
|
||||
admin_close = thisdb.getOption('admin_close')
|
||||
if admin_close == 'yes':
|
||||
thisdb.setOption('admin_close', 'no')
|
||||
mw.echoInfo("开启面板访问成功!")
|
||||
else:
|
||||
mw.echoInfo("已开启面板访问!")
|
||||
elif mw_input == 20:
|
||||
basic_auth = thisdb.getOptionByJson('basic_auth', default={'open':False})
|
||||
if basic_auth['open']:
|
||||
basic_auth['open'] = False
|
||||
thisdb.setOption('basic_auth', json.dumps(basic_auth))
|
||||
os.system(INIT_CMD + " restart")
|
||||
mw.echoInfo("关闭basic_auth成功")
|
||||
elif mw_input == 21:
|
||||
panel_domain = thisdb.getOption('panel_domain', default='')
|
||||
if panel_domain != '':
|
||||
thisdb.setOption('panel_domain', '')
|
||||
os.system(INIT_CMD + " unbind_domain")
|
||||
mw.echoInfo("解除域名绑定成功")
|
||||
else:
|
||||
mw.echoInfo("面板未绑定域名!")
|
||||
elif mw_input == 22:
|
||||
panel_ssl = thisdb.getOptionByJson('panel_ssl', default={'open':False})
|
||||
if panel_ssl['open']:
|
||||
panel_ssl['open'] = False
|
||||
thisdb.setOption('panel_ssl', json.dumps(panel_ssl))
|
||||
os.system(INIT_CMD + " unbind_ssl")
|
||||
mw.echoInfo("解除面板SSL绑定成功")
|
||||
elif mw_input == 23:
|
||||
listen_ipv6 = panel_dir + '/data/ipv6.pl'
|
||||
if not os.path.exists(listen_ipv6):
|
||||
mw.writeFile(listen_ipv6, 'True')
|
||||
os.system(INIT_CMD + " restart")
|
||||
mw.echoInfo("开启IPv6支持了")
|
||||
else:
|
||||
mw.echoInfo("已开启IPv6支持!")
|
||||
elif mw_input == 24:
|
||||
listen_ipv6 = panel_dir + '/data/ipv6.pl'
|
||||
if not os.path.exists(listen_ipv6):
|
||||
mw.echoInfo("已关闭IPv6支持!")
|
||||
else:
|
||||
os.remove(listen_ipv6)
|
||||
os.system(INIT_CMD + " restart")
|
||||
mw.echoInfo("关闭IPv6支持了")
|
||||
elif mw_input == 25:
|
||||
open_ssh_port()
|
||||
mw.echoInfo("已开启!")
|
||||
elif mw_input == 26:
|
||||
two_step_verification = thisdb.getOptionByJson('two_step_verification', default={'open':False})
|
||||
if two_step_verification['open']:
|
||||
two_step_verification['open'] = False
|
||||
thisdb.setOption('two_step_verification', json.dumps(two_step_verification))
|
||||
mw.echoInfo("关闭二次验证成功!")
|
||||
else:
|
||||
mw.echoInfo("二次验证已关闭!")
|
||||
elif mw_input == 27:
|
||||
cmd = 'which ufw'
|
||||
run_cmd = False
|
||||
find_cmd = mw.execShell(cmd)
|
||||
if find_cmd[0].strip() != '':
|
||||
run_cmd = True
|
||||
os.system('ufw status')
|
||||
|
||||
cmd = 'which firewall-cmd'
|
||||
find_cmd = mw.execShell(cmd)
|
||||
if find_cmd[0].strip() != '':
|
||||
run_cmd = True
|
||||
os.system('firewall-cmd --list-all')
|
||||
if not run_cmd:
|
||||
mw.echoInfo("未检测到防火墙!")
|
||||
elif mw_input == 28:
|
||||
MwFirewall.instance().aIF()
|
||||
mw.echoInfo("执行自动识别防火墙端口到面板成功!")
|
||||
elif mw_input == 29:
|
||||
from utils.site_reflect import parse as MwParse
|
||||
MwParse()
|
||||
mw.echoInfo("自动识别配置站点信息成功!")
|
||||
elif mw_input == 100:
|
||||
php_conf = panel_dir + '/plugins/php/info.json'
|
||||
if os.path.exists(php_conf):
|
||||
cont = mw.readFile(php_conf)
|
||||
cont = re.sub("\"53\"", "\"52\",\"53\"", cont)
|
||||
cont = re.sub("\"5.3.29\"", "\"5.2.17\",\"5.3.29\"", cont)
|
||||
mw.writeFile(php_conf, cont)
|
||||
mw.echoInfo("执行PHP52显示成功!")
|
||||
elif mw_input == 101:
|
||||
php_conf = panel_dir + '/plugins/php/info.json'
|
||||
if os.path.exists(php_conf):
|
||||
cont = mw.readFile(php_conf)
|
||||
cont = re.sub("\"52\",", "", cont)
|
||||
cont = re.sub("\"5.2.17\",", cont)
|
||||
mw.writeFile(php_conf, cont)
|
||||
mw.echoInfo("执行PHP52隐藏成功!")
|
||||
elif mw_input == 200:
|
||||
os.system("bash <(curl -sSL https://linuxmirrors.cn/main.sh)")
|
||||
# os.system(INIT_CMD + " mirror")
|
||||
elif mw_input == 201:
|
||||
os.system('curl -Lso- bench.sh | bash')
|
||||
elif mw_input == 202:
|
||||
package = mw.getPanelDir()+'/plugins'
|
||||
|
||||
dst_plugin = mw.getServerDir() + "/webssh"
|
||||
if not os.path.exists(dst_plugin):
|
||||
mw.echoInfo("未安装!")
|
||||
exit(1)
|
||||
|
||||
if not package in sys.path:
|
||||
sys.path.append(package)
|
||||
from webssh.index import App
|
||||
|
||||
obj = App()
|
||||
data = obj.get_server_list()
|
||||
data = json.loads(data)
|
||||
|
||||
if data['status']:
|
||||
wlist = data['data']
|
||||
wlist_len = len(wlist)
|
||||
if wlist_len == 0:
|
||||
mw.echoInfo("SSH终端管理,数据为空!")
|
||||
exit(1)
|
||||
|
||||
if len(sys.argv) == 4:
|
||||
pos = int(sys.argv[3])
|
||||
if pos >= wlist_len:
|
||||
mw.echoInfo("SSH终端管理,超过限制!")
|
||||
exit(1)
|
||||
dst_data = wlist[pos]
|
||||
tmp_host = dst_data["host"]
|
||||
info = obj.get_server_by_host_data(tmp_host)
|
||||
pp_info = tmp_host+"|"+info["port"]+"|"+info['username']+"|"+info['password']+""
|
||||
print(pp_info)
|
||||
exit(0)
|
||||
|
||||
mw.echoInfo("请选择:")
|
||||
for x in range(wlist_len):
|
||||
dst_data = wlist[x]
|
||||
tag = dst_data['host']
|
||||
if dst_data['ps'] != "":
|
||||
tag = dst_data['ps']
|
||||
print(str(x) +") " + tag)
|
||||
|
||||
def open_ssh_port():
|
||||
|
||||
find_ssh_port_cmd = "cat /etc/ssh/sshd_config | grep '^Port \\d*' | tail -1"
|
||||
cmd_data = mw.execShell(find_ssh_port_cmd)
|
||||
ssh_port = cmd_data[0].replace("Port ", '').strip()
|
||||
if ssh_port == '':
|
||||
ssh_port = '22'
|
||||
|
||||
mw.echoInfo("SSH端口: "+ str(ssh_port))
|
||||
MwFirewall.instance().addAcceptPort(ssh_port, 'SSH远程管理服务', 'port')
|
||||
return True
|
||||
|
||||
|
||||
def set_panel_pwd(password, ncli=False):
|
||||
info = thisdb.getUserByRoot()
|
||||
thisdb.setUserByRoot(password=password)
|
||||
if ncli:
|
||||
mw.echoInfo("username: " + info['name'])
|
||||
mw.echoInfo("password: " + password)
|
||||
else:
|
||||
print(username)
|
||||
|
||||
|
||||
def show_panel_pwd():
|
||||
# 面板密码展示
|
||||
info = thisdb.getUserByRoot()
|
||||
defailt_pwd_file = mw.getPanelDir()+'/data/default.pl'
|
||||
pwd = ''
|
||||
if os.path.exists(defailt_pwd_file):
|
||||
pwd = mw.readFile(defailt_pwd_file).strip()
|
||||
|
||||
if mw.md5(pwd) == info['password']:
|
||||
mw.echoInfo('password: ' + pwd)
|
||||
return
|
||||
print("*-password has been changed!")
|
||||
|
||||
def show_panel_adminpath():
|
||||
admin_path = thisdb.getOption('admin_path')
|
||||
if admin_path == '':
|
||||
print('/login')
|
||||
else:
|
||||
print('/'+admin_path)
|
||||
|
||||
|
||||
def set_panel_username(username=None):
|
||||
# 随机面板用户名
|
||||
if username:
|
||||
if len(username) < 5:
|
||||
mw.echoInfo("错误,用户名长度不能少于5位")
|
||||
return
|
||||
if username in ['admin', 'root']:
|
||||
mw.echoInfo("错误,不能使用过于简单的用户名")
|
||||
return
|
||||
|
||||
thisdb.setUserByRoot(name=username)
|
||||
mw.echoInfo("username: %s" % username)
|
||||
return
|
||||
|
||||
info = thisdb.getUserByRoot()
|
||||
if info['name'] == 'admin':
|
||||
username = mw.getRandomString(8).lower()
|
||||
thisdb.setUserByRoot(name=username)
|
||||
mw.echoInfo('username: ' + info['name'])
|
||||
|
||||
|
||||
def getServerIp():
|
||||
version = sys.argv[2]
|
||||
# ip = mw.execShell(
|
||||
# "curl --insecure -{} -sS --connect-timeout 5 -m 60 https://v6r.ipip.net/?format=text".format(version))
|
||||
ip = mw.execShell(
|
||||
"curl --insecure -{} -sS --connect-timeout 5 -m 60 https://ip.cachecha.com/?format=text".format(version))
|
||||
# print(ip[0])
|
||||
return ip[0]
|
||||
|
||||
|
||||
def getPanelSslType():
|
||||
scheme = 'http'
|
||||
panel_ssl = thisdb.getOptionByJson('panel_ssl', default={'open':False})
|
||||
if panel_ssl['open']:
|
||||
scheme = 'https'
|
||||
return scheme
|
||||
|
||||
def getPanelBindDomain():
|
||||
return thisdb.getOption('panel_domain', default='')
|
||||
|
||||
|
||||
def main():
|
||||
if len(sys.argv) == 1:
|
||||
print('ERROR: Parameter error!')
|
||||
exit(-2)
|
||||
method = sys.argv[1]
|
||||
if method == 'panel':
|
||||
set_panel_pwd(sys.argv[2])
|
||||
elif method == 'username':
|
||||
if len(sys.argv) > 2:
|
||||
set_panel_username(sys.argv[2])
|
||||
else:
|
||||
set_panel_username()
|
||||
elif method == 'password':
|
||||
show_panel_pwd()
|
||||
elif method == 'test':
|
||||
thisdb.getOption('admin_path')
|
||||
elif method == 'admin_path':
|
||||
show_panel_adminpath()
|
||||
elif method == 'getServerIp':
|
||||
print(getServerIp())
|
||||
elif method == 'panel_ssl_type':
|
||||
print(getPanelSslType())
|
||||
elif method == 'panel_bind_domain':
|
||||
print(getPanelBindDomain())
|
||||
elif method == "cli":
|
||||
clinum = 0
|
||||
try:
|
||||
if len(sys.argv) > 2:
|
||||
clinum = int(sys.argv[2]) if sys.argv[2][:6] else sys.argv[2]
|
||||
except:
|
||||
clinum = sys.argv[2]
|
||||
mwcli(clinum)
|
||||
else:
|
||||
print('ERROR: Parameter error')
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -1,34 +0,0 @@
|
||||
CREATE TABLE IF NOT EXISTS `dnsapi` (
|
||||
`id` INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
`name` TEXT,
|
||||
`type` TEXT,
|
||||
`val` TEXT,
|
||||
`remark` TEXT,
|
||||
`addtime` TEXT
|
||||
);
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `email` (
|
||||
`id` INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
`addr` TEXT,
|
||||
`remark` TEXT,
|
||||
`addtime` TEXT
|
||||
);
|
||||
|
||||
|
||||
CREATE TABLE IF NOT EXISTS `domain` (
|
||||
`id` INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||
`domain` TEXT,
|
||||
`dnsapi_id` TEXT,
|
||||
`email` TEXT,
|
||||
`remark` TEXT,
|
||||
`effective_date` TEXT default '',
|
||||
`expiration_date` TEXT default '',
|
||||
`error` TEXT,
|
||||
`status` INTEGER default '0',
|
||||
`addtime` TEXT
|
||||
);
|
||||
|
||||
-- ALTER TABLE `domain` ADD COLUMN `effective_date` TEXT DEFAULT '';
|
||||
-- ALTER TABLE `domain` ADD COLUMN `expiration_date` TEXT DEFAULT '';
|
||||
-- ALTER TABLE `domain` ADD COLUMN `error` TEXT DEFAULT '';
|
||||
-- ALTER TABLE `domain` ADD COLUMN `status` INTEGER DEFAULT '0';
|
||||
@@ -1,136 +0,0 @@
|
||||
# coding:utf-8
|
||||
|
||||
import sys
|
||||
import io
|
||||
import os
|
||||
import time
|
||||
import re
|
||||
import requests
|
||||
import base64
|
||||
|
||||
|
||||
# 8001 / 7788
|
||||
goedge_addr = 'http://127.0.0.2:8009'
|
||||
access_keyid = "xxx"
|
||||
access_key = "xxx"
|
||||
|
||||
# 指定用户
|
||||
userId = 1
|
||||
|
||||
if sys.platform != "darwin":
|
||||
os.chdir("/www/server/mdserver-web")
|
||||
|
||||
|
||||
web_dir = os.getcwd() + "/web"
|
||||
if os.path.exists(web_dir):
|
||||
sys.path.append(web_dir)
|
||||
os.chdir(web_dir)
|
||||
|
||||
import core.mw as mw
|
||||
|
||||
domain = sys.argv[1]
|
||||
ssl_path = sys.argv[2]
|
||||
|
||||
|
||||
def getToken():
|
||||
api_url = goedge_addr+'/APIAccessTokenService/getAPIAccessToken'
|
||||
post_data = {
|
||||
"type": "admin",
|
||||
"accessKeyId": access_keyid,
|
||||
"accessKey": access_key
|
||||
}
|
||||
data = requests.post(api_url,json=post_data)
|
||||
data_obj = data.json()
|
||||
|
||||
return data_obj['data']['token']
|
||||
|
||||
token = getToken()
|
||||
|
||||
def commonReq(url, data):
|
||||
headers = {
|
||||
'X-Edge-Access-Token': token
|
||||
}
|
||||
api_url = goedge_addr+url
|
||||
resp_data = requests.post(api_url,json=data, headers=headers)
|
||||
return resp_data.json()
|
||||
|
||||
def listSSLCerts(domain):
|
||||
request_data = {
|
||||
"userId":userId,
|
||||
"isCA":False,
|
||||
"keyword": "ACME泛域名自动上传",
|
||||
"domains":[domain,"*."+domain],
|
||||
"size":1
|
||||
}
|
||||
response_data = commonReq('/SSLCertService/listSSLCerts', request_data)
|
||||
|
||||
data = response_data['data']['sslCertsJSON']
|
||||
data = mw.base64StrDecode(data)
|
||||
data = mw.getObjectByJson(data)
|
||||
# print(data)
|
||||
return data
|
||||
|
||||
|
||||
|
||||
# createSSLCert(domain)
|
||||
def createSSLCert(domain, did=0):
|
||||
|
||||
ssl_cer_file = ssl_path + '/'+domain+'.cer'
|
||||
|
||||
if not os.path.exists(ssl_cer_file):
|
||||
print("没有有效证书!")
|
||||
return ''
|
||||
# print(ssl_cer_file)
|
||||
ssl_info = mw.getCertName(ssl_cer_file)
|
||||
cer_data = mw.readFile(ssl_cer_file)
|
||||
cer_data = mw.base64StrEncode(cer_data)
|
||||
# print('cer',cer_data)
|
||||
|
||||
ssl_key_file = ssl_path + '/'+domain+'.key'
|
||||
key_data = mw.readFile(ssl_key_file)
|
||||
key_data = mw.base64StrEncode(key_data)
|
||||
# print('ssl_info',ssl_info)
|
||||
|
||||
timeBeginAt = int(time.mktime(time.strptime(ssl_info['notBefore'], "%Y-%m-%d")))
|
||||
timeEndAt = int(time.mktime(time.strptime(ssl_info['notAfter'], "%Y-%m-%d")))
|
||||
|
||||
request_data = {
|
||||
"isOn":True,
|
||||
"userId":userId,
|
||||
"name": "ACME泛域名自动上传",
|
||||
"isCA":False,
|
||||
"description":domain,
|
||||
"serverName":domain,
|
||||
"certData":cer_data,
|
||||
"keyData":key_data,
|
||||
"timeBeginAt":timeBeginAt,
|
||||
"timeEndAt": timeEndAt,
|
||||
"dnsNames":[domain,"*."+domain],
|
||||
"commonNames":[ssl_info['issuer']]
|
||||
}
|
||||
|
||||
if did>0:
|
||||
request_data['sslCertId'] = did
|
||||
# print(request_data)
|
||||
response_data = commonReq('/SSLCertService/updateSSLCert', request_data)
|
||||
print('更新成功',domain,response_data)
|
||||
return response_data
|
||||
else:
|
||||
# print(request_data)
|
||||
response_data = commonReq('/SSLCertService/createSSLCert', request_data)
|
||||
print('创建成功',domain,response_data)
|
||||
return response_data
|
||||
return response_data
|
||||
|
||||
def autoSyncDomain(domain):
|
||||
data = listSSLCerts(domain)
|
||||
if len(data) > 0 :
|
||||
did = data[0]['id']
|
||||
createSSLCert(domain,did)
|
||||
else:
|
||||
createSSLCert(domain)
|
||||
print(data)
|
||||
|
||||
|
||||
autoSyncDomain(domain)
|
||||
print(domain,ssl_path)
|
||||
@@ -1,12 +0,0 @@
|
||||
# coding:utf-8
|
||||
|
||||
import sys
|
||||
import io
|
||||
import os
|
||||
import time
|
||||
import re
|
||||
|
||||
domain = sys.argv[1]
|
||||
path = sys.argv[2]
|
||||
|
||||
print(domain,path)
|
||||
|
Before Width: | Height: | Size: 4.0 KiB |
@@ -1,34 +0,0 @@
|
||||
<style>
|
||||
.overflow_hide {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
vertical-align: middle;
|
||||
}
|
||||
</style>
|
||||
|
||||
<div class="bt-form">
|
||||
<div class='plugin_version'></div>
|
||||
<div class="bt-w-main">
|
||||
<div class="bt-w-menu">
|
||||
<p class="bgw" onclick="pluginService('acme_pandominassl_apply');">服务</p>
|
||||
<p onclick="dnsapiList();">DNSAPI *</p>
|
||||
<p onclick="emailList();">邮件地址 </p>
|
||||
<p onclick="domainList()">域名SSL *</p>
|
||||
<p onclick="pluginConfigTpl('acme_pandominassl_apply',$('.plugin_version').attr('version'));">HOOK</p>
|
||||
<p onclick="pluginLogs('acme_pandominassl_apply','','run_log');">日志</p>
|
||||
<p onclick="apaReadme();">相关说明</p>
|
||||
|
||||
</div>
|
||||
<div class="bt-w-con pd15">
|
||||
<div class="soft-man-con"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script type="text/javascript">
|
||||
resetPluginWinWidth(1000);
|
||||
resetPluginWinHeight(550);
|
||||
$.getScript( "/plugins/file?name=acme_pandominassl_apply&f=js/common.js", function(){
|
||||
pluginService('acme_pandominassl_apply', $('.plugin_version').attr('version'));
|
||||
});
|
||||
</script>
|
||||
@@ -1,863 +0,0 @@
|
||||
# coding:utf-8
|
||||
|
||||
import sys
|
||||
import io
|
||||
import os
|
||||
import time
|
||||
import re
|
||||
import requests
|
||||
import json
|
||||
|
||||
web_dir = os.getcwd() + "/web"
|
||||
if os.path.exists(web_dir):
|
||||
sys.path.append(web_dir)
|
||||
os.chdir(web_dir)
|
||||
|
||||
import core.mw as mw
|
||||
|
||||
app_debug = False
|
||||
if mw.isAppleSystem():
|
||||
app_debug = True
|
||||
|
||||
|
||||
def getPluginName():
|
||||
return 'acme_pandominassl_apply'
|
||||
|
||||
|
||||
def getPluginDir():
|
||||
return mw.getPluginDir() + '/' + getPluginName()
|
||||
|
||||
|
||||
def getServerDir():
|
||||
return mw.getServerDir() + '/' + getPluginName()
|
||||
|
||||
|
||||
def getInitDFile():
|
||||
current_os = mw.getOs()
|
||||
if current_os == 'darwin':
|
||||
return '/tmp/' + getPluginName()
|
||||
|
||||
if current_os.startswith('freebsd'):
|
||||
return '/etc/rc.d/' + getPluginName()
|
||||
|
||||
return '/etc/init.d/' + getPluginName()
|
||||
|
||||
|
||||
def getConf():
|
||||
path = getServerDir() + "/hook.py"
|
||||
return path
|
||||
|
||||
|
||||
def getInitDTpl():
|
||||
path = getPluginDir() + "/init.d/" + getPluginName() + ".tpl"
|
||||
return path
|
||||
|
||||
def runLog():
|
||||
return getServerDir() + '/hook.log'
|
||||
|
||||
def getArgs():
|
||||
args = sys.argv[3:]
|
||||
# print(args)
|
||||
tmp = {}
|
||||
args_len = len(args)
|
||||
|
||||
if args_len == 1:
|
||||
t = args[0].strip('{').strip('}')
|
||||
if t.strip() == '':
|
||||
tmp = []
|
||||
else:
|
||||
t = t.split(':')
|
||||
tmp[t[0]] = t[1]
|
||||
tmp[t[0]] = t[1]
|
||||
elif args_len > 1:
|
||||
for i in range(len(args)):
|
||||
t = args[i].split(':')
|
||||
tmp[t[0]] = t[1]
|
||||
return tmp
|
||||
|
||||
def checkArgs(data, ck=[]):
|
||||
for i in range(len(ck)):
|
||||
if not ck[i] in data:
|
||||
return (False, mw.returnJson(False, '参数:(' + ck[i] + ')没有!'))
|
||||
return (True, mw.returnJson(True, 'ok'))
|
||||
|
||||
|
||||
def getHomeDir():
|
||||
if mw.isAppleSystem():
|
||||
user = mw.execShell(
|
||||
"who | sed -n '2, 1p' |awk '{print $1}'")[0].strip()
|
||||
return '/Users/' + user
|
||||
else:
|
||||
return '/root'
|
||||
|
||||
|
||||
def getRunUser():
|
||||
if mw.isAppleSystem():
|
||||
user = mw.execShell(
|
||||
"who | sed -n '2, 1p' |awk '{print $1}'")[0].strip()
|
||||
return user
|
||||
else:
|
||||
return 'root'
|
||||
|
||||
def configTpl():
|
||||
path = getPluginDir() + '/hooks'
|
||||
pathFile = os.listdir(path)
|
||||
tmp = []
|
||||
for one in pathFile:
|
||||
file = path + '/' + one
|
||||
tmp.append(file)
|
||||
return mw.getJson(tmp)
|
||||
|
||||
|
||||
def readConfigTpl():
|
||||
args = getArgs()
|
||||
data = checkArgs(args, ['file'])
|
||||
if not data[0]:
|
||||
return data[1]
|
||||
|
||||
content = mw.readFile(args['file'])
|
||||
content = contentReplace(content)
|
||||
return mw.returnJson(True, 'ok', content)
|
||||
|
||||
def getPidFile():
|
||||
file = getConf()
|
||||
content = mw.readFile(file)
|
||||
rep = r'pidfile\s*(.*)'
|
||||
tmp = re.search(rep, content)
|
||||
return tmp.groups()[0].strip()
|
||||
|
||||
def status():
|
||||
return 'start'
|
||||
|
||||
def contentReplace(content):
|
||||
service_path = mw.getServerDir()
|
||||
content = content.replace('{$ROOT_PATH}', mw.getFatherDir())
|
||||
content = content.replace('{$SERVER_PATH}', service_path)
|
||||
content = content.replace('{$SERVER_APP}', service_path + '/redis')
|
||||
content = content.replace('{$REDIS_PASS}', mw.getRandomString(10))
|
||||
return content
|
||||
|
||||
|
||||
def pSqliteDb(dbname='dnsapi'):
|
||||
file = getServerDir() + '/acme.db'
|
||||
name = 'acme'
|
||||
|
||||
import_sql = mw.readFile(getPluginDir() + '/conf/acme.sql')
|
||||
md5_sql = mw.md5(import_sql)
|
||||
|
||||
import_sign = False
|
||||
save_md5_file = getServerDir() + '/acme.md5'
|
||||
if os.path.exists(save_md5_file):
|
||||
save_md5_sql = mw.readFile(save_md5_file)
|
||||
if save_md5_sql != md5_sql:
|
||||
import_sign = True
|
||||
mw.writeFile(save_md5_file, md5_sql)
|
||||
else:
|
||||
mw.writeFile(save_md5_file, md5_sql)
|
||||
|
||||
if not os.path.exists(file) or import_sql:
|
||||
conn = mw.M(dbname).dbPos(getServerDir(), name)
|
||||
csql_list = import_sql.split(';')
|
||||
for index in range(len(csql_list)):
|
||||
conn.execute(csql_list[index], ())
|
||||
|
||||
conn = mw.M(dbname).dbPos(getServerDir(), name)
|
||||
return conn
|
||||
|
||||
def initDreplace():
|
||||
|
||||
file_tpl = getInitDTpl()
|
||||
|
||||
initD_path = getServerDir() + '/init.d'
|
||||
if not os.path.exists(initD_path):
|
||||
os.mkdir(initD_path)
|
||||
file_bin = initD_path + '/' + getPluginName()
|
||||
|
||||
run_log_file = runLog()
|
||||
if not os.path.exists(run_log_file):
|
||||
mw.writeFile(run_log_file,'')
|
||||
|
||||
hook_file = getConf()
|
||||
if not os.path.exists(hook_file):
|
||||
mw.writeFile(hook_file,'')
|
||||
|
||||
return file_bin
|
||||
|
||||
|
||||
def apaOp(method):
|
||||
file = initDreplace()
|
||||
return 'ok'
|
||||
|
||||
# current_os = mw.getOs()
|
||||
# if current_os == "darwin":
|
||||
# data = mw.execShell(file + ' ' + method)
|
||||
# if data[1] == '':
|
||||
# return 'ok'
|
||||
# return data[1]
|
||||
|
||||
# if current_os.startswith("freebsd"):
|
||||
# data = mw.execShell('service ' + getPluginName() + ' ' + method)
|
||||
# if data[1] == '':
|
||||
# return 'ok'
|
||||
# return data[1]
|
||||
|
||||
# data = mw.execShell('systemctl ' + method + ' ' + getPluginName())
|
||||
# if data[1] == '':
|
||||
# return 'ok'
|
||||
# return data[1]
|
||||
|
||||
|
||||
def start():
|
||||
import tool_cron
|
||||
tool_cron.createBgTask()
|
||||
|
||||
return apaOp('start')
|
||||
|
||||
|
||||
def stop():
|
||||
import tool_cron
|
||||
tool_cron.removeBgTask()
|
||||
return apaOp('stop')
|
||||
|
||||
|
||||
def restart():
|
||||
status = apaOp('restart')
|
||||
return status
|
||||
|
||||
|
||||
def reload():
|
||||
return apaOp('reload')
|
||||
|
||||
|
||||
def dnsapiAdd():
|
||||
conn = pSqliteDb('dnsapi')
|
||||
args = getArgs()
|
||||
data = checkArgs(args,['id','name', 'type', 'remark'])
|
||||
if not data[0]:
|
||||
return data[1]
|
||||
|
||||
name = args['name'].strip()
|
||||
remark = args['remark'].strip()
|
||||
stype = args['type'].strip()
|
||||
val = args['val'].strip()
|
||||
sid = args['id'].strip()
|
||||
|
||||
if name == '':
|
||||
return mw.returnJson(False, '名称不能为空!')
|
||||
|
||||
if sid != '0' : #修改操作
|
||||
conn.where("id=?", (sid,)).update({
|
||||
'name':name,
|
||||
'type':stype,
|
||||
'val':val,
|
||||
'remark':remark,
|
||||
})
|
||||
return mw.returnJson(True, '修改成功!')
|
||||
|
||||
if conn.where("name=?", (name,)).count():
|
||||
return mw.returnJson(False, name+'已存在!')
|
||||
|
||||
addTime = time.strftime('%Y-%m-%d %X', time.localtime())
|
||||
err = conn.add('name,type,val,remark,addtime', (name, stype, val,remark, addTime))
|
||||
# print(err)
|
||||
return mw.returnJson(True, '添加成功!')
|
||||
|
||||
def dnsapiDel():
|
||||
args = getArgs()
|
||||
data = checkArgs(args, ['id', 'name'])
|
||||
if not data[0]:
|
||||
return data[1]
|
||||
|
||||
conn = pSqliteDb('dnsapi')
|
||||
try:
|
||||
sid = args['id']
|
||||
name = args['name']
|
||||
conn.where("id=?", (sid,)).delete()
|
||||
return mw.returnJson(True, '删除成功!')
|
||||
except Exception as ex:
|
||||
return mw.returnJson(False, '删除失败!' + str(ex))
|
||||
|
||||
def dnsapiList():
|
||||
args = getArgs()
|
||||
page = 1
|
||||
page_size = 10
|
||||
search = ''
|
||||
data = {}
|
||||
if 'page' in args:
|
||||
page = int(args['page'])
|
||||
|
||||
if 'page_size' in args:
|
||||
page_size = int(args['page_size'])
|
||||
|
||||
if 'search' in args:
|
||||
search = args['search']
|
||||
|
||||
conn = pSqliteDb('dnsapi')
|
||||
limit = str((page - 1) * page_size) + ',' + str(page_size)
|
||||
condition = ''
|
||||
if not search == '':
|
||||
condition = "name like '%" + search + "%'"
|
||||
field = 'id,name,type,val,remark,addtime'
|
||||
clist = conn.where(condition, ()).field(
|
||||
field).limit(limit).order('id desc').select()
|
||||
|
||||
count = conn.where(condition, ()).count()
|
||||
_page = {}
|
||||
_page['count'] = count
|
||||
_page['p'] = page
|
||||
_page['row'] = page_size
|
||||
_page['tojs'] = 'dbList'
|
||||
data['page'] = mw.getPage(_page)
|
||||
data['data'] = clist
|
||||
|
||||
return mw.getJson(data)
|
||||
|
||||
def dnsapiListAll():
|
||||
conn = pSqliteDb('dnsapi')
|
||||
field = 'id,name,type,val,remark,addtime'
|
||||
data = conn.field(field).limit('1000').select()
|
||||
return mw.getJson(data)
|
||||
|
||||
def emailList():
|
||||
args = getArgs()
|
||||
page = 1
|
||||
page_size = 10
|
||||
search = ''
|
||||
data = {}
|
||||
if 'page' in args:
|
||||
page = int(args['page'])
|
||||
|
||||
if 'page_size' in args:
|
||||
page_size = int(args['page_size'])
|
||||
|
||||
if 'search' in args:
|
||||
search = args['search']
|
||||
|
||||
conn = pSqliteDb('email')
|
||||
limit = str((page - 1) * page_size) + ',' + str(page_size)
|
||||
condition = ''
|
||||
if not search == '':
|
||||
condition = "addr like '%" + search + "%'"
|
||||
field = 'id,addr,remark,addtime'
|
||||
clist = conn.where(condition, ()).field(
|
||||
field).limit(limit).order('id desc').select()
|
||||
|
||||
count = conn.where(condition, ()).count()
|
||||
_page = {}
|
||||
_page['count'] = count
|
||||
_page['p'] = page
|
||||
_page['row'] = page_size
|
||||
_page['tojs'] = 'dbList'
|
||||
data['page'] = mw.getPage(_page)
|
||||
data['data'] = clist
|
||||
|
||||
return mw.getJson(data)
|
||||
|
||||
def emailAdd():
|
||||
args = getArgs()
|
||||
data = checkArgs(args,['addr', 'remark'])
|
||||
if not data[0]:
|
||||
return data[1]
|
||||
|
||||
addr = args['addr'].strip()
|
||||
remark = args['remark'].strip()
|
||||
|
||||
if addr == '':
|
||||
return mw.returnJson(False, '邮件地址不能为空!')
|
||||
|
||||
conn = pSqliteDb('email')
|
||||
|
||||
addTime = time.strftime('%Y-%m-%d %X', time.localtime())
|
||||
conn.add('addr,remark,addtime', (addr, remark, addTime))
|
||||
return mw.returnJson(True, '添加成功!')
|
||||
|
||||
def emailDel():
|
||||
args = getArgs()
|
||||
data = checkArgs(args, ['id', 'name'])
|
||||
if not data[0]:
|
||||
return data[1]
|
||||
|
||||
conn = pSqliteDb('email')
|
||||
try:
|
||||
sid = args['id']
|
||||
name = args['name']
|
||||
conn.where("id=?", (sid,)).delete()
|
||||
return mw.returnJson(True, '删除成功!')
|
||||
except Exception as ex:
|
||||
return mw.returnJson(False, '删除失败!' + str(ex))
|
||||
|
||||
def domainAdd():
|
||||
args = getArgs()
|
||||
data = checkArgs(args,['id','domain', 'dnsapi_id','email','remark'])
|
||||
if not data[0]:
|
||||
return data[1]
|
||||
|
||||
sid = args['id'].strip()
|
||||
domain = args['domain'].strip()
|
||||
remark = args['remark'].strip()
|
||||
email = args['email'].strip()
|
||||
dnsapi_id = args['dnsapi_id'].strip()
|
||||
|
||||
if domain == '':
|
||||
return mw.returnJson(False, '域名不能为空!')
|
||||
if email == '':
|
||||
return mw.returnJson(False, '邮件不能为空!')
|
||||
|
||||
conn = pSqliteDb('domain')
|
||||
|
||||
if sid != '0' : #修改操作
|
||||
conn.where("id=?", (sid,)).update({
|
||||
'domain':domain,
|
||||
'dnsapi_id':dnsapi_id,
|
||||
'email':email,
|
||||
'remark':remark,
|
||||
})
|
||||
return mw.returnJson(True, '修改成功!')
|
||||
|
||||
if conn.where("domain=?", (domain,)).count():
|
||||
return mw.returnJson(False, domain+'已存在!')
|
||||
|
||||
|
||||
|
||||
addTime = time.strftime('%Y-%m-%d %X', time.localtime())
|
||||
conn.add('domain,dnsapi_id,email,remark,addtime', (domain, dnsapi_id,email,remark, addTime))
|
||||
return mw.returnJson(True, '添加成功!')
|
||||
|
||||
def domainDel():
|
||||
args = getArgs()
|
||||
data = checkArgs(args, ['id', 'name'])
|
||||
if not data[0]:
|
||||
return data[1]
|
||||
|
||||
conn = pSqliteDb('domain')
|
||||
try:
|
||||
sid = args['id']
|
||||
name = args['name']
|
||||
conn.where("id=?", (sid,)).delete()
|
||||
return mw.returnJson(True, '删除成功!')
|
||||
except Exception as ex:
|
||||
return mw.returnJson(False, '删除失败!' + str(ex))
|
||||
|
||||
def domainStatusToggle():
|
||||
args = getArgs()
|
||||
data = checkArgs(args, ['id'])
|
||||
if not data[0]:
|
||||
return data[1]
|
||||
|
||||
conn = pSqliteDb('domain')
|
||||
|
||||
field = 'id,status'
|
||||
fdata = conn.field(field).where('id=?',(args['id'],)).find()
|
||||
|
||||
fstatus = fdata['status']
|
||||
if fstatus == 0:
|
||||
fstatus = 1
|
||||
else:
|
||||
fstatus = 0
|
||||
# print(fdata['status'],fstatus)
|
||||
conn.where("id=?", (args['id'],)).update({
|
||||
'status':fstatus,
|
||||
})
|
||||
return mw.returnJson(True, '切换成功!')
|
||||
|
||||
def domainList():
|
||||
args = getArgs()
|
||||
page = 1
|
||||
page_size = 10
|
||||
search = ''
|
||||
data = {}
|
||||
if 'page' in args:
|
||||
page = int(args['page'])
|
||||
|
||||
if 'page_size' in args:
|
||||
page_size = int(args['page_size'])
|
||||
|
||||
if 'search' in args:
|
||||
search = args['search']
|
||||
|
||||
conn = pSqliteDb('domain')
|
||||
conn_dnsapi = pSqliteDb('dnsapi')
|
||||
limit = str((page - 1) * page_size) + ',' + str(page_size)
|
||||
condition = ''
|
||||
if not search == '':
|
||||
condition = "domain like '%" + search + "%'"
|
||||
field = 'id,domain,dnsapi_id,email,status,effective_date,expiration_date,remark,addtime'
|
||||
clist = conn.where(condition, ()).field(
|
||||
field).limit(limit).order('id desc').select()
|
||||
|
||||
for i in range(len(clist)):
|
||||
tdata = conn_dnsapi.field('id,name').where('id=?',(clist[i]['dnsapi_id'],)).select()
|
||||
if len(tdata) > 0:
|
||||
# print(tdata[0]['name'])
|
||||
clist[i]['dnsapi_id_alias'] = clist[i]['dnsapi_id']+'('+ tdata[0]['name'] +')'
|
||||
else:
|
||||
clist[i]['dnsapi_id_alias'] = clist[i]['dnsapi_id']+' (无)'
|
||||
|
||||
count = conn.where(condition, ()).count()
|
||||
_page = {}
|
||||
_page['count'] = count
|
||||
_page['p'] = page
|
||||
_page['row'] = page_size
|
||||
_page['tojs'] = 'dbList'
|
||||
data['page'] = mw.getPage(_page)
|
||||
data['data'] = clist
|
||||
|
||||
return mw.getJson(data)
|
||||
|
||||
def getDnsapiData(dnsapi_id):
|
||||
if dnsapi_id == '0':
|
||||
return {}
|
||||
conn_dnsapi = pSqliteDb('dnsapi')
|
||||
tdata = conn_dnsapi.field('id,name,type,val').where('id=?',(dnsapi_id,)).select()
|
||||
|
||||
if len(tdata)>0:
|
||||
return tdata[0]
|
||||
return {}
|
||||
|
||||
def getDnsapiExportVar(val):
|
||||
cmd_list = val.split('~')
|
||||
def_var = ''
|
||||
for x in range(len(cmd_list)):
|
||||
v = cmd_list[x]
|
||||
vlist = v.split('|')
|
||||
def_var += 'export '+vlist[0]+'="'+vlist[1]+'"\n'
|
||||
return def_var
|
||||
|
||||
def getDnsapiKv(val):
|
||||
cmd_list = val.split('~')
|
||||
def_var = {}
|
||||
for x in range(len(cmd_list)):
|
||||
v = cmd_list[x]
|
||||
vlist = v.split('|')
|
||||
kk = vlist[0]
|
||||
vv = vlist[1]
|
||||
def_var[kk] = vv
|
||||
return def_var
|
||||
|
||||
def domainApplyPathJudge(domain):
|
||||
acme_dir = "/root/.acme.sh"
|
||||
if mw.isAppleSystem():
|
||||
user = getRunUser()
|
||||
acme_dir = "/Users/"+user+"/.acme.sh"
|
||||
|
||||
abs_domain_path = acme_dir+'/'+domain+'_ecc'
|
||||
# print(abs_domain_path)
|
||||
if os.path.exists(abs_domain_path):
|
||||
return True, abs_domain_path
|
||||
|
||||
abs_domain_path = acme_dir+'/'+domain
|
||||
# print(abs_domain_path)
|
||||
if os.path.exists(abs_domain_path):
|
||||
return True, abs_domain_path
|
||||
|
||||
return False,''
|
||||
|
||||
|
||||
def hookWriteLog(line):
|
||||
hook_file = runLog()
|
||||
mdate = time.strftime('%Y-%m-%d %X', time.localtime())
|
||||
log = "["+mdate+"]:"+line
|
||||
print(log)
|
||||
return mw.writeFile(hook_file,log+"\n",'a+')
|
||||
|
||||
def runHookPy(domain,path):
|
||||
# print(domain,path)
|
||||
run_log = runLog()
|
||||
hook_file = getConf()
|
||||
cmd = 'cd '+mw.getPanelDir()
|
||||
cmd += ' && python3 '+hook_file + ' ' + domain + ' ' + path
|
||||
cmd += ' >> '+ run_log
|
||||
print(cmd)
|
||||
return mw.execShell(cmd)
|
||||
|
||||
def autoUpdateSslData(domain, path):
|
||||
ssl_cer_file = path + '/'+domain+'.cer'
|
||||
ssl_info = mw.getCertName(ssl_cer_file)
|
||||
|
||||
time_begin = int(time.mktime(time.strptime(ssl_info['notBefore'], "%Y-%m-%d")))
|
||||
time_end = int(time.mktime(time.strptime(ssl_info['notAfter'], "%Y-%m-%d")))
|
||||
# print(ssl_info)
|
||||
# print(time_begin,time_end)
|
||||
conn = pSqliteDb('domain')
|
||||
conn.where('domain=?', (domain,)).update({
|
||||
'effective_date':str(time_begin),
|
||||
'expiration_date':str(time_end),
|
||||
})
|
||||
|
||||
return True
|
||||
|
||||
def runHookDstDomain(row):
|
||||
domain = row['domain']
|
||||
email = str(row['email'])
|
||||
|
||||
if row['effective_date'] != '':
|
||||
effective_date = int(row['effective_date'])
|
||||
now_int = int(time.time())
|
||||
day = (now_int - effective_date)/86400
|
||||
if int(day) < 8:
|
||||
common_log = '【'+domain+'】未过期'
|
||||
hookWriteLog(common_log)
|
||||
return
|
||||
|
||||
hookWriteLog('开始申请【'+domain+'】SSL证书')
|
||||
cmd = "#!/bin/bash\n"
|
||||
cmd += "PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin:/opt/homebrew/bin:/root/.acme.sh\n"
|
||||
cmd += "export PATH\n"
|
||||
|
||||
if mw.isAppleSystem():
|
||||
user = getRunUser()
|
||||
cmd += ". /Users/"+user+"/.zshrc\n"
|
||||
|
||||
cmd_data = getDnsapiData(row['dnsapi_id'])
|
||||
# print(cmd_data)
|
||||
export_val = getDnsapiExportVar(cmd_data['val'])
|
||||
cmd += export_val
|
||||
|
||||
# acme.sh --register-account -m my@example.com
|
||||
cmd_register = 'acme.sh --register-account -m '+ email + '\n'
|
||||
cmd += cmd_register
|
||||
|
||||
|
||||
# acme.sh --issue -d "example.com" -d "*.example.com" --dns dns_cf
|
||||
cmd_apply = 'acme.sh --issue --dns '+str(cmd_data['type'])+' -d '+domain+' -d "*.'+domain+'"'
|
||||
if row['effective_date'] != '':
|
||||
effective_date = int(row['effective_date'])
|
||||
now_int = int(time.time())
|
||||
day = (now_int - effective_date)/86400
|
||||
if int(day) > 7:
|
||||
cmd_apply = 'acme.sh --issue --dns '+str(cmd_data['type'])+' -d '+domain+' -d "*.'+domain+'" --force'
|
||||
cmd += cmd_apply
|
||||
|
||||
run_log = runLog()
|
||||
cmd += ' >> '+ run_log
|
||||
print(cmd)
|
||||
os.system(cmd)
|
||||
hookWriteLog('结束申请【'+domain+'】SSL证书')
|
||||
isok, path = domainApplyPathJudge(domain)
|
||||
print(isok,path)
|
||||
if isok:
|
||||
autoUpdateSslData(domain, path)
|
||||
hookWriteLog('开始执行【'+domain+'】Hook脚本')
|
||||
runHookPy(domain,path)
|
||||
hookWriteLog('结束执行【'+domain+'】Hook脚本')
|
||||
|
||||
def getHookIdCmd():
|
||||
args = getArgs()
|
||||
data = checkArgs(args, ['id'])
|
||||
if not data[0]:
|
||||
return data[1]
|
||||
|
||||
cmd = "cd "+mw.getPanelDir()+" "
|
||||
cmd += '&& python3 plugins/acme_pandominassl_apply/index.py run_hook_id 1.0 {"id":"'+args['id']+'"}'
|
||||
return mw.returnJson(True, 'ok',cmd)
|
||||
|
||||
def runHookId():
|
||||
args = getArgs()
|
||||
data = checkArgs(args, ['id'])
|
||||
if not data[0]:
|
||||
return data[1]
|
||||
|
||||
conn = pSqliteDb('domain')
|
||||
field = 'id,domain,dnsapi_id,email,effective_date,expiration_date,remark'
|
||||
row = conn.field(field).where('id=?',(args['id'],)).find()
|
||||
runHookDstDomain(row)
|
||||
return 'run hook '+args['id']+' end'
|
||||
|
||||
def runHookCmd():
|
||||
cmd = "cd "+mw.getPanelDir()+" "
|
||||
cmd += '&& python3 plugins/acme_pandominassl_apply/index.py run_hook'
|
||||
return mw.returnJson(True, 'ok',cmd)
|
||||
|
||||
def runHook():
|
||||
conn = pSqliteDb('domain')
|
||||
|
||||
field = 'id,domain,dnsapi_id,email,effective_date,expiration_date,remark'
|
||||
clist = conn.field(field).where('status=?',(1,)).limit('1000').order('id desc').select()
|
||||
|
||||
for idx in range(len(clist)):
|
||||
row = clist[idx]
|
||||
# print(row)
|
||||
runHookDstDomain(row)
|
||||
time.sleep(1)
|
||||
return 'run hook end'
|
||||
|
||||
def autoSyncDomain(domain, dnsapi_id, email):
|
||||
conn = pSqliteDb('domain')
|
||||
|
||||
field = 'id,domain,dnsapi_id,email,effective_date,expiration_date,remark'
|
||||
fdata = conn.field(field).where('domain=?',(domain,)).select()
|
||||
# print(fdata)
|
||||
mdate = time.strftime('%Y-%m-%d %X', time.localtime())
|
||||
if len(fdata) == 0:
|
||||
conn.add('domain,dnsapi_id,email,remark,addtime', (domain, dnsapi_id, email,"备注", mdate))
|
||||
print(domain+" 同步成功!")
|
||||
return True
|
||||
|
||||
def runSyncCfDataRow(row, page = 1):
|
||||
# print(row, page)
|
||||
|
||||
cf_key = row['kv']['CF_Key']
|
||||
cf_mail = row['kv']['CF_Email']
|
||||
header = {
|
||||
'X-Auth-Email': cf_mail,
|
||||
'X-Auth-Key': cf_key,
|
||||
'Content-Type': 'application/json'
|
||||
}
|
||||
url = "https://api.cloudflare.com/client/v4/zones?status=active&page="+str(page)+"&per_page=20&order=status&direction=desc&match=all"
|
||||
try:
|
||||
r = requests.get(url, timeout=30, headers=header)
|
||||
r.raise_for_status()
|
||||
r.encoding = r.apparent_encoding
|
||||
# print(r.text)
|
||||
jdata = json.loads(r.text)
|
||||
result = jdata['result']
|
||||
result_info = jdata['result_info']
|
||||
|
||||
# print(len(result))
|
||||
for i in result:
|
||||
autoSyncDomain(i['name'], row['id'], cf_mail)
|
||||
|
||||
if result_info['total_pages'] > page:
|
||||
page += 1
|
||||
runSyncCfDataRow(row, page)
|
||||
# print(result)
|
||||
except Exception as e:
|
||||
print(e)
|
||||
|
||||
# 同步CloudFlare数据
|
||||
def runSyncCfData():
|
||||
print("同步CloudFlare数据开始")
|
||||
conn_dnsapi = pSqliteDb('dnsapi')
|
||||
field = "id,name,type,val"
|
||||
fdata = conn_dnsapi.field(field).where('type=?', ('dns_cf',)).limit('10').select()
|
||||
|
||||
for x in range(len(fdata)):
|
||||
row = fdata[x]
|
||||
dnsapi_kv = getDnsapiKv(row['val'])
|
||||
row['kv'] = dnsapi_kv
|
||||
runSyncCfDataRow(row)
|
||||
|
||||
print("同步CloudFlare数据结束")
|
||||
return ''
|
||||
|
||||
def runSyncCfCmd():
|
||||
cmd = "cd "+mw.getPanelDir()+" "
|
||||
cmd += '&& python3 plugins/acme_pandominassl_apply/index.py run_sync_cf_data'
|
||||
return mw.returnJson(True, 'ok',cmd)
|
||||
|
||||
def runSyncDnspodDataRow(row, page = 1):
|
||||
# print(row, page)
|
||||
|
||||
DPI_Id = row['kv']['DPI_Id']
|
||||
DPI_Key = row['kv']['DPI_Key']
|
||||
data = {
|
||||
'login_token': DPI_Id+','+DPI_Key,
|
||||
'length':5,
|
||||
'format':'json',
|
||||
}
|
||||
url = "https://api.dnspod.com/Domain.List"
|
||||
try:
|
||||
r = requests.post(url, timeout=30, data=data)
|
||||
r.raise_for_status()
|
||||
r.encoding = r.apparent_encoding
|
||||
|
||||
# print(r.text)
|
||||
jdata = json.loads(r.text)
|
||||
info = jdata['info']
|
||||
domains = jdata['domains']
|
||||
|
||||
# print(jdata)
|
||||
for i in domains:
|
||||
autoSyncDomain(i['name'], row['id'], i['owner'])
|
||||
|
||||
page_total = int(info['domain_total']/data['length'])
|
||||
if page_total > page:
|
||||
page += 1
|
||||
runSyncDnspodDataRow(row, page)
|
||||
except Exception as e:
|
||||
print(e)
|
||||
|
||||
# 同步DnsPod数据
|
||||
def runSyncDnsPodData():
|
||||
print("同步DnsPod数据开始")
|
||||
conn_dnsapi = pSqliteDb('dnsapi')
|
||||
field = "id,name,type,val"
|
||||
fdata = conn_dnsapi.field(field).where('type=?', ('dns_dpi',)).limit('10').select()
|
||||
for x in range(len(fdata)):
|
||||
row = fdata[x]
|
||||
dnsapi_kv = getDnsapiKv(row['val'])
|
||||
row['kv'] = dnsapi_kv
|
||||
runSyncDnspodDataRow(row)
|
||||
|
||||
print(fdata)
|
||||
print("同步DnsPod数据结束")
|
||||
return ''
|
||||
|
||||
def runSyncDnsPodCmd():
|
||||
cmd = "cd "+mw.getPanelDir()+" "
|
||||
cmd += '&& python3 plugins/acme_pandominassl_apply/index.py run_sync_dnspod_data'
|
||||
return mw.returnJson(True, 'ok',cmd)
|
||||
|
||||
if __name__ == "__main__":
|
||||
func = sys.argv[1]
|
||||
# print(func)
|
||||
if func == 'status':
|
||||
print(status())
|
||||
elif func == 'start':
|
||||
print(start())
|
||||
elif func == 'stop':
|
||||
print(stop())
|
||||
elif func == 'restart':
|
||||
print(restart())
|
||||
elif func == 'reload':
|
||||
print(reload())
|
||||
elif func == 'run_info':
|
||||
print(runInfo())
|
||||
elif func == 'conf':
|
||||
print(getConf())
|
||||
elif func == 'run_log':
|
||||
print(runLog())
|
||||
elif func == 'config_tpl':
|
||||
print(configTpl())
|
||||
elif func == 'read_config_tpl':
|
||||
print(readConfigTpl())
|
||||
elif func == 'dnsapi_list':
|
||||
print(dnsapiList())
|
||||
elif func == 'dnsapi_list_all':
|
||||
print(dnsapiListAll())
|
||||
elif func == 'dnsapi_add':
|
||||
print(dnsapiAdd())
|
||||
elif func == 'dnsapi_del':
|
||||
print(dnsapiDel())
|
||||
elif func == 'email_list':
|
||||
print(emailList())
|
||||
elif func == 'email_add':
|
||||
print(emailAdd())
|
||||
elif func == 'email_del':
|
||||
print(emailDel())
|
||||
elif func == 'domain_list':
|
||||
print(domainList())
|
||||
elif func == 'domain_add':
|
||||
print(domainAdd())
|
||||
elif func == 'domain_del':
|
||||
print(domainDel())
|
||||
elif func == 'domain_status_toggle':
|
||||
print(domainStatusToggle())
|
||||
elif func == 'run_hook':
|
||||
print(runHook())
|
||||
elif func == 'run_hook_cmd':
|
||||
print(runHookCmd())
|
||||
elif func == 'get_run_hook_id_cmd':
|
||||
print(getHookIdCmd())
|
||||
elif func == 'run_hook_id':
|
||||
print(runHookId())
|
||||
elif func == 'run_sync_cf_data':
|
||||
print(runSyncCfData())
|
||||
elif func == 'run_sync_cf_cmd':
|
||||
print(runSyncCfCmd())
|
||||
elif func == 'run_sync_dnspod_data':
|
||||
print(runSyncDnsPodData())
|
||||
elif func == 'run_sync_dnspod_cmd':
|
||||
print(runSyncDnsPodCmd())
|
||||
else:
|
||||
print('error')
|
||||
@@ -1,17 +0,0 @@
|
||||
{
|
||||
"sort": 7,
|
||||
"ps": "ACME泛域名SSL申请/管理",
|
||||
"name": "acme_pandominassl_apply",
|
||||
"title": "ACME泛域名SSL",
|
||||
"shell": "install.sh",
|
||||
"versions":["1.0"],
|
||||
"tip": "soft",
|
||||
"checks": "server/acme_pandominassl_apply",
|
||||
"path": "server/acme_pandominassl_apply",
|
||||
"display": 1,
|
||||
"author": "ACME",
|
||||
"date": "2024-09-12",
|
||||
"home": "https://github.com/acmesh-official/acme.sh",
|
||||
"type": 0,
|
||||
"pid": "5"
|
||||
}
|
||||
@@ -1,54 +0,0 @@
|
||||
#!/bin/bash
|
||||
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin:/opt/homebrew/bin
|
||||
export PATH
|
||||
|
||||
curPath=`pwd`
|
||||
rootPath=$(dirname "$curPath")
|
||||
rootPath=$(dirname "$rootPath")
|
||||
serverPath=$(dirname "$rootPath")
|
||||
|
||||
VERSION=$2
|
||||
|
||||
# curl https://get.acme.sh | sh
|
||||
# acme.sh --uninstall
|
||||
|
||||
|
||||
# . /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
|
||||
. ${rootPath}/bin/activate
|
||||
fi
|
||||
|
||||
# pip install cloudflare
|
||||
Install_App()
|
||||
{
|
||||
echo '正在安装脚本文件...'
|
||||
mkdir -p $serverPath/source
|
||||
mkdir -p $serverPath/acme_pandominassl_apply
|
||||
echo "${VERSION}" > $serverPath/acme_pandominassl_apply/version.pl
|
||||
|
||||
cd ${rootPath} && python3 ${rootPath}/plugins/acme_pandominassl_apply/index.py start
|
||||
echo '安装[ACME泛域名SSL]完成'
|
||||
}
|
||||
|
||||
Uninstall_App()
|
||||
{
|
||||
cd ${rootPath} && python3 ${rootPath}/plugins/acme_pandominassl_apply/index.py stop
|
||||
rm -rf $serverPath/acme_pandominassl_apply
|
||||
echo "卸载[ACME泛域名SSL]成功"
|
||||
}
|
||||
|
||||
action=$1
|
||||
if [ "${1}" = 'install' ];then
|
||||
Install_App
|
||||
else
|
||||
Uninstall_App
|
||||
fi
|
||||
@@ -1,801 +0,0 @@
|
||||
function apaPost(method, args,callback){
|
||||
var loadT = layer.msg('正在获取...', { icon: 16, time: 0, shade: 0.3 });
|
||||
|
||||
var req_data = {};
|
||||
req_data['name'] = 'acme_pandominassl_apply';
|
||||
req_data['func'] = method;
|
||||
req_data['version'] = $('.plugin_version').attr('version');
|
||||
|
||||
if (typeof(args) == 'string'){
|
||||
req_data['args'] = JSON.stringify(toArrayObject(args));
|
||||
} else {
|
||||
req_data['args'] = JSON.stringify(args);
|
||||
}
|
||||
|
||||
$.post('/plugins/run', req_data, function(data) {
|
||||
layer.close(loadT);
|
||||
if (!data.status){
|
||||
//错误展示10S
|
||||
layer.msg(data.msg,{icon:0,time:2000,shade: [10, '#000']});
|
||||
return;
|
||||
}
|
||||
|
||||
if(typeof(callback) == 'function'){
|
||||
callback(data);
|
||||
}
|
||||
},'json');
|
||||
}
|
||||
|
||||
|
||||
function apaPostN(method, args,callback){
|
||||
|
||||
var req_data = {};
|
||||
req_data['name'] = 'acme_pandominassl_apply';
|
||||
req_data['func'] = method;
|
||||
req_data['version'] = $('.plugin_version').attr('version');
|
||||
|
||||
if (typeof(args) == 'string'){
|
||||
req_data['args'] = JSON.stringify(toArrayObject(args));
|
||||
} else {
|
||||
req_data['args'] = JSON.stringify(args);
|
||||
}
|
||||
|
||||
$.post('/plugins/run', req_data, function(data) {
|
||||
if (!data.status){
|
||||
//错误展示10S
|
||||
layer.msg(data.msg,{icon:0,time:2000,shade: [10, '#000']});
|
||||
return;
|
||||
}
|
||||
|
||||
if(typeof(callback) == 'function'){
|
||||
callback(data);
|
||||
}
|
||||
},'json');
|
||||
}
|
||||
|
||||
function apaPostCallbak(method, version, args,callback){
|
||||
var loadT = layer.msg('正在获取...', { icon: 16, time: 0, shade: 0.3 });
|
||||
|
||||
var req_data = {};
|
||||
req_data['name'] = 'acme_pandominassl_apply';
|
||||
req_data['func'] = method;
|
||||
args['version'] = version;
|
||||
|
||||
if (typeof(args) == 'string'){
|
||||
req_data['args'] = JSON.stringify(toArrayObject(args));
|
||||
} else {
|
||||
req_data['args'] = JSON.stringify(args);
|
||||
}
|
||||
|
||||
$.post('/plugins/callback', req_data, function(data) {
|
||||
layer.close(loadT);
|
||||
if (!data.status){
|
||||
layer.msg(data.msg,{icon:0,time:2000,shade: [0.3, '#000']});
|
||||
return;
|
||||
}
|
||||
|
||||
if(typeof(callback) == 'function'){
|
||||
callback(data);
|
||||
}
|
||||
},'json');
|
||||
}
|
||||
|
||||
function apaReadme(){
|
||||
var readme = '<ul class="help-info-text c7">';
|
||||
readme += '<li>ACME泛域名SSL申请/管理/HOOK</li>';
|
||||
readme += '<li>通过DNS验证获取SSL证书!</li>';
|
||||
readme += '<li>HOOK: ssl发生变动时调用!</li>';
|
||||
readme += '<li>暂时仅支持1000个域名管理!</li>';
|
||||
readme += '<li>DNSAPI文档: https://github.com/acmesh-official/acme.sh/wiki/dnsapi</li>';
|
||||
readme += '<li>默认7天强制更新!</li>';
|
||||
|
||||
|
||||
readme += '</ul>';
|
||||
$('.soft-man-con').html(readme);
|
||||
}
|
||||
|
||||
|
||||
function emailDel(id, name){
|
||||
safeMessage('删除['+name+']','您真的要删除['+name+']吗?',function(){
|
||||
var data='id='+id+'&name='+name;
|
||||
apaPost('email_del', data, function(data){
|
||||
var rdata = $.parseJSON(data.data);
|
||||
showMsg(rdata.msg,function(){
|
||||
emailList();
|
||||
},{icon: rdata.status ? 1 : 2}, 600);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function emailAdd(type){
|
||||
layer.open({
|
||||
type: 1,
|
||||
area: '500px',
|
||||
title: '添加邮件地址',
|
||||
closeBtn: 1,
|
||||
shift: 5,
|
||||
shadeClose: true,
|
||||
btn:["提交","关闭"],
|
||||
content: "<form class='bt-form pd20' id='email_add'>\
|
||||
<div class='line'>\
|
||||
<span class='tname'>邮件地址</span>\
|
||||
<div class='info-r'><input name='addr' class='bt-input-text mr5' style='width:100%;' placeholder='邮件地址' type='text'></div>\
|
||||
</div>\
|
||||
<div class='line'>\
|
||||
<span class='tname'>备注</span>\
|
||||
<div class='info-r'><input name='remark' class='bt-input-text mr5' style='width:100%;' placeholder='备注' type='text'></div>\
|
||||
</div>\
|
||||
</form>",
|
||||
success:function(){
|
||||
$("input[name='addr']").keyup(function(){
|
||||
var v = $(this).val();
|
||||
$("input[name='remark']").val(v);
|
||||
});
|
||||
},
|
||||
yes:function(index) {
|
||||
var data = $("#email_add").serialize();
|
||||
data = decodeURIComponent(data);
|
||||
// data = toArrayObject(data);
|
||||
apaPost('email_add', data, function(data){
|
||||
var rdata = $.parseJSON(data.data);
|
||||
showMsg(rdata.msg,function(){
|
||||
if (rdata.status){
|
||||
layer.close(index);
|
||||
emailList();
|
||||
}
|
||||
},{icon: rdata.status ? 1 : 2}, 2000);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
function emailList(page, search){
|
||||
var _data = {};
|
||||
if (typeof(page) =='undefined'){
|
||||
var page = 1;
|
||||
}
|
||||
|
||||
_data['page'] = page;
|
||||
_data['page_size'] = 10;
|
||||
if(typeof(search) != 'undefined'){
|
||||
_data['search'] = search;
|
||||
}
|
||||
|
||||
apaPost('email_list', _data, function(data){
|
||||
var rdata = $.parseJSON(data.data);
|
||||
var list = '';
|
||||
for(i in rdata.data){
|
||||
list += '<tr>';
|
||||
list +='<td><input value="'+rdata.data[i]['id']+'" class="check" onclick="checkSelect();" type="checkbox"></td>';
|
||||
list += '<td>' + rdata.data[i]['addr'] +'</td>';
|
||||
list += '<td>' + rdata.data[i]['remark'] +'</td>';
|
||||
list += '<td style="text-align:right">';
|
||||
list += '<a href="javascript:;" class="btlink" onclick="emailDel(\''+rdata.data[i]['id']+'\',\''+rdata.data[i]['addr']+'\')" title="删除">删除</a>' +
|
||||
'</td>';
|
||||
list += '</tr>';
|
||||
}
|
||||
|
||||
var con = '<div class="safe bgw">\
|
||||
<button onclick="emailAdd()" title="添加邮件地址" class="btn btn-success btn-sm" type="button" style="margin-right: 5px;">添加邮件地址</button>\
|
||||
<span style="float:right"> \
|
||||
<button batch="true" style="float: right;display: none;margin-left:10px;" onclick="delDbBatch();" title="删除选中项" class="btn btn-default btn-sm">删除选中</button>\
|
||||
</span>\
|
||||
<div class="divtable mtb10">\
|
||||
<div class="tablescroll">\
|
||||
<table id="DataBody" class="table table-hover" width="100%" cellspacing="0" cellpadding="0" border="0" style="border: 0 none;">\
|
||||
<thead><tr><th width="30"><input class="check" onclick="checkSelect();" type="checkbox"></th>\
|
||||
<th>邮件地址</th>\
|
||||
<th>备注</th>\
|
||||
<th style="text-align:right;">操作</th></tr></thead>\
|
||||
<tbody>'+ list +'</tbody>\
|
||||
</table>\
|
||||
</div>\
|
||||
<div class="dataTables_paginate paging_bootstrap page"></div>\
|
||||
</div>\
|
||||
</div>';
|
||||
|
||||
$(".soft-man-con").html(con);
|
||||
$('.dataTables_paginate').html(rdata.page);
|
||||
|
||||
readerTableChecked();
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
function dnsapiDel(id, name){
|
||||
safeMessage('删除['+name+']','您真的要删除['+name+']吗?',function(){
|
||||
var data='id='+id+'&name='+name;
|
||||
apaPost('dnsapi_del', data, function(data){
|
||||
var rdata = $.parseJSON(data.data);
|
||||
showMsg(rdata.msg,function(){
|
||||
dnsapiList();
|
||||
},{icon: rdata.status ? 1 : 2}, 600);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
var dnsapi_option = [
|
||||
{"name":"dns_cf", "title":'cloudflare', 'key':'CF_Key:CF_Email'},
|
||||
{"name":"dns_dp", "title":'dnspod/国内', 'key':'DP_Id:DP_Key'},
|
||||
{"name":"dns_dpi", "title":'dnspod/国际', 'key':'DPI_Id:DPI_Key'},
|
||||
{"name":"dns_gd", "title":'GoDaddy', 'key':'GD_Key:GD_Secret'},
|
||||
{"name":"dns_pdns", "title":'PowerDNS', 'key':'PDNS_Url:PDNS_ServerId:PDNS_Token:PDNS_Ttl'},
|
||||
{"name":"dns_lua", "title":'LuaDNS', 'key':'LUA_Key:LUA_Email'},
|
||||
{"name":"dns_me", "title":'DNSMadeEasy', 'key':'ME_Key:ME_Secret'},
|
||||
{"name":"dns_aws", "title":'Amazon Route53', 'key':'AWS_ACCESS_KEY_ID:AWS_SECRET_ACCESS_KEY'},
|
||||
{"name":"dns_ali", "title":'Aliyun', 'key':'Ali_Key:Ali_Secret'},
|
||||
{"name":"dns_ispconfig", "title":'ISPConfig', 'key':'ISPC_User:ISPC_Password:ISPC_Api:ISPC_Api_Insecure'},
|
||||
{"name":"dns_ad", "title":'Alwaysdata', 'key':'AD_API_KEY'},
|
||||
{"name":"dns_linode_v4", "title":'Linode', 'key':'LINODE_V4_API_KEY'},
|
||||
{"name":"dns_freedns", "title":'FreeDNS', 'key':'FREEDNS_User:FREEDNS_Password'},
|
||||
{"name":"dns_cyon", "title":'cyon.ch', 'key':'CY_Username:CY_Password:CY_OTP_Secret'},
|
||||
{"name":"dns_gandi_livedns", "title":'LiveDNS', 'key':'GANDI_LIVEDNS_TOKEN'},
|
||||
{"name":"dns_knot", "title":'Knot', 'key':'KNOT_SERVER:KNOT_KEY'},
|
||||
{"name":"dns_dgon", "title":'DigitalOcean', 'key':'DO_API_KEY'},
|
||||
{"name":"dns_cloudns", "title":'ClouDNS.net', 'key':'CLOUDNS_SUB_AUTH_ID:CLOUDNS_AUTH_PASSWORD'},
|
||||
{"name":"dns_namesilo", "title":'Namesilo', 'key':'Namesilo_Key'},
|
||||
{"name":"dns_azure", "title":'Azure', 'key':'AZUREDNS_SUBSCRIPTIONID:AZUREDNS_TENANTID:AZUREDNS_APPID:AZUREDNS_CLIENTSECRET'},
|
||||
{"name":"dns_selectel", "title":'selectel.com', 'key':'SL_Key'},
|
||||
{"name":"dns_zonomi", "title":'zonomi.com', 'key':'ZM_Key'},
|
||||
{"name":"dns_kinghost", "title":'KingHost', 'key':'KINGHOST_Username:KINGHOST_Password'},
|
||||
{"name":"dns_zilore", "title":'Zilore', 'key':'Zilore_Key'},
|
||||
{"name":"dns_gcloud", "title":'Google Cloud DNS', 'key':'CLOUDSDK_ACTIVE_CONFIG_NAME'},
|
||||
{"name":"dns_mydnsjp", "title":'MyDNS.JP', 'key':'MYDNSJP_MasterID:MYDNSJP_Password'},
|
||||
{"name":"dns_doapi", "title":'do.de', 'key':'DO_LETOKEN'},
|
||||
{"name":"dns_online", "title":'Online', 'key':'ONLINE_API_KEY'},
|
||||
{"name":"dns_cn", "title":'Core-Networks', 'key':'CN_User:CN_Password'},
|
||||
{"name":"dns_ultra", "title":'UltraDNS', 'key':'ULTRA_USR:ULTRA_PWD'},
|
||||
{"name":"dns_hetzner", "title":'Hetzner', 'key':'HETZNER_Token'},
|
||||
{"name":"dns_ddnss", "title":'DDNSS.de', 'key':'DDNSS_Token'},
|
||||
];
|
||||
|
||||
function getDnsapiKey(name){
|
||||
for (var i = 0; i < dnsapi_option.length; i++) {
|
||||
if (dnsapi_option[i]['name'] == name){
|
||||
return dnsapi_option[i]['key'];
|
||||
}
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
function getDnsapiTitle(name){
|
||||
for (var i = 0; i < dnsapi_option.length; i++) {
|
||||
if (dnsapi_option[i]['name'] == name){
|
||||
return dnsapi_option[i]['title'];
|
||||
}
|
||||
}
|
||||
return '其他';
|
||||
}
|
||||
|
||||
|
||||
|
||||
function dnsapiAdd(row){
|
||||
// console.log(row);
|
||||
var option_name = '';
|
||||
var option_remark = '';
|
||||
var option_type = 'dns_cf';
|
||||
var option_val = '';
|
||||
var option_id = 0;
|
||||
if (typeof(row) != 'undefined'){
|
||||
option_name = row['name'];
|
||||
option_remark = row['remark'];
|
||||
option_type = row['type'];
|
||||
option_val = row['val'];
|
||||
option_id = row['id'];
|
||||
|
||||
}
|
||||
|
||||
function renderDnsapiOption(name, val){
|
||||
var vlist = {};
|
||||
if (val != ''){
|
||||
var t = val.split('~');
|
||||
for (var i = 0; i < t.length; i++) {
|
||||
var kv = t[i].split('|');
|
||||
if (kv.length == 2){
|
||||
vlist[kv[0]] = kv[1];
|
||||
} else {
|
||||
vlist[kv[0]] = '';
|
||||
}
|
||||
}
|
||||
// console.log(vlist);
|
||||
}
|
||||
|
||||
|
||||
|
||||
var key = getDnsapiKey(name);
|
||||
var klist = key.split(':');
|
||||
var option_html = '';
|
||||
for (var i = 0; i < klist.length; i++) {
|
||||
var klist_val = '';
|
||||
if (klist[i] in vlist){
|
||||
klist_val = vlist[klist[i]];
|
||||
}
|
||||
option_html += "<span class='tname'>"+klist[i]+"</span>\
|
||||
<div class='info-r'>\
|
||||
<input name='"+klist[i]+"' class='bt-input-text mr5' style='width:100%;' value='"+klist_val+"' placeholder='请输入对应值' type='text'>\
|
||||
</div>";
|
||||
}
|
||||
$('#dnsapi_option').html(option_html);
|
||||
}
|
||||
|
||||
layer.open({
|
||||
type: 1,
|
||||
area: '500px',
|
||||
title: '添加DNSAPI',
|
||||
closeBtn: 1,
|
||||
shift: 5,
|
||||
shadeClose: true,
|
||||
btn:["提交","关闭"],
|
||||
content: "<form class='bt-form pd20' id='dnsapi_add'>\
|
||||
<div class='line'>\
|
||||
<span class='tname'>名称</span>\
|
||||
<div class='info-r'><input name='name' class='bt-input-text mr5' style='width:100%;' placeholder='名称' value='"+option_name+"' type='text'></div>\
|
||||
</div>\
|
||||
<div class='line'>\
|
||||
<span class='tname'>DNSAPI类型</span>\
|
||||
<div class='info-r'>\
|
||||
<select class='bt-input-text mr5' name='type'>\
|
||||
<option name='cf'>cloudflare</option>\
|
||||
</select>\
|
||||
</div>\
|
||||
</div>\
|
||||
<div class='line' id='dnsapi_option'>\
|
||||
<span class='tname'>CF_Key</span>\
|
||||
<div class='info-r'>\
|
||||
<input name='v1' class='bt-input-text mr5' style='width:100%;' placeholder='请输入对应值' type='text'>\
|
||||
</div>\
|
||||
<span class='tname'>CF_Email</span>\
|
||||
<div class='info-r'>\
|
||||
<input name='v2' class='bt-input-text mr5' style='width:100%;' placeholder='请输入对应值' type='text'>\
|
||||
</div>\
|
||||
</div>\
|
||||
<div class='line'>\
|
||||
<span class='tname'>备注</span>\
|
||||
<div class='info-r'><input name='remark' class='bt-input-text mr5' style='width:100%;' placeholder='备注' value='"+option_remark+"' type='text'></div>\
|
||||
</div>\
|
||||
<input name='id' value='"+option_id+"' type='hidden'>\
|
||||
</form>",
|
||||
success:function(){
|
||||
$("input[name='name']").keyup(function(){
|
||||
var v = $(this).val();
|
||||
$("input[name='remark']").val(v);
|
||||
});
|
||||
|
||||
var option = '';
|
||||
for (var i = 0; i<dnsapi_option.length; i++) {
|
||||
|
||||
if (dnsapi_option[i]['name'] == option_type){
|
||||
option += "<option value='"+dnsapi_option[i]['name']+"' key='"+dnsapi_option[i]['key']+"' selected>"+dnsapi_option[i]['title']+"</option>";
|
||||
} else {
|
||||
option += "<option value='"+dnsapi_option[i]['name']+"' key='"+dnsapi_option[i]['key']+"'>"+dnsapi_option[i]['title']+"</option>";
|
||||
}
|
||||
}
|
||||
|
||||
renderDnsapiOption(option_type, option_val);
|
||||
$('select[name="type"]').html(option);
|
||||
$('select[name="type"]').change(function(){
|
||||
var name = $(this).val();
|
||||
if (option_type == name){
|
||||
renderDnsapiOption(name, option_val);
|
||||
} else {
|
||||
renderDnsapiOption(name,'');
|
||||
}
|
||||
|
||||
});
|
||||
},
|
||||
yes:function(index) {
|
||||
var data = $("#dnsapi_add").serialize();
|
||||
data = decodeURIComponent(data);
|
||||
data = toArrayObject(data);
|
||||
|
||||
var key = getDnsapiKey(data['type']);
|
||||
var klist = key.split(':');
|
||||
var val = '';
|
||||
for (var i = 0; i < klist.length; i++) {
|
||||
var k = klist[i];
|
||||
if (k in data){
|
||||
if (klist.length - 1 == i){
|
||||
val += k + '|' + data[k];
|
||||
} else {
|
||||
val += k + '|' + data[k]+'~';
|
||||
}
|
||||
}
|
||||
}
|
||||
data['val'] = val;
|
||||
apaPost('dnsapi_add', data, function(data){
|
||||
var rdata = $.parseJSON(data.data);
|
||||
showMsg(rdata.msg,function(){
|
||||
if (rdata.status){
|
||||
layer.close(index);
|
||||
dnsapiList();
|
||||
}
|
||||
},{icon: rdata.status ? 1 : 2}, 2000);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
function dnsapiList(page, search){
|
||||
var _data = {};
|
||||
if (typeof(page) =='undefined'){
|
||||
var page = 1;
|
||||
}
|
||||
|
||||
_data['page'] = page;
|
||||
_data['page_size'] = 10;
|
||||
if(typeof(search) != 'undefined'){
|
||||
_data['search'] = search;
|
||||
}
|
||||
|
||||
apaPost('dnsapi_list', _data, function(data){
|
||||
var rdata = $.parseJSON(data.data);
|
||||
var list = '';
|
||||
for(i in rdata.data){
|
||||
list += '<tr>';
|
||||
list +='<td><input value="'+rdata.data[i]['id']+'" class="check" onclick="checkSelect();" type="checkbox"></td>';
|
||||
list += '<td>' + rdata.data[i]['name'] +'</td>';
|
||||
list += '<td>' + getDnsapiTitle(rdata.data[i]['type']) +'</td>';
|
||||
list += '<td>' + rdata.data[i]['val'].split('~').join("<br/>") +'</td>';
|
||||
list += '<td>' + rdata.data[i]['remark'] +'</td>';
|
||||
|
||||
list += '<td style="text-align:right">';
|
||||
list += '<a href="javascript:;" index="'+i+'" class="btlink edit" title="编辑">编辑</a> | ';
|
||||
list += '<a href="javascript:;" class="btlink" onclick="dnsapiDel(\''+rdata.data[i]['id']+'\',\''+rdata.data[i]['name']+'\')" title="删除">删除</a>';
|
||||
list += '</td></tr>';
|
||||
}
|
||||
|
||||
var con = '<div class="safe bgw">\
|
||||
<button onclick="dnsapiAdd()" title="DNSAPI" class="btn btn-success btn-sm" type="button" style="margin-right: 5px;">添加DNSAPI</button>\
|
||||
<span style="float:right"> \
|
||||
<button batch="true" style="float: right;display: none;margin-left:10px;" onclick="delDbBatch();" title="删除选中项" class="btn btn-default btn-sm">删除选中</button>\
|
||||
</span>\
|
||||
<div class="divtable mtb10">\
|
||||
<div class="tablescroll">\
|
||||
<table id="DataBody" class="table table-hover" width="100%" cellspacing="0" cellpadding="0" border="0" style="border: 0 none;">\
|
||||
<thead><tr><th width="30"><input class="check" onclick="checkSelect();" type="checkbox"></th>\
|
||||
<th>名称</th>\
|
||||
<th>类型</th>\
|
||||
<th>值</th>\
|
||||
<th>备注</th>\
|
||||
<th style="text-align:right;">操作</th></tr></thead>\
|
||||
<tbody>'+ list +'</tbody>\
|
||||
</table>\
|
||||
</div>\
|
||||
<div class="dataTables_paginate paging_bootstrap page"></div>\
|
||||
</div>\
|
||||
</div>';
|
||||
|
||||
$(".soft-man-con").html(con);
|
||||
$('.dataTables_paginate').html(rdata.page);
|
||||
|
||||
$('.edit').click(function(){
|
||||
var idx = $(this).attr('index');
|
||||
var row = rdata.data[idx];
|
||||
// console.log(row);
|
||||
dnsapiAdd(row);
|
||||
})
|
||||
|
||||
readerTableChecked();
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
function domainDel(id, name){
|
||||
safeMessage('删除['+name+']','您真的要删除['+name+']吗?',function(){
|
||||
var data='id='+id+'&name='+name;
|
||||
apaPost('domain_del', data, function(data){
|
||||
var rdata = $.parseJSON(data.data);
|
||||
showMsg(rdata.msg,function(){
|
||||
domainList();
|
||||
},{icon: rdata.status ? 1 : 2}, 600);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function domainStatusToggle(id){
|
||||
var data='id='+id;
|
||||
apaPost('domain_status_toggle', data, function(data){
|
||||
var rdata = $.parseJSON(data.data);
|
||||
showMsg(rdata.msg,function(){
|
||||
domainList();
|
||||
},{icon: rdata.status ? 1 : 2}, 600);
|
||||
});
|
||||
}
|
||||
|
||||
function domainIdCmd(id){
|
||||
var data='id='+id;
|
||||
apaPost('get_run_hook_id_cmd', data, function(data){
|
||||
var rdata = $.parseJSON(data.data);
|
||||
// console.log(rdata);
|
||||
layer.open({
|
||||
title: "手动同步命令",
|
||||
area: ['600px', '180px'],
|
||||
type:1,
|
||||
closeBtn: 1,
|
||||
shadeClose: false,
|
||||
btn:["复制","取消"],
|
||||
content: '<div class="pd15">\
|
||||
<div class="divtable">\
|
||||
<pre class="layui-code">'+rdata.data+'</pre>\
|
||||
</div>\
|
||||
</div>',
|
||||
success:function(){
|
||||
copyText(rdata.data);
|
||||
},
|
||||
yes:function(){
|
||||
copyText(rdata.data);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function domainHookCmd(){
|
||||
apaPost('run_hook_cmd', {}, function(data){
|
||||
var rdata = $.parseJSON(data.data);
|
||||
layer.open({
|
||||
title: "手动同步全部命令",
|
||||
area: ['600px', '180px'],
|
||||
type:1,
|
||||
closeBtn: 1,
|
||||
shadeClose: false,
|
||||
btn:["复制","取消"],
|
||||
content: '<div class="pd15">\
|
||||
<div class="divtable">\
|
||||
<pre class="layui-code">'+rdata.data+'</pre>\
|
||||
</div>\
|
||||
</div>',
|
||||
success:function(){
|
||||
copyText(rdata.data);
|
||||
},
|
||||
yes:function(){
|
||||
copyText(rdata.data);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function syncCfCmd(){
|
||||
apaPost('run_sync_cf_cmd', {}, function(data){
|
||||
var rdata = $.parseJSON(data.data);
|
||||
layer.open({
|
||||
title: "手动同步CloudFlare全部域名命令",
|
||||
area: ['600px', '180px'],
|
||||
type:1,
|
||||
closeBtn: 1,
|
||||
shadeClose: false,
|
||||
btn:["复制","取消"],
|
||||
content: '<div class="pd15">\
|
||||
<div class="divtable">\
|
||||
<pre class="layui-code">'+rdata.data+'</pre>\
|
||||
</div>\
|
||||
</div>',
|
||||
success:function(){
|
||||
copyText(rdata.data);
|
||||
},
|
||||
yes:function(){
|
||||
copyText(rdata.data);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function syncDnsPodCmd(){
|
||||
apaPost('run_sync_dnspod_cmd', {}, function(data){
|
||||
var rdata = $.parseJSON(data.data);
|
||||
layer.open({
|
||||
title: "手动同步DnsPod全部域名命令",
|
||||
area: ['600px', '180px'],
|
||||
type:1,
|
||||
closeBtn: 1,
|
||||
shadeClose: false,
|
||||
btn:["复制","取消"],
|
||||
content: '<div class="pd15">\
|
||||
<div class="divtable">\
|
||||
<pre class="layui-code">'+rdata.data+'</pre>\
|
||||
</div>\
|
||||
</div>',
|
||||
success:function(){
|
||||
copyText(rdata.data);
|
||||
},
|
||||
yes:function(){
|
||||
copyText(rdata.data);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function domainAdd(row){
|
||||
|
||||
var option_domian = '';
|
||||
var option_remark = '备注';
|
||||
var option_email = '';
|
||||
var option_id = 0;
|
||||
var option_dnsapi_id = 0;
|
||||
if (typeof(row) != 'undefined'){
|
||||
option_domian = row['domain'];
|
||||
option_remark = row['remark'];
|
||||
option_email = row['email'];
|
||||
option_id = row['id'];
|
||||
option_dnsapi_id = row['dnsapi_id'];
|
||||
}
|
||||
|
||||
layer.open({
|
||||
type: 1,
|
||||
area: '500px',
|
||||
title: '添加顶级域名',
|
||||
closeBtn: 1,
|
||||
shift: 5,
|
||||
shadeClose: true,
|
||||
btn:["提交","关闭"],
|
||||
content: "<form class='bt-form pd20' id='domain_add'>\
|
||||
<div class='line'>\
|
||||
<span class='tname'>域名</span>\
|
||||
<div class='info-r'><input name='domain' class='bt-input-text mr5' style='width:100%;' value='"+option_domian+"' placeholder='域名' type='text'></div>\
|
||||
</div>\
|
||||
<div class='line'>\
|
||||
<span class='tname'>DNSAPI</span>\
|
||||
<div class='info-r'>\
|
||||
<select class='bt-input-text mr5' name='dnsapi_id'>\
|
||||
<option name='0'>无设置</option>\
|
||||
</select>\
|
||||
</div>\
|
||||
</div>\
|
||||
<div class='line'>\
|
||||
<span class='tname'>邮件</span>\
|
||||
<div class='info-r'><input name='email' class='bt-input-text mr5' style='width:100%;' value='"+option_email+"' placeholder='邮件' type='text'></div>\
|
||||
</div>\
|
||||
<div class='line'>\
|
||||
<span class='tname'>备注</span>\
|
||||
<div class='info-r'><input name='remark' class='bt-input-text mr5' style='width:100%;' value='"+option_remark+"' placeholder='备注' type='text'></div>\
|
||||
</div>\
|
||||
<input name='id' value='"+option_id+"' type='hidden'>\
|
||||
</form>",
|
||||
success:function(){
|
||||
// $("input[name='domain']").keyup(function(){
|
||||
// var v = $(this).val();
|
||||
// $("input[name='remark']").val(v);
|
||||
// });
|
||||
|
||||
var dnsapi_id_html = "<option value='0'>无设置</option>";
|
||||
apaPostN('dnsapi_list_all', {}, function(data){
|
||||
var rdata = $.parseJSON(data.data);
|
||||
for (var i = 0; i < rdata.length; i++) {
|
||||
if (option_dnsapi_id == rdata[i]['id']){
|
||||
dnsapi_id_html += "<option value='"+rdata[i]['id']+"' val='"+rdata[i]['val']+"' selected>"+rdata[i]['name']+"</option>";
|
||||
} else {
|
||||
dnsapi_id_html += "<option value='"+rdata[i]['id']+"' val='"+rdata[i]['val']+"'>"+rdata[i]['name']+"</option>";
|
||||
}
|
||||
}
|
||||
|
||||
$('select[name="dnsapi_id"]').change(function(){
|
||||
var val = $('select[name="dnsapi_id"]').find("option:selected").attr('val');
|
||||
var val_arr = val.split("|");
|
||||
for (var i = 0; i < val_arr.length; i++) {
|
||||
var param = val_arr[i];
|
||||
var strictEmailRegex = /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
|
||||
if(strictEmailRegex.test(param)){
|
||||
$('input[name="email"]').val(param);
|
||||
}
|
||||
}
|
||||
|
||||
});
|
||||
$('select[name="dnsapi_id"]').html(dnsapi_id_html);
|
||||
});
|
||||
},
|
||||
yes:function(index) {
|
||||
var data = $("#domain_add").serialize();
|
||||
data = decodeURIComponent(data);
|
||||
apaPost('domain_add', data, function(data){
|
||||
var rdata = $.parseJSON(data.data);
|
||||
showMsg(rdata.msg,function(){
|
||||
if (rdata.status){
|
||||
layer.close(index);
|
||||
domainList();
|
||||
}
|
||||
},{icon: rdata.status ? 1 : 2}, 2000);
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function domainList(page, search){
|
||||
var _data = {};
|
||||
if (typeof(page) =='undefined'){
|
||||
var page = 1;
|
||||
}
|
||||
|
||||
_data['page'] = page;
|
||||
_data['page_size'] = 10;
|
||||
if(typeof(search) != 'undefined'){
|
||||
_data['search'] = search;
|
||||
}
|
||||
|
||||
apaPost('domain_list', _data, function(data){
|
||||
var rdata = $.parseJSON(data.data);
|
||||
// console.log(rdata);
|
||||
var list = '';
|
||||
for(i in rdata.data){
|
||||
list += '<tr>';
|
||||
list +='<td><input value="'+rdata.data[i]['id']+'" class="check" onclick="checkSelect();" type="checkbox"></td>';
|
||||
list += '<td>' + rdata.data[i]['domain'] +'</td>';
|
||||
list += '<td>' + rdata.data[i]['dnsapi_id_alias'] +'</td>';
|
||||
list += '<td>' + rdata.data[i]['email'] +'</td>';
|
||||
list += '<td>' + rdata.data[i]['remark'] +'</td>';
|
||||
|
||||
|
||||
|
||||
if (rdata.data[i]['effective_date'] == ''){
|
||||
list += '<td>空/未申请</td>';
|
||||
} else {
|
||||
list += '<td>'+getFormatTime(rdata.data[i]['effective_date'],'yyyy/MM/dd')+'</td>';
|
||||
}
|
||||
|
||||
if (rdata.data[i]['expiration_date'] == ''){
|
||||
list += '<td>空/未申请</td>';
|
||||
} else {
|
||||
list += '<td>'+getFormatTime(rdata.data[i]['expiration_date'],'yyyy/MM/dd')+'</td>';
|
||||
}
|
||||
|
||||
if (rdata.data[i]['status'] == '0'){
|
||||
list += '<td><a href="javascript:;" index="'+i+'" class="btlink status">否</a></td>';
|
||||
} else {
|
||||
list += '<td><a href="javascript:;" index="'+i+'" class="btlink status">是</a></td>';
|
||||
}
|
||||
|
||||
list += '<td style="text-align:right">';
|
||||
list += '<a href="javascript:;" index="'+i+'" class="btlink cmd" title="命令">命令</a> | ';
|
||||
list += '<a href="javascript:;" index="'+i+'" class="btlink edit" title="编辑">编辑</a> | ';
|
||||
list += '<a href="javascript:;" class="btlink" onclick="domainDel(\''+rdata.data[i]['id']+'\',\''+rdata.data[i]['domain']+'\')" title="删除">删除</a>';
|
||||
list += '</td></tr>';
|
||||
}
|
||||
|
||||
var con = '<div class="safe bgw">\
|
||||
<button onclick="domainAdd()" title="添加顶级域名" class="btn btn-success btn-sm" type="button" style="margin-right: 5px;">添加域名</button>\
|
||||
<button onclick="domainHookCmd()" title="全部同步命令" class="btn btn-success btn-sm" type="button" style="margin-right: 5px;">全部同步命令</button>\
|
||||
<button onclick="syncCfCmd()" title="cloudflare同步命令" class="btn btn-success btn-sm" type="button" style="margin-right: 5px;">cloudflare同步命令</button>\
|
||||
<button onclick="syncDnsPodCmd()" title="DnsPod国际同步命令" class="btn btn-success btn-sm" type="button" style="margin-right: 5px;">DnsPod国际同步命令</button>\
|
||||
<div class="divtable mtb10">\
|
||||
<div class="tablescroll">\
|
||||
<table id="DataBody" class="table table-hover" width="100%" cellspacing="0" cellpadding="0" border="0" style="border: 0 none;">\
|
||||
<thead><tr><th width="30"><input class="check" onclick="checkSelect();" type="checkbox"></th>\
|
||||
<th>域名</th>\
|
||||
<th>DNSAPI</th>\
|
||||
<th>邮件</th>\
|
||||
<th>备注</th>\
|
||||
<th>开始</th>\
|
||||
<th>结束</th>\
|
||||
<th>同步</th>\
|
||||
<th style="text-align:right;">操作</th></tr></thead>\
|
||||
<tbody>'+ list +'</tbody>\
|
||||
</table>\
|
||||
</div>\
|
||||
<div class="dataTables_paginate paging_bootstrap page"></div>\
|
||||
</div>\
|
||||
</div>';
|
||||
|
||||
$(".soft-man-con").html(con);
|
||||
$('.dataTables_paginate').html(rdata.page);
|
||||
|
||||
$('.edit').click(function(){
|
||||
var idx = $(this).attr('index');
|
||||
var row = rdata.data[idx];
|
||||
domainAdd(row);
|
||||
});
|
||||
|
||||
$('.status').click(function(){
|
||||
var idx = $(this).attr('index');
|
||||
var row = rdata.data[idx];
|
||||
domainStatusToggle(row['id']);
|
||||
});
|
||||
|
||||
$('.cmd').click(function(){
|
||||
var idx = $(this).attr('index');
|
||||
var row = rdata.data[idx];
|
||||
domainIdCmd(row['id']);
|
||||
});
|
||||
|
||||
readerTableChecked();
|
||||
});
|
||||
}
|
||||
@@ -1,142 +0,0 @@
|
||||
# coding:utf-8
|
||||
|
||||
import sys
|
||||
import io
|
||||
import os
|
||||
import time
|
||||
import json
|
||||
|
||||
# print(sys.platform)
|
||||
if sys.platform != "darwin":
|
||||
os.chdir("/www/server/mdserver-web")
|
||||
|
||||
|
||||
web_dir = os.getcwd() + "/web"
|
||||
if os.path.exists(web_dir):
|
||||
sys.path.append(web_dir)
|
||||
os.chdir(web_dir)
|
||||
|
||||
import core.mw as mw
|
||||
from utils.crontab import crontab as MwCrontab
|
||||
|
||||
|
||||
app_debug = False
|
||||
if mw.isAppleSystem():
|
||||
app_debug = True
|
||||
|
||||
|
||||
def getPluginName():
|
||||
return 'acme_pandominassl_apply'
|
||||
|
||||
|
||||
def getPluginDir():
|
||||
return mw.getPluginDir() + '/' + getPluginName()
|
||||
|
||||
|
||||
def getServerDir():
|
||||
return mw.getServerDir() + '/' + getPluginName()
|
||||
|
||||
|
||||
def getTaskConf():
|
||||
conf = getServerDir() + "/cron_config.json"
|
||||
return conf
|
||||
|
||||
def getTaskDeltaConf():
|
||||
conf = getServerDir() + "/cron_delta_config.json"
|
||||
return conf
|
||||
|
||||
|
||||
def getConfigData():
|
||||
try:
|
||||
return json.loads(mw.readFile(getTaskConf()))
|
||||
except:
|
||||
pass
|
||||
return {
|
||||
"task_id": -1,
|
||||
"period": "day-n",
|
||||
"where1": "1",
|
||||
"hour": "0",
|
||||
"minute": "15",
|
||||
}
|
||||
|
||||
|
||||
def createBgTask():
|
||||
removeBgTask()
|
||||
createBgTaskByName(getPluginName())
|
||||
return True
|
||||
|
||||
|
||||
def createBgTaskByName(name):
|
||||
args = getConfigData()
|
||||
_name = "[勿删]ACME泛域名SSL[APA]"
|
||||
res = mw.M("crontab").field("id, name").where("name=?", (_name,)).find()
|
||||
if res:
|
||||
return True
|
||||
|
||||
if "task_id" in args and args["task_id"] > 0:
|
||||
res = mw.M("crontab").field("id, name").where(
|
||||
"id=?", (args["task_id"],)).find()
|
||||
if res and res["id"] == args["task_id"]:
|
||||
print("计划任务已经存在!")
|
||||
return True
|
||||
|
||||
mw_dir = mw.getPanelDir()
|
||||
cmd = '''
|
||||
mw_dir=%s
|
||||
rname=%s
|
||||
plugin_path=%s
|
||||
script_path=%s
|
||||
logs_file=$plugin_path/${rname}.log
|
||||
''' % (mw_dir, name, getServerDir(), getPluginDir())
|
||||
cmd += 'echo "★【`date +"%Y-%m-%d %H:%M:%S"`】 STSRT★" >> $logs_file' + "\n"
|
||||
cmd += 'echo ">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>" >> $logs_file' + "\n"
|
||||
cmd += 'echo "python3 $script_path/index.py run_hook"' + "\n"
|
||||
cmd += 'cd $mw_dir && python3 $script_path/index.py run_hook' + "\n"
|
||||
cmd += 'echo "【`date +"%Y-%m-%d %H:%M:%S"`】 END★" >> $logs_file' + "\n"
|
||||
cmd += 'echo "<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<" >> $logs_file' + "\n"
|
||||
|
||||
params = {
|
||||
'name': _name,
|
||||
'type': args['period'],
|
||||
'week': "",
|
||||
'where1': args['where1'],
|
||||
'hour': args['hour'],
|
||||
'minute': args['minute'],
|
||||
'save': "",
|
||||
'backup_to': "",
|
||||
'stype': "toShell",
|
||||
'sname': '',
|
||||
'sbody': cmd,
|
||||
'url_address': '',
|
||||
}
|
||||
|
||||
task_id = MwCrontab.instance().add(params)
|
||||
if task_id > 0:
|
||||
args["task_id"] = task_id
|
||||
args["name"] = name
|
||||
mw.writeFile(getTaskConf(), json.dumps(args))
|
||||
|
||||
|
||||
def removeBgTask():
|
||||
cfg = getConfigData()
|
||||
if "task_id" in cfg and cfg["task_id"] > 0:
|
||||
res = mw.M("crontab").field("id, name").where(
|
||||
"id=?", (cfg["task_id"],)).find()
|
||||
if res and res["id"] == cfg["task_id"]:
|
||||
import crontab_api
|
||||
api = crontab_api.crontab_api()
|
||||
data = api.delete(cfg["task_id"])
|
||||
if data[0]:
|
||||
cfg["task_id"] = -1
|
||||
mw.writeFile(getTaskConf(), json.dumps(cfg))
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
if len(sys.argv) > 1:
|
||||
action = sys.argv[1]
|
||||
if action == "remove":
|
||||
removeBgTask()
|
||||
elif action == "add":
|
||||
createBgTask()
|
||||
|
Before Width: | Height: | Size: 42 KiB |
@@ -1,31 +0,0 @@
|
||||
<style>
|
||||
.overflow_hide {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
vertical-align: middle;
|
||||
}
|
||||
</style>
|
||||
|
||||
<div class="bt-form">
|
||||
<div class='plugin_version'></div>
|
||||
<div class="bt-w-main">
|
||||
<div class="bt-w-menu">
|
||||
<p class="bgw" onclick="pluginService('alist');">服务</p>
|
||||
<p onclick="pluginInitD('alist');">自启动</p>
|
||||
<p onclick="alistCommonFunc();">常用功能</p>
|
||||
<p onclick="pluginConfigTpl('alist',$('.plugin_version').attr('version'));">配置修改</p>
|
||||
<p onclick="pluginLogs('alist','','run_log');">运行日志</p>
|
||||
<p onclick="alistReadme();">相关说明</p>
|
||||
|
||||
</div>
|
||||
<div class="bt-w-con pd15">
|
||||
<div class="soft-man-con" style="height: 520px; overflow: auto;"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script type="text/javascript">
|
||||
$.getScript( "/plugins/file?name=alist&f=js/alist.js", function(){
|
||||
pluginService('alist', $('.plugin_version').attr('version'));
|
||||
});
|
||||
</script>
|
||||
@@ -1,319 +0,0 @@
|
||||
# coding:utf-8
|
||||
|
||||
import sys
|
||||
import io
|
||||
import os
|
||||
import time
|
||||
import re
|
||||
import json
|
||||
|
||||
web_dir = os.getcwd() + "/web"
|
||||
if os.path.exists(web_dir):
|
||||
sys.path.append(web_dir)
|
||||
os.chdir(web_dir)
|
||||
|
||||
import core.mw as mw
|
||||
|
||||
app_debug = False
|
||||
if mw.isAppleSystem():
|
||||
app_debug = True
|
||||
|
||||
|
||||
def getPluginName():
|
||||
return 'alist'
|
||||
|
||||
|
||||
def getPluginDir():
|
||||
return mw.getPluginDir() + '/' + getPluginName()
|
||||
|
||||
|
||||
def getServerDir():
|
||||
return mw.getServerDir() + '/' + getPluginName()
|
||||
|
||||
|
||||
def getInitDFile():
|
||||
current_os = mw.getOs()
|
||||
if current_os == 'darwin':
|
||||
return '/tmp/' + getPluginName()
|
||||
|
||||
if current_os.startswith('freebsd'):
|
||||
return '/etc/rc.d/' + getPluginName()
|
||||
|
||||
return '/etc/init.d/' + getPluginName()
|
||||
|
||||
|
||||
def getInitDTpl():
|
||||
path = getPluginDir() + "/init.d/" + getPluginName() + ".tpl"
|
||||
return path
|
||||
|
||||
|
||||
def getArgs():
|
||||
args = sys.argv[3:]
|
||||
tmp = {}
|
||||
args_len = len(args)
|
||||
|
||||
if args_len == 1:
|
||||
t = args[0].strip('{').strip('}')
|
||||
if t.strip() == '':
|
||||
tmp = []
|
||||
else:
|
||||
t = t.split(':')
|
||||
tmp[t[0]] = t[1]
|
||||
tmp[t[0]] = t[1]
|
||||
elif args_len > 1:
|
||||
for i in range(len(args)):
|
||||
t = args[i].split(':')
|
||||
tmp[t[0]] = t[1]
|
||||
return tmp
|
||||
|
||||
def checkArgs(data, ck=[]):
|
||||
for i in range(len(ck)):
|
||||
if not ck[i] in data:
|
||||
return (False, mw.returnJson(False, '参数:(' + ck[i] + ')没有!'))
|
||||
return (True, mw.returnJson(True, 'ok'))
|
||||
|
||||
|
||||
def getConf():
|
||||
path = getServerDir() + "/data/config.json"
|
||||
return path
|
||||
|
||||
def configTpl():
|
||||
path = getPluginDir() + '/tpl'
|
||||
pathFile = os.listdir(path)
|
||||
tmp = []
|
||||
for one in pathFile:
|
||||
file = path + '/' + one
|
||||
tmp.append(file)
|
||||
return mw.getJson(tmp)
|
||||
|
||||
|
||||
def readConfigTpl():
|
||||
args = getArgs()
|
||||
data = checkArgs(args, ['file'])
|
||||
if not data[0]:
|
||||
return data[1]
|
||||
|
||||
content = mw.readFile(args['file'])
|
||||
content = contentReplace(content)
|
||||
return mw.returnJson(True, 'ok', content)
|
||||
|
||||
def __release_port(port, ps = '开启端口'):
|
||||
try:
|
||||
from utils.firewall import Firewall as MwFirewall
|
||||
MwFirewall.instance().addAcceptPort(port, ps, 'port')
|
||||
return port
|
||||
except Exception as e:
|
||||
return "Release failed {}".format(e)
|
||||
|
||||
def __delete_port(port):
|
||||
try:
|
||||
from utils.firewall import Firewall as MwFirewall
|
||||
MwFirewall.instance().delAcceptPortCmd(port, 'tcp')
|
||||
return port
|
||||
except Exception as e:
|
||||
return "Delete failed {}".format(e)
|
||||
|
||||
|
||||
def openPort():
|
||||
content = mw.readFile(getConf())
|
||||
data = json.loads(content)
|
||||
http_port = data['scheme']['http_port']
|
||||
__release_port(http_port, 'alist')
|
||||
return True
|
||||
|
||||
def status():
|
||||
cmd = "ps -ef|grep alist |grep -v grep | grep -v python | grep -v mdserver-web | awk '{print $2}'"
|
||||
data = mw.execShell(cmd)
|
||||
if data[0] == '':
|
||||
return 'stop'
|
||||
return 'start'
|
||||
|
||||
def contentReplace(content):
|
||||
service_path = mw.getServerDir()
|
||||
content = content.replace('{$ROOT_PATH}', mw.getFatherDir())
|
||||
content = content.replace('{$SERVER_PATH}', service_path)
|
||||
content = content.replace('{$SERVER_APP}', service_path + '/'+getPluginName())
|
||||
return content
|
||||
|
||||
def initDreplace():
|
||||
|
||||
file_tpl = getInitDTpl()
|
||||
service_path = mw.getServerDir()
|
||||
|
||||
initD_path = getServerDir() + '/init.d'
|
||||
if not os.path.exists(initD_path):
|
||||
os.mkdir(initD_path)
|
||||
file_bin = initD_path + '/' + getPluginName()
|
||||
|
||||
# initd replace
|
||||
if not os.path.exists(file_bin):
|
||||
content = mw.readFile(file_tpl)
|
||||
content = content.replace('{$SERVER_PATH}', service_path)
|
||||
mw.writeFile(file_bin, content)
|
||||
mw.execShell('chmod +x ' + file_bin)
|
||||
|
||||
|
||||
# systemd
|
||||
systemDir = mw.systemdCfgDir()
|
||||
systemService = systemDir + '/' + getPluginName() + '.service'
|
||||
if os.path.exists(systemDir) and not os.path.exists(systemService):
|
||||
systemServiceTpl = getPluginDir() + '/init.d/' + getPluginName() + '.service.tpl'
|
||||
service_path = mw.getServerDir()
|
||||
se_content = mw.readFile(systemServiceTpl)
|
||||
se_content = se_content.replace('{$SERVER_PATH}', service_path)
|
||||
mw.writeFile(systemService, se_content)
|
||||
mw.execShell('systemctl daemon-reload')
|
||||
time.sleep(1)
|
||||
return file_bin
|
||||
|
||||
|
||||
def alistOp(method):
|
||||
file = initDreplace()
|
||||
|
||||
current_os = mw.getOs()
|
||||
if current_os == "darwin":
|
||||
data = mw.execShell(file + ' ' + method)
|
||||
if data[1] == '':
|
||||
return 'ok'
|
||||
return data[1]
|
||||
|
||||
if current_os.startswith("freebsd"):
|
||||
data = mw.execShell('service ' + getPluginName() + ' ' + method)
|
||||
if data[1] == '':
|
||||
return 'ok'
|
||||
return data[1]
|
||||
|
||||
data = mw.execShell('systemctl ' + method + ' ' + getPluginName())
|
||||
if data[1] == '':
|
||||
return 'ok'
|
||||
return data[1]
|
||||
|
||||
|
||||
def start():
|
||||
return alistOp('start')
|
||||
|
||||
|
||||
def stop():
|
||||
return alistOp('stop')
|
||||
|
||||
|
||||
def restart():
|
||||
status = alistOp('restart')
|
||||
return status
|
||||
|
||||
|
||||
def reload():
|
||||
return alistOp('reload')
|
||||
|
||||
|
||||
def initdStatus():
|
||||
current_os = mw.getOs()
|
||||
if current_os == 'darwin':
|
||||
return "Apple Computer does not support"
|
||||
|
||||
if current_os.startswith('freebsd'):
|
||||
initd_bin = getInitDFile()
|
||||
if os.path.exists(initd_bin):
|
||||
return 'ok'
|
||||
|
||||
shell_cmd = 'systemctl status ' + \
|
||||
getPluginName() + ' | grep loaded | grep "enabled;"'
|
||||
data = mw.execShell(shell_cmd)
|
||||
if data[0] == '':
|
||||
return 'fail'
|
||||
return 'ok'
|
||||
|
||||
|
||||
def initdInstall():
|
||||
current_os = mw.getOs()
|
||||
if current_os == 'darwin':
|
||||
return "Apple Computer does not support"
|
||||
|
||||
# freebsd initd install
|
||||
if current_os.startswith('freebsd'):
|
||||
import shutil
|
||||
source_bin = initDreplace()
|
||||
initd_bin = getInitDFile()
|
||||
shutil.copyfile(source_bin, initd_bin)
|
||||
mw.execShell('chmod +x ' + initd_bin)
|
||||
mw.execShell('sysrc ' + getPluginName() + '_enable="YES"')
|
||||
return 'ok'
|
||||
|
||||
mw.execShell('systemctl enable ' + getPluginName())
|
||||
return 'ok'
|
||||
|
||||
|
||||
def initdUinstall():
|
||||
current_os = mw.getOs()
|
||||
if current_os == 'darwin':
|
||||
return "Apple Computer does not support"
|
||||
|
||||
if current_os.startswith('freebsd'):
|
||||
initd_bin = getInitDFile()
|
||||
os.remove(initd_bin)
|
||||
mw.execShell('sysrc ' + getPluginName() + '_enable="NO"')
|
||||
return 'ok'
|
||||
|
||||
mw.execShell('systemctl disable ' + getPluginName())
|
||||
return 'ok'
|
||||
|
||||
|
||||
def runLog():
|
||||
return getServerDir() + '/data/log/log.log'
|
||||
|
||||
def pSqliteDb(dbname='databases'):
|
||||
pos_file = getServerDir() + '/data/'
|
||||
file = pos_file + '/data.db'
|
||||
name = 'data'
|
||||
conn = mw.M(dbname).dbPos(pos_file, name)
|
||||
return conn
|
||||
|
||||
def clearCopyTask():
|
||||
conn = pSqliteDb('x_task_items')
|
||||
conn.where('key=?', ('copy',)).setField('persist_data','[]')
|
||||
restart()
|
||||
return mw.returnJson(True, '清空成功并重启服务!')
|
||||
|
||||
def homePage():
|
||||
content = mw.readFile(getConf())
|
||||
data = json.loads(content)
|
||||
http_port = data['scheme']['http_port']
|
||||
ip = mw.getLocalIp()
|
||||
if mw.isAppleSystem():
|
||||
ip = '127.0.0.1'
|
||||
url = 'http://'+ip+":"+str(http_port)
|
||||
# print(url)
|
||||
return mw.returnJson(True, 'ok!', url)
|
||||
|
||||
if __name__ == "__main__":
|
||||
func = sys.argv[1]
|
||||
if func == 'status':
|
||||
print(status())
|
||||
elif func == 'start':
|
||||
print(start())
|
||||
elif func == 'stop':
|
||||
print(stop())
|
||||
elif func == 'restart':
|
||||
print(restart())
|
||||
elif func == 'reload':
|
||||
print(reload())
|
||||
elif func == 'initd_status':
|
||||
print(initdStatus())
|
||||
elif func == 'initd_install':
|
||||
print(initdInstall())
|
||||
elif func == 'initd_uninstall':
|
||||
print(initdUinstall())
|
||||
elif func == 'conf':
|
||||
print(getConf())
|
||||
elif func == 'run_log':
|
||||
print(runLog())
|
||||
elif func == 'config_tpl':
|
||||
print(configTpl())
|
||||
elif func == 'read_config_tpl':
|
||||
print(readConfigTpl())
|
||||
elif func == 'clear_copy_task':
|
||||
print(clearCopyTask())
|
||||
elif func == 'home_page':
|
||||
print(homePage())
|
||||
else:
|
||||
print('error')
|
||||
@@ -1,17 +0,0 @@
|
||||
{
|
||||
"sort": 7,
|
||||
"ps": "一个支持多种存储的文件列表程序",
|
||||
"name": "alist",
|
||||
"title": "Alist",
|
||||
"shell": "install.sh",
|
||||
"versions":["3.37.4","3.38.0","3.45.0"],
|
||||
"tip": "soft",
|
||||
"checks": "server/alist",
|
||||
"path": "server/alist",
|
||||
"display": 1,
|
||||
"author": "alist",
|
||||
"date": "2022-10-09",
|
||||
"home": "https://alist.nn.ci",
|
||||
"type": 0,
|
||||
"pid": "5"
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
[Unit]
|
||||
Description=A file list program that supports multiple storage
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
WorkingDirectory={$SERVER_PATH}/alist
|
||||
ExecStart={$SERVER_PATH}/alist/alist server
|
||||
ExecReload=/bin/kill -USR2 $MAINPID
|
||||
Restart=on-failure
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
@@ -1,46 +0,0 @@
|
||||
#!/bin/sh
|
||||
# chkconfig: 2345 55 25
|
||||
# description: alist Service
|
||||
|
||||
### BEGIN INIT INFO
|
||||
# Provides: alist
|
||||
# Required-Start: $all
|
||||
# Required-Stop: $all
|
||||
# Default-Start: 2 3 4 5
|
||||
# Default-Stop: 0 1 6
|
||||
# Short-Description: starts alist
|
||||
# Description: starts the MDW-Web
|
||||
### END INIT INFO
|
||||
|
||||
# Simple alist init.d script conceived to work on Linux systems
|
||||
# as it does use of the /proc filesystem.
|
||||
|
||||
app_start(){
|
||||
echo "Starting alist server..."
|
||||
cd {$SERVER_PATH}/alist
|
||||
./alist server >> {$SERVER_PATH}/alist/logs.pl 2>&1 &
|
||||
}
|
||||
|
||||
app_stop(){
|
||||
echo "dztasks stopped"
|
||||
ps -ef| grep alist | grep -v grep | grep -v python | grep -v sh | awk '{print $2}'| xargs kill -9
|
||||
}
|
||||
|
||||
|
||||
case "$1" in
|
||||
start)
|
||||
app_start
|
||||
;;
|
||||
stop)
|
||||
app_stop
|
||||
;;
|
||||
restart|reload)
|
||||
app_stop
|
||||
sleep 0.3
|
||||
app_start
|
||||
;;
|
||||
*)
|
||||
echo "Please use start or stop as first argument"
|
||||
;;
|
||||
esac
|
||||
|
||||
@@ -1,93 +0,0 @@
|
||||
#!/bin/bash
|
||||
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin:/opt/homebrew/bin
|
||||
export PATH
|
||||
|
||||
curPath=`pwd`
|
||||
rootPath=$(dirname "$curPath")
|
||||
rootPath=$(dirname "$rootPath")
|
||||
serverPath=$(dirname "$rootPath")
|
||||
|
||||
# cd /Users/midoks/Desktop/mwdev/server/mdserver-web/plugins/alist && bash install.sh install 3.37.4
|
||||
# cd /www/server/mdserver-web/plugins/alist && bash install.sh install 3.45.0
|
||||
|
||||
VERSION=$2
|
||||
|
||||
sysArch=`arch`
|
||||
sysName=`uname`
|
||||
|
||||
ALIST_ARCH_NAME=amd64
|
||||
if [ "$sysArch" = "arm64" ];then
|
||||
ALIST_ARCH_NAME=arm64
|
||||
elif [ "$sysArch" = "x86_64" ]; then
|
||||
ALIST_ARCH_NAME=amd64
|
||||
elif [ "$sysArch" = "aarch64" ]; then
|
||||
ALIST_ARCH_NAME=arm64
|
||||
fi
|
||||
|
||||
ALIST_NAME=linux
|
||||
if [ "$sysName" = "Darwin" ];then
|
||||
ALIST_NAME=darwin
|
||||
fi
|
||||
|
||||
Install_App()
|
||||
{
|
||||
echo '正在安装脚本文件...'
|
||||
|
||||
mkdir -p $serverPath/source
|
||||
mkdir -p $serverPath/source/alist
|
||||
|
||||
FILE_TGZ=alist-${ALIST_NAME}-${ALIST_ARCH_NAME}.tar.gz
|
||||
|
||||
ALIST_DIR=$serverPath/source/alist
|
||||
|
||||
if [ ! -f $ALIST_DIR/${FILE_TGZ} ];then
|
||||
wget --no-check-certificate -O $ALIST_DIR/${FILE_TGZ} https://github.com/alist-org/alist/releases/download/v${VERSION}/${FILE_TGZ}
|
||||
fi
|
||||
|
||||
mkdir -p $serverPath/alist
|
||||
|
||||
cd $ALIST_DIR && tar -zxvf ${FILE_TGZ} -C $serverPath/alist
|
||||
echo "${VERSION}" > $serverPath/alist/version.pl
|
||||
|
||||
cd ${rootPath} && python3 ${rootPath}/plugins/alist/index.py start
|
||||
cd ${rootPath} && python3 ${rootPath}/plugins/alist/index.py initd_install
|
||||
|
||||
cd $serverPath/alist && ./alist admin set admin
|
||||
# cd /www/server/alist && ./alist admin set admin
|
||||
|
||||
echo '安装完成'
|
||||
}
|
||||
|
||||
Uninstall_App()
|
||||
{
|
||||
if [ -f /usr/lib/systemd/system/alist.service ];then
|
||||
systemctl stop alist
|
||||
systemctl disable alist
|
||||
rm -rf /usr/lib/systemd/system/alist.service
|
||||
systemctl daemon-reload
|
||||
fi
|
||||
|
||||
if [ -f /lib/systemd/system/alist.service ];then
|
||||
systemctl stop alist
|
||||
systemctl disable alist
|
||||
rm -rf /lib/systemd/system/alist.service
|
||||
systemctl daemon-reload
|
||||
fi
|
||||
|
||||
if [ -f $serverPath/alist/initd/alist ];then
|
||||
$serverPath/alist/initd/alist stop
|
||||
fi
|
||||
|
||||
if [ -d $serverPath/alist ];then
|
||||
rm -rf $serverPath/alist
|
||||
fi
|
||||
|
||||
echo "卸载alist成功"
|
||||
}
|
||||
|
||||
action=$1
|
||||
if [ "${1}" = 'install' ];then
|
||||
Install_App
|
||||
else
|
||||
Uninstall_App
|
||||
fi
|
||||
@@ -1,93 +0,0 @@
|
||||
function alistPost(method, version, args,callback){
|
||||
var loadT = layer.msg('正在获取...', { icon: 16, time: 0, shade: 0.3 });
|
||||
|
||||
var req_data = {};
|
||||
req_data['name'] = 'alist';
|
||||
req_data['func'] = method;
|
||||
req_data['version'] = version;
|
||||
|
||||
if (typeof(args) == 'string'){
|
||||
req_data['args'] = JSON.stringify(toArrayObject(args));
|
||||
} else {
|
||||
req_data['args'] = JSON.stringify(args);
|
||||
}
|
||||
|
||||
$.post('/plugins/run', req_data, function(data) {
|
||||
layer.close(loadT);
|
||||
if (!data.status){
|
||||
//错误展示10S
|
||||
layer.msg(data.msg,{icon:0,time:2000,shade: [10, '#000']});
|
||||
return;
|
||||
}
|
||||
|
||||
if(typeof(callback) == 'function'){
|
||||
callback(data);
|
||||
}
|
||||
},'json');
|
||||
}
|
||||
|
||||
function alistPostCallbak(method, version, args,callback){
|
||||
var loadT = layer.msg('正在获取...', { icon: 16, time: 0, shade: 0.3 });
|
||||
|
||||
var req_data = {};
|
||||
req_data['name'] = 'alist';
|
||||
req_data['func'] = method;
|
||||
args['version'] = version;
|
||||
|
||||
if (typeof(args) == 'string'){
|
||||
req_data['args'] = JSON.stringify(toArrayObject(args));
|
||||
} else {
|
||||
req_data['args'] = JSON.stringify(args);
|
||||
}
|
||||
|
||||
$.post('/plugins/callback', req_data, function(data) {
|
||||
layer.close(loadT);
|
||||
if (!data.status){
|
||||
layer.msg(data.msg,{icon:0,time:2000,shade: [0.3, '#000']});
|
||||
return;
|
||||
}
|
||||
|
||||
if(typeof(callback) == 'function'){
|
||||
callback(data);
|
||||
}
|
||||
},'json');
|
||||
}
|
||||
|
||||
function clearTaskCopy(){
|
||||
layer.confirm('您真的要清空复制任务吗?', { icon: 3, closeBtn: 2 }, function() {
|
||||
alistPost('clear_copy_task', '', {}, function(data){
|
||||
var rdata = $.parseJSON(data.data);
|
||||
showMsg(rdata.msg, function(){},{ icon: rdata.status ? 1 : 2 });
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function commonHomePage(){
|
||||
alistPost('home_page', '', {}, function(data){
|
||||
var rdata = $.parseJSON(data.data);
|
||||
window.open(rdata.data);
|
||||
});
|
||||
}
|
||||
|
||||
function alistCommonFunc(){
|
||||
var con = '';
|
||||
con += '<hr/><p class="conf_p" style="text-align:center;">\
|
||||
<button class="btn btn-default btn-sm" onclick="commonHomePage()">主页</button>\
|
||||
<button class="btn btn-default btn-sm" onclick="clearTaskCopy()">清空复制任务</button>\
|
||||
</p>';
|
||||
|
||||
$(".soft-man-con").html(con);
|
||||
}
|
||||
|
||||
function alistReadme(){
|
||||
|
||||
|
||||
var readme = '<ul class="help-info-text c7">';
|
||||
readme += '<li>手动开启默认端口:5244</li>';
|
||||
readme += '<li>默认admin:admin</li>';
|
||||
readme += '<li>手动改密码: cd /www/server/alist && ./alist admin set NEW_PASSWORD</li>';
|
||||
readme += '</ul>';
|
||||
|
||||
$('.soft-man-con').html(readme);
|
||||
}
|
||||
|
||||
@@ -1,35 +0,0 @@
|
||||
#!/bin/bash
|
||||
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin:/opt/homebrew/bin
|
||||
export PATH
|
||||
|
||||
# OpenResty服务名称
|
||||
service_name="openresty"
|
||||
|
||||
# 检查OpenResty是否正在运行
|
||||
if systemctl is-active --quiet "$service_name"; then
|
||||
# 检查是否存在僵尸进程
|
||||
zombie_processes=$(ps -ef | grep -i openresty | grep -v grep | awk '{print $2}' | xargs ps -o state= -p 2>/dev/null | grep -c Z)
|
||||
if [ "$zombie_processes" -gt 0 ]; then
|
||||
echo "kill nginx 僵尸进程"
|
||||
ps -ef|grep nginx| grep -v grep| awk '{print $2}' | xargs kill -9
|
||||
echo "检测到OpenResty僵尸进程,正在重启服务..."
|
||||
systemctl restart "$service_name"
|
||||
echo "服务已重启"
|
||||
else
|
||||
echo "OpenResty运行正常"
|
||||
fi
|
||||
else
|
||||
echo "kill nginx"
|
||||
ps -ef|grep nginx| grep -v grep| awk '{print $2}' | xargs kill -9
|
||||
echo "OpenResty未运行,正在启动服务..."
|
||||
systemctl start "$service_name"
|
||||
echo "服务已启动"
|
||||
fi
|
||||
|
||||
NGINX_IDS=`ps -ef|grep nginx | grep -v grep| awk '{print $2}'`
|
||||
if [ "$NGINX_IDS" = "" ];then
|
||||
ps -ef|grep nginx| grep -v grep| awk '{print $2}' | xargs kill -9
|
||||
systemctl start "$service_name"
|
||||
echo "OpenResty未运行,正在启动服务..."
|
||||
fi
|
||||
|
||||
@@ -1,57 +0,0 @@
|
||||
<IfModule !mpm_netware_module>
|
||||
PidFile "logs/httpd.pid"
|
||||
</IfModule>
|
||||
|
||||
<IfModule mpm_prefork_module>
|
||||
StartServers 5
|
||||
MinSpareServers 5
|
||||
MaxSpareServers 10
|
||||
MaxRequestWorkers 250
|
||||
MaxConnectionsPerChild 0
|
||||
</IfModule>
|
||||
|
||||
<IfModule mpm_worker_module>
|
||||
StartServers 3
|
||||
MinSpareThreads 75
|
||||
MaxSpareThreads 250
|
||||
ThreadsPerChild 25
|
||||
MaxRequestWorkers 400
|
||||
MaxConnectionsPerChild 0
|
||||
</IfModule>
|
||||
|
||||
<IfModule mpm_event_module>
|
||||
StartServers 3
|
||||
MinSpareThreads 75
|
||||
MaxSpareThreads 250
|
||||
ThreadsPerChild 25
|
||||
MaxRequestWorkers 400
|
||||
MaxConnectionsPerChild 0
|
||||
</IfModule>
|
||||
|
||||
<IfModule mpm_netware_module>
|
||||
ThreadStackSize 65536
|
||||
StartThreads 250
|
||||
MinSpareThreads 25
|
||||
MaxSpareThreads 250
|
||||
MaxThreads 1000
|
||||
MaxConnectionsPerChild 0
|
||||
</IfModule>
|
||||
|
||||
<IfModule mpm_mpmt_os2_module>
|
||||
StartServers 2
|
||||
MinSpareThreads 5
|
||||
MaxSpareThreads 10
|
||||
MaxConnectionsPerChild 0
|
||||
</IfModule>
|
||||
|
||||
<IfModule mpm_winnt_module>
|
||||
ThreadsPerChild 150
|
||||
MaxConnectionsPerChild 0
|
||||
</IfModule>
|
||||
|
||||
<IfModule !mpm_netware_module>
|
||||
MaxMemFree 2048
|
||||
</IfModule>
|
||||
<IfModule mpm_netware_module>
|
||||
MaxMemFree 100
|
||||
</IfModule>
|
||||
@@ -1,105 +0,0 @@
|
||||
ServerRoot "{$SERVER_PATH}/apache/httpd"
|
||||
Listen 8280
|
||||
|
||||
LoadModule authn_file_module modules/mod_authn_file.so
|
||||
LoadModule authn_core_module modules/mod_authn_core.so
|
||||
LoadModule authz_host_module modules/mod_authz_host.so
|
||||
LoadModule authz_groupfile_module modules/mod_authz_groupfile.so
|
||||
LoadModule authz_user_module modules/mod_authz_user.so
|
||||
LoadModule authz_core_module modules/mod_authz_core.so
|
||||
LoadModule access_compat_module modules/mod_access_compat.so
|
||||
LoadModule auth_basic_module modules/mod_auth_basic.so
|
||||
LoadModule reqtimeout_module modules/mod_reqtimeout.so
|
||||
LoadModule filter_module modules/mod_filter.so
|
||||
LoadModule brotli_module modules/mod_brotli.so
|
||||
LoadModule mime_module modules/mod_mime.so
|
||||
LoadModule log_config_module modules/mod_log_config.so
|
||||
LoadModule env_module modules/mod_env.so
|
||||
LoadModule expires_module modules/mod_expires.so
|
||||
LoadModule headers_module modules/mod_headers.so
|
||||
LoadModule unique_id_module modules/mod_unique_id.so
|
||||
LoadModule setenvif_module modules/mod_setenvif.so
|
||||
LoadModule version_module modules/mod_version.so
|
||||
LoadModule unixd_module modules/mod_unixd.so
|
||||
LoadModule status_module modules/mod_status.so
|
||||
LoadModule dir_module modules/mod_dir.so
|
||||
LoadModule alias_module modules/mod_alias.so
|
||||
LoadModule rewrite_module modules/mod_rewrite.so
|
||||
|
||||
<IfModule unixd_module>
|
||||
User daemon
|
||||
Group daemon
|
||||
</IfModule>
|
||||
|
||||
ServerAdmin you@example.com
|
||||
ServerName localhost:8280
|
||||
|
||||
<Directory />
|
||||
AllowOverride none
|
||||
Require all denied
|
||||
</Directory>
|
||||
|
||||
DocumentRoot "{$SERVER_PATH}/apache/httpd/htdocs"
|
||||
<Directory "{$SERVER_PATH}/apache/httpd/htdocs">
|
||||
Options Indexes FollowSymLinks
|
||||
AllowOverride None
|
||||
Require all granted
|
||||
</Directory>
|
||||
|
||||
<IfModule dir_module>
|
||||
DirectoryIndex index.html
|
||||
</IfModule>
|
||||
|
||||
<Files ".ht*">
|
||||
Require all denied
|
||||
</Files>
|
||||
|
||||
ErrorLog "logs/error.log"
|
||||
LogLevel warn
|
||||
|
||||
<IfModule log_config_module>
|
||||
LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" combined
|
||||
LogFormat "%h %l %u %t \"%r\" %>s %b" common
|
||||
|
||||
<IfModule logio_module>
|
||||
# You need to enable mod_logio.c to use %I and %O
|
||||
LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\" %I %O" combinedio
|
||||
</IfModule>
|
||||
|
||||
CustomLog "logs/access.log" common
|
||||
</IfModule>
|
||||
|
||||
<IfModule alias_module>
|
||||
ScriptAlias /cgi-bin/ "{$SERVER_PATH}/apache/httpd/cgi-bin/"
|
||||
</IfModule>
|
||||
|
||||
<IfModule cgid_module>
|
||||
</IfModule>
|
||||
|
||||
<Directory "{$SERVER_PATH}/apache/httpd/cgi-bin">
|
||||
AllowOverride None
|
||||
Options None
|
||||
Require all granted
|
||||
</Directory>
|
||||
|
||||
<IfModule headers_module>
|
||||
RequestHeader unset Proxy early
|
||||
</IfModule>
|
||||
|
||||
<IfModule mime_module>
|
||||
AddType application/x-compress .Z
|
||||
AddType application/x-gzip .gz .tgz
|
||||
</IfModule>
|
||||
|
||||
EnableSendfile on
|
||||
|
||||
Include conf/extra/httpd-info.conf
|
||||
Include conf/extra/httpd-mpm.conf
|
||||
|
||||
<IfModule ssl_module>
|
||||
SSLRandomSeed startup builtin
|
||||
SSLRandomSeed connect builtin
|
||||
</IfModule>
|
||||
|
||||
|
||||
|
||||
@@ -1,506 +0,0 @@
|
||||
#
|
||||
# This is the main Apache HTTP server configuration file. It contains the
|
||||
# configuration directives that give the server its instructions.
|
||||
# See <URL:http://httpd.apache.org/docs/2.4/> for detailed information.
|
||||
# In particular, see
|
||||
# <URL:http://httpd.apache.org/docs/2.4/mod/directives.html>
|
||||
# for a discussion of each configuration directive.
|
||||
#
|
||||
# Do NOT simply read the instructions in here without understanding
|
||||
# what they do. They're here only as hints or reminders. If you are unsure
|
||||
# consult the online docs. You have been warned.
|
||||
#
|
||||
# Configuration and logfile names: If the filenames you specify for many
|
||||
# of the server's control files begin with "/" (or "drive:/" for Win32), the
|
||||
# server will use that explicit path. If the filenames do *not* begin
|
||||
# with "/", the value of ServerRoot is prepended -- so "logs/access_log"
|
||||
# with ServerRoot set to "/usr/local/apache2" will be interpreted by the
|
||||
# server as "/usr/local/apache2/logs/access_log", whereas "/logs/access_log"
|
||||
# will be interpreted as '/logs/access_log'.
|
||||
|
||||
#
|
||||
# ServerRoot: The top of the directory tree under which the server's
|
||||
# configuration, error, and log files are kept.
|
||||
#
|
||||
# Do not add a slash at the end of the directory path. If you point
|
||||
# ServerRoot at a non-local disk, be sure to specify a local disk on the
|
||||
# Mutex directive, if file-based mutexes are used. If you wish to share the
|
||||
# same ServerRoot for multiple httpd daemons, you will need to change at
|
||||
# least PidFile.
|
||||
#
|
||||
ServerRoot "/Users/midoks/Desktop/mwdev/server/apache/httpd"
|
||||
|
||||
#
|
||||
# Mutex: Allows you to set the mutex mechanism and mutex file directory
|
||||
# for individual mutexes, or change the global defaults
|
||||
#
|
||||
# Uncomment and change the directory if mutexes are file-based and the default
|
||||
# mutex file directory is not on a local disk or is not appropriate for some
|
||||
# other reason.
|
||||
#
|
||||
# Mutex default:logs
|
||||
|
||||
#
|
||||
# Listen: Allows you to bind Apache to specific IP addresses and/or
|
||||
# ports, instead of the default. See also the <VirtualHost>
|
||||
# directive.
|
||||
#
|
||||
# Change this to Listen on specific IP addresses as shown below to
|
||||
# prevent Apache from glomming onto all bound IP addresses.
|
||||
#
|
||||
#Listen 12.34.56.78:80
|
||||
Listen 80
|
||||
|
||||
#
|
||||
# Dynamic Shared Object (DSO) Support
|
||||
#
|
||||
# To be able to use the functionality of a module which was built as a DSO you
|
||||
# have to place corresponding `LoadModule' lines at this location so the
|
||||
# directives contained in it are actually available _before_ they are used.
|
||||
# Statically compiled modules (those listed by `httpd -l') do not need
|
||||
# to be loaded here.
|
||||
#
|
||||
# Example:
|
||||
# LoadModule foo_module modules/mod_foo.so
|
||||
#
|
||||
LoadModule authn_file_module modules/mod_authn_file.so
|
||||
#LoadModule authn_dbm_module modules/mod_authn_dbm.so
|
||||
#LoadModule authn_anon_module modules/mod_authn_anon.so
|
||||
#LoadModule authn_dbd_module modules/mod_authn_dbd.so
|
||||
#LoadModule authn_socache_module modules/mod_authn_socache.so
|
||||
LoadModule authn_core_module modules/mod_authn_core.so
|
||||
LoadModule authz_host_module modules/mod_authz_host.so
|
||||
LoadModule authz_groupfile_module modules/mod_authz_groupfile.so
|
||||
LoadModule authz_user_module modules/mod_authz_user.so
|
||||
#LoadModule authz_dbm_module modules/mod_authz_dbm.so
|
||||
#LoadModule authz_owner_module modules/mod_authz_owner.so
|
||||
#LoadModule authz_dbd_module modules/mod_authz_dbd.so
|
||||
LoadModule authz_core_module modules/mod_authz_core.so
|
||||
LoadModule access_compat_module modules/mod_access_compat.so
|
||||
LoadModule auth_basic_module modules/mod_auth_basic.so
|
||||
#LoadModule auth_form_module modules/mod_auth_form.so
|
||||
#LoadModule auth_digest_module modules/mod_auth_digest.so
|
||||
#LoadModule allowmethods_module modules/mod_allowmethods.so
|
||||
#LoadModule file_cache_module modules/mod_file_cache.so
|
||||
#LoadModule cache_module modules/mod_cache.so
|
||||
#LoadModule cache_disk_module modules/mod_cache_disk.so
|
||||
#LoadModule cache_socache_module modules/mod_cache_socache.so
|
||||
#LoadModule socache_shmcb_module modules/mod_socache_shmcb.so
|
||||
#LoadModule socache_dbm_module modules/mod_socache_dbm.so
|
||||
#LoadModule socache_memcache_module modules/mod_socache_memcache.so
|
||||
#LoadModule socache_redis_module modules/mod_socache_redis.so
|
||||
#LoadModule watchdog_module modules/mod_watchdog.so
|
||||
#LoadModule macro_module modules/mod_macro.so
|
||||
#LoadModule dbd_module modules/mod_dbd.so
|
||||
#LoadModule dumpio_module modules/mod_dumpio.so
|
||||
#LoadModule buffer_module modules/mod_buffer.so
|
||||
#LoadModule ratelimit_module modules/mod_ratelimit.so
|
||||
LoadModule reqtimeout_module modules/mod_reqtimeout.so
|
||||
#LoadModule ext_filter_module modules/mod_ext_filter.so
|
||||
#LoadModule request_module modules/mod_request.so
|
||||
#LoadModule include_module modules/mod_include.so
|
||||
LoadModule filter_module modules/mod_filter.so
|
||||
#LoadModule substitute_module modules/mod_substitute.so
|
||||
#LoadModule sed_module modules/mod_sed.so
|
||||
#LoadModule brotli_module modules/mod_brotli.so
|
||||
LoadModule mime_module modules/mod_mime.so
|
||||
LoadModule log_config_module modules/mod_log_config.so
|
||||
#LoadModule log_debug_module modules/mod_log_debug.so
|
||||
#LoadModule logio_module modules/mod_logio.so
|
||||
LoadModule env_module modules/mod_env.so
|
||||
#LoadModule expires_module modules/mod_expires.so
|
||||
LoadModule headers_module modules/mod_headers.so
|
||||
#LoadModule unique_id_module modules/mod_unique_id.so
|
||||
LoadModule setenvif_module modules/mod_setenvif.so
|
||||
LoadModule version_module modules/mod_version.so
|
||||
#LoadModule remoteip_module modules/mod_remoteip.so
|
||||
#LoadModule proxy_module modules/mod_proxy.so
|
||||
#LoadModule proxy_connect_module modules/mod_proxy_connect.so
|
||||
#LoadModule proxy_ftp_module modules/mod_proxy_ftp.so
|
||||
#LoadModule proxy_http_module modules/mod_proxy_http.so
|
||||
#LoadModule proxy_fcgi_module modules/mod_proxy_fcgi.so
|
||||
#LoadModule proxy_scgi_module modules/mod_proxy_scgi.so
|
||||
#LoadModule proxy_uwsgi_module modules/mod_proxy_uwsgi.so
|
||||
#LoadModule proxy_fdpass_module modules/mod_proxy_fdpass.so
|
||||
#LoadModule proxy_wstunnel_module modules/mod_proxy_wstunnel.so
|
||||
#LoadModule proxy_ajp_module modules/mod_proxy_ajp.so
|
||||
#LoadModule proxy_balancer_module modules/mod_proxy_balancer.so
|
||||
#LoadModule proxy_express_module modules/mod_proxy_express.so
|
||||
#LoadModule proxy_hcheck_module modules/mod_proxy_hcheck.so
|
||||
#LoadModule session_module modules/mod_session.so
|
||||
#LoadModule session_cookie_module modules/mod_session_cookie.so
|
||||
#LoadModule session_dbd_module modules/mod_session_dbd.so
|
||||
#LoadModule slotmem_shm_module modules/mod_slotmem_shm.so
|
||||
#LoadModule ssl_module modules/mod_ssl.so
|
||||
#LoadModule http2_module modules/mod_http2.so
|
||||
#LoadModule lbmethod_byrequests_module modules/mod_lbmethod_byrequests.so
|
||||
#LoadModule lbmethod_bytraffic_module modules/mod_lbmethod_bytraffic.so
|
||||
#LoadModule lbmethod_bybusyness_module modules/mod_lbmethod_bybusyness.so
|
||||
#LoadModule lbmethod_heartbeat_module modules/mod_lbmethod_heartbeat.so
|
||||
LoadModule unixd_module modules/mod_unixd.so
|
||||
#LoadModule dav_module modules/mod_dav.so
|
||||
LoadModule status_module modules/mod_status.so
|
||||
LoadModule autoindex_module modules/mod_autoindex.so
|
||||
#LoadModule info_module modules/mod_info.so
|
||||
#LoadModule cgid_module modules/mod_cgid.so
|
||||
#LoadModule dav_fs_module modules/mod_dav_fs.so
|
||||
#LoadModule vhost_alias_module modules/mod_vhost_alias.so
|
||||
#LoadModule negotiation_module modules/mod_negotiation.so
|
||||
LoadModule dir_module modules/mod_dir.so
|
||||
#LoadModule actions_module modules/mod_actions.so
|
||||
#LoadModule speling_module modules/mod_speling.so
|
||||
#LoadModule userdir_module modules/mod_userdir.so
|
||||
LoadModule alias_module modules/mod_alias.so
|
||||
#LoadModule rewrite_module modules/mod_rewrite.so
|
||||
|
||||
<IfModule unixd_module>
|
||||
#
|
||||
# If you wish httpd to run as a different user or group, you must run
|
||||
# httpd as root initially and it will switch.
|
||||
#
|
||||
# User/Group: The name (or #number) of the user/group to run httpd as.
|
||||
# It is usually good practice to create a dedicated user and group for
|
||||
# running httpd, as with most system services.
|
||||
#
|
||||
User daemon
|
||||
Group daemon
|
||||
|
||||
</IfModule>
|
||||
|
||||
# 'Main' server configuration
|
||||
#
|
||||
# The directives in this section set up the values used by the 'main'
|
||||
# server, which responds to any requests that aren't handled by a
|
||||
# <VirtualHost> definition. These values also provide defaults for
|
||||
# any <VirtualHost> containers you may define later in the file.
|
||||
#
|
||||
# All of these directives may appear inside <VirtualHost> containers,
|
||||
# in which case these default settings will be overridden for the
|
||||
# virtual host being defined.
|
||||
#
|
||||
|
||||
#
|
||||
# ServerAdmin: Your address, where problems with the server should be
|
||||
# e-mailed. This address appears on some server-generated pages, such
|
||||
# as error documents. e.g. admin@your-domain.com
|
||||
#
|
||||
ServerAdmin you@example.com
|
||||
|
||||
#
|
||||
# ServerName gives the name and port that the server uses to identify itself.
|
||||
# This can often be determined automatically, but we recommend you specify
|
||||
# it explicitly to prevent problems during startup.
|
||||
#
|
||||
# If your host doesn't have a registered DNS name, enter its IP address here.
|
||||
#
|
||||
#ServerName www.example.com:80
|
||||
|
||||
#
|
||||
# Deny access to the entirety of your server's filesystem. You must
|
||||
# explicitly permit access to web content directories in other
|
||||
# <Directory> blocks below.
|
||||
#
|
||||
<Directory />
|
||||
AllowOverride none
|
||||
Require all denied
|
||||
</Directory>
|
||||
|
||||
#
|
||||
# Note that from this point forward you must specifically allow
|
||||
# particular features to be enabled - so if something's not working as
|
||||
# you might expect, make sure that you have specifically enabled it
|
||||
# below.
|
||||
#
|
||||
|
||||
#
|
||||
# DocumentRoot: The directory out of which you will serve your
|
||||
# documents. By default, all requests are taken from this directory, but
|
||||
# symbolic links and aliases may be used to point to other locations.
|
||||
#
|
||||
DocumentRoot "/Users/midoks/Desktop/mwdev/server/apache/httpd/htdocs"
|
||||
<Directory "/Users/midoks/Desktop/mwdev/server/apache/httpd/htdocs">
|
||||
#
|
||||
# Possible values for the Options directive are "None", "All",
|
||||
# or any combination of:
|
||||
# Indexes Includes FollowSymLinks SymLinksifOwnerMatch ExecCGI MultiViews
|
||||
#
|
||||
# Note that "MultiViews" must be named *explicitly* --- "Options All"
|
||||
# doesn't give it to you.
|
||||
#
|
||||
# The Options directive is both complicated and important. Please see
|
||||
# http://httpd.apache.org/docs/2.4/mod/core.html#options
|
||||
# for more information.
|
||||
#
|
||||
Options Indexes FollowSymLinks
|
||||
|
||||
#
|
||||
# AllowOverride controls what directives may be placed in .htaccess files.
|
||||
# It can be "All", "None", or any combination of the keywords:
|
||||
# AllowOverride FileInfo AuthConfig Limit
|
||||
#
|
||||
AllowOverride None
|
||||
|
||||
#
|
||||
# Controls who can get stuff from this server.
|
||||
#
|
||||
Require all granted
|
||||
</Directory>
|
||||
|
||||
#
|
||||
# DirectoryIndex: sets the file that Apache will serve if a directory
|
||||
# is requested.
|
||||
#
|
||||
<IfModule dir_module>
|
||||
DirectoryIndex index.html
|
||||
</IfModule>
|
||||
|
||||
#
|
||||
# The following lines prevent .htaccess and .htpasswd files from being
|
||||
# viewed by Web clients.
|
||||
#
|
||||
<Files ".ht*">
|
||||
Require all denied
|
||||
</Files>
|
||||
|
||||
#
|
||||
# ErrorLog: The location of the error log file.
|
||||
# If you do not specify an ErrorLog directive within a <VirtualHost>
|
||||
# container, error messages relating to that virtual host will be
|
||||
# logged here. If you *do* define an error logfile for a <VirtualHost>
|
||||
# container, that host's errors will be logged there and not here.
|
||||
#
|
||||
ErrorLog "logs/error_log"
|
||||
|
||||
#
|
||||
# LogLevel: Control the number of messages logged to the error_log.
|
||||
# Possible values include: debug, info, notice, warn, error, crit,
|
||||
# alert, emerg.
|
||||
#
|
||||
LogLevel warn
|
||||
|
||||
<IfModule log_config_module>
|
||||
#
|
||||
# The following directives define some format nicknames for use with
|
||||
# a CustomLog directive (see below).
|
||||
#
|
||||
LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"" combined
|
||||
LogFormat "%h %l %u %t \"%r\" %>s %b" common
|
||||
|
||||
<IfModule logio_module>
|
||||
# You need to enable mod_logio.c to use %I and %O
|
||||
LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\" %I %O" combinedio
|
||||
</IfModule>
|
||||
|
||||
#
|
||||
# The location and format of the access logfile (Common Logfile Format).
|
||||
# If you do not define any access logfiles within a <VirtualHost>
|
||||
# container, they will be logged here. Contrariwise, if you *do*
|
||||
# define per-<VirtualHost> access logfiles, transactions will be
|
||||
# logged therein and *not* in this file.
|
||||
#
|
||||
CustomLog "logs/access_log" common
|
||||
|
||||
#
|
||||
# If you prefer a logfile with access, agent, and referer information
|
||||
# (Combined Logfile Format) you can use the following directive.
|
||||
#
|
||||
#CustomLog "logs/access_log" combined
|
||||
</IfModule>
|
||||
|
||||
<IfModule alias_module>
|
||||
#
|
||||
# Redirect: Allows you to tell clients about documents that used to
|
||||
# exist in your server's namespace, but do not anymore. The client
|
||||
# will make a new request for the document at its new location.
|
||||
# Example:
|
||||
# Redirect permanent /foo http://www.example.com/bar
|
||||
|
||||
#
|
||||
# Alias: Maps web paths into filesystem paths and is used to
|
||||
# access content that does not live under the DocumentRoot.
|
||||
# Example:
|
||||
# Alias /webpath /full/filesystem/path
|
||||
#
|
||||
# If you include a trailing / on /webpath then the server will
|
||||
# require it to be present in the URL. You will also likely
|
||||
# need to provide a <Directory> section to allow access to
|
||||
# the filesystem path.
|
||||
|
||||
#
|
||||
# ScriptAlias: This controls which directories contain server scripts.
|
||||
# ScriptAliases are essentially the same as Aliases, except that
|
||||
# documents in the target directory are treated as applications and
|
||||
# run by the server when requested rather than as documents sent to the
|
||||
# client. The same rules about trailing "/" apply to ScriptAlias
|
||||
# directives as to Alias.
|
||||
#
|
||||
ScriptAlias /cgi-bin/ "/Users/midoks/Desktop/mwdev/server/apache/httpd/cgi-bin/"
|
||||
|
||||
</IfModule>
|
||||
|
||||
<IfModule cgid_module>
|
||||
#
|
||||
# ScriptSock: On threaded servers, designate the path to the UNIX
|
||||
# socket used to communicate with the CGI daemon of mod_cgid.
|
||||
#
|
||||
#Scriptsock cgisock
|
||||
</IfModule>
|
||||
|
||||
#
|
||||
# "/Users/midoks/Desktop/mwdev/server/apache/httpd/cgi-bin" should be changed to whatever your ScriptAliased
|
||||
# CGI directory exists, if you have that configured.
|
||||
#
|
||||
<Directory "/Users/midoks/Desktop/mwdev/server/apache/httpd/cgi-bin">
|
||||
AllowOverride None
|
||||
Options None
|
||||
Require all granted
|
||||
</Directory>
|
||||
|
||||
<IfModule headers_module>
|
||||
#
|
||||
# Avoid passing HTTP_PROXY environment to CGI's on this or any proxied
|
||||
# backend servers which have lingering "httpoxy" defects.
|
||||
# 'Proxy' request header is undefined by the IETF, not listed by IANA
|
||||
#
|
||||
RequestHeader unset Proxy early
|
||||
</IfModule>
|
||||
|
||||
<IfModule mime_module>
|
||||
#
|
||||
# TypesConfig points to the file containing the list of mappings from
|
||||
# filename extension to MIME-type.
|
||||
#
|
||||
TypesConfig conf/mime.types
|
||||
|
||||
#
|
||||
# AddType allows you to add to or override the MIME configuration
|
||||
# file specified in TypesConfig for specific file types.
|
||||
#
|
||||
#AddType application/x-gzip .tgz
|
||||
#
|
||||
# AddEncoding allows you to have certain browsers uncompress
|
||||
# information on the fly. Note: Not all browsers support this.
|
||||
#
|
||||
#AddEncoding x-compress .Z
|
||||
#AddEncoding x-gzip .gz .tgz
|
||||
#
|
||||
# If the AddEncoding directives above are commented-out, then you
|
||||
# probably should define those extensions to indicate media types:
|
||||
#
|
||||
AddType application/x-compress .Z
|
||||
AddType application/x-gzip .gz .tgz
|
||||
|
||||
#
|
||||
# AddHandler allows you to map certain file extensions to "handlers":
|
||||
# actions unrelated to filetype. These can be either built into the server
|
||||
# or added with the Action directive (see below)
|
||||
#
|
||||
# To use CGI scripts outside of ScriptAliased directories:
|
||||
# (You will also need to add "ExecCGI" to the "Options" directive.)
|
||||
#
|
||||
#AddHandler cgi-script .cgi
|
||||
|
||||
# For type maps (negotiated resources):
|
||||
#AddHandler type-map var
|
||||
|
||||
#
|
||||
# Filters allow you to process content before it is sent to the client.
|
||||
#
|
||||
# To parse .shtml files for server-side includes (SSI):
|
||||
# (You will also need to add "Includes" to the "Options" directive.)
|
||||
#
|
||||
#AddType text/html .shtml
|
||||
#AddOutputFilter INCLUDES .shtml
|
||||
</IfModule>
|
||||
|
||||
#
|
||||
# The mod_mime_magic module allows the server to use various hints from the
|
||||
# contents of the file itself to determine its type. The MIMEMagicFile
|
||||
# directive tells the module where the hint definitions are located.
|
||||
#
|
||||
#MIMEMagicFile conf/magic
|
||||
|
||||
#
|
||||
# Customizable error responses come in three flavors:
|
||||
# 1) plain text 2) local redirects 3) external redirects
|
||||
#
|
||||
# Some examples:
|
||||
#ErrorDocument 500 "The server made a boo boo."
|
||||
#ErrorDocument 404 /missing.html
|
||||
#ErrorDocument 404 "/cgi-bin/missing_handler.pl"
|
||||
#ErrorDocument 402 http://www.example.com/subscription_info.html
|
||||
#
|
||||
|
||||
#
|
||||
# MaxRanges: Maximum number of Ranges in a request before
|
||||
# returning the entire resource, or one of the special
|
||||
# values 'default', 'none' or 'unlimited'.
|
||||
# Default setting is to accept 200 Ranges.
|
||||
#MaxRanges unlimited
|
||||
|
||||
#
|
||||
# EnableMMAP and EnableSendfile: On systems that support it,
|
||||
# memory-mapping or the sendfile syscall may be used to deliver
|
||||
# files. This usually improves server performance, but must
|
||||
# be turned off when serving from networked-mounted
|
||||
# filesystems or if support for these functions is otherwise
|
||||
# broken on your system.
|
||||
# Defaults: EnableMMAP On, EnableSendfile Off
|
||||
#
|
||||
#EnableMMAP off
|
||||
#EnableSendfile on
|
||||
|
||||
# Supplemental configuration
|
||||
#
|
||||
# The configuration files in the conf/extra/ directory can be
|
||||
# included to add extra features or to modify the default configuration of
|
||||
# the server, or you may simply copy their contents here and change as
|
||||
# necessary.
|
||||
|
||||
# Server-pool management (MPM specific)
|
||||
#Include conf/extra/httpd-mpm.conf
|
||||
|
||||
# Multi-language error messages
|
||||
#Include conf/extra/httpd-multilang-errordoc.conf
|
||||
|
||||
# Fancy directory listings
|
||||
#Include conf/extra/httpd-autoindex.conf
|
||||
|
||||
# Language settings
|
||||
#Include conf/extra/httpd-languages.conf
|
||||
|
||||
# User home directories
|
||||
#Include conf/extra/httpd-userdir.conf
|
||||
|
||||
# Real-time info on requests and configuration
|
||||
#Include conf/extra/httpd-info.conf
|
||||
|
||||
# Virtual hosts
|
||||
#Include conf/extra/httpd-vhosts.conf
|
||||
|
||||
# Local access to the Apache HTTP Server Manual
|
||||
#Include conf/extra/httpd-manual.conf
|
||||
|
||||
# Distributed authoring and versioning (WebDAV)
|
||||
#Include conf/extra/httpd-dav.conf
|
||||
|
||||
# Various default settings
|
||||
#Include conf/extra/httpd-default.conf
|
||||
|
||||
# Configure mod_proxy_html to understand HTML4/XHTML1
|
||||
<IfModule proxy_html_module>
|
||||
Include conf/extra/proxy-html.conf
|
||||
</IfModule>
|
||||
|
||||
# Secure (SSL/TLS) connections
|
||||
#Include conf/extra/httpd-ssl.conf
|
||||
#
|
||||
# Note: The following must must be present to support
|
||||
# starting without SSL on platforms with no /dev/random equivalent
|
||||
# but a statically compiled-in mod_ssl.
|
||||
#
|
||||
<IfModule ssl_module>
|
||||
SSLRandomSeed startup builtin
|
||||
SSLRandomSeed connect builtin
|
||||
</IfModule>
|
||||
|
||||
|
Before Width: | Height: | Size: 1.4 KiB |
@@ -1,25 +0,0 @@
|
||||
<div class="bt-form">
|
||||
<!-- <div class='plugin_version'></div> -->
|
||||
<div class="bt-w-main">
|
||||
<div class="bt-w-menu">
|
||||
<p class="bgw" onclick="httpPluginService('apache',$('.plugin_version').attr('version'));">服务</p>
|
||||
<p onclick="pluginInitD('apache', $('.plugin_version').attr('version'));">自启动</p>
|
||||
<p onclick="pluginConfig('apache');">配置修改</p>
|
||||
<p onclick="getHttpdStatus();">负载状态</p>
|
||||
<p onclick="setOpCfg();">MPM性能调整</p>
|
||||
<p onclick="pluginLogs('apache');">错误日志</p>
|
||||
<p onclick="otherFunc();">维护功能</p>
|
||||
</div>
|
||||
<div class="bt-w-con pd15">
|
||||
<div class="soft-man-con" style="height: 500px; overflow-y: auto;"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<script type="text/javascript">
|
||||
// resetPluginWinHeight(600);
|
||||
$.getScript( "/plugins/file?name=apache&f=js/httpd.js", function(){
|
||||
httpPluginService('apache');
|
||||
});
|
||||
</script>
|
||||
@@ -1,593 +0,0 @@
|
||||
# coding:utf-8
|
||||
|
||||
import sys
|
||||
import io
|
||||
import os
|
||||
import time
|
||||
import threading
|
||||
import subprocess
|
||||
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 'apache'
|
||||
|
||||
|
||||
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 getArgs():
|
||||
args = sys.argv[2:]
|
||||
# print(args)
|
||||
tmp = {}
|
||||
args_len = len(args)
|
||||
|
||||
if args_len == 1:
|
||||
t = args[0].strip('{').strip('}')
|
||||
t = t.split(':',2)
|
||||
tmp[t[0]] = t[1]
|
||||
elif args_len > 1:
|
||||
for i in range(len(args)):
|
||||
t = args[i].split(':',2)
|
||||
tmp[t[0]] = t[1]
|
||||
# print(tmp)
|
||||
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 clearTemp():
|
||||
path_bin = getServerDir() + "/httpd"
|
||||
|
||||
|
||||
def getConf():
|
||||
path = getServerDir() + "/httpd/conf/httpd.conf"
|
||||
return path
|
||||
|
||||
|
||||
def getConfMpm():
|
||||
path = getServerDir() + "/httpd/conf/extra/httpd-mpm.conf"
|
||||
return path
|
||||
|
||||
|
||||
def getConfTpl():
|
||||
path = getPluginDir() + '/conf/httpd.conf'
|
||||
return path
|
||||
|
||||
|
||||
def getOs():
|
||||
data = {}
|
||||
data['os'] = mw.getOs()
|
||||
data['auth'] = True
|
||||
return mw.getJson(data)
|
||||
|
||||
|
||||
def getInitDTpl():
|
||||
path = getPluginDir() + "/init.d/httpd.tpl"
|
||||
return path
|
||||
|
||||
|
||||
def getPidFile():
|
||||
file = getConf()
|
||||
content = mw.readFile(file)
|
||||
rep = r'pid\s*(.*);'
|
||||
tmp = re.search(rep, content)
|
||||
return tmp.groups()[0].strip()
|
||||
|
||||
|
||||
def getFileOwner(filename):
|
||||
import pwd
|
||||
stat = os.lstat(filename)
|
||||
uid = stat.st_uid
|
||||
pw = pwd.getpwuid(uid)
|
||||
return pw.pw_name
|
||||
|
||||
|
||||
def checkAuthEq(file, owner='root'):
|
||||
fowner = getFileOwner(file)
|
||||
if (fowner == owner):
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def confReplace():
|
||||
service_path = mw.getServerDir()
|
||||
content = mw.readFile(getConfTpl())
|
||||
content = content.replace('{$SERVER_PATH}', service_path)
|
||||
|
||||
# 主配置文件
|
||||
nconf = getServerDir() + '/httpd/conf/httpd.conf'
|
||||
mw.writeFile(nconf, content)
|
||||
|
||||
|
||||
def initDreplace():
|
||||
|
||||
file_tpl = getInitDTpl()
|
||||
service_path = mw.getServerDir()
|
||||
|
||||
initD_path = getServerDir() + '/init.d'
|
||||
|
||||
# OpenResty is not installed
|
||||
if not os.path.exists(getServerDir()):
|
||||
print("ok")
|
||||
exit(0)
|
||||
|
||||
# init.d
|
||||
file_bin = initD_path + '/' + getPluginName()
|
||||
if not os.path.exists(initD_path):
|
||||
os.mkdir(initD_path)
|
||||
|
||||
# initd replace
|
||||
content = mw.readFile(file_tpl)
|
||||
content = content.replace('{$SERVER_PATH}', service_path)
|
||||
mw.writeFile(file_bin, content)
|
||||
mw.execShell('chmod +x ' + file_bin)
|
||||
|
||||
# config replace
|
||||
confReplace()
|
||||
|
||||
# systemd
|
||||
# /usr/lib/systemd/system
|
||||
systemDir = mw.systemdCfgDir()
|
||||
systemService = systemDir + '/httpd.service'
|
||||
if os.path.exists(systemDir) and not os.path.exists(systemService):
|
||||
systemServiceTpl = getPluginDir() + '/init.d/httpd.service.tpl'
|
||||
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 status():
|
||||
cmd = "ps -ef|grep 'httpd' |grep -v grep | grep -v python | awk '{print $2}'"
|
||||
data = mw.execShell(cmd)
|
||||
if data[0] == '':
|
||||
return 'stop'
|
||||
return 'start'
|
||||
|
||||
|
||||
def restyOp(method):
|
||||
file = initDreplace()
|
||||
|
||||
# 启动时,先检查一下配置文件
|
||||
check = getServerDir() + "/httpd/bin/httpd -t"
|
||||
check_data = mw.execShell(check)
|
||||
if not check_data[1].find('Syntax OK') > -1:
|
||||
return check_data[1]
|
||||
|
||||
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"):
|
||||
mw.execShell('service httpd '+method)
|
||||
if data[1] == '':
|
||||
return 'ok'
|
||||
return data[1]
|
||||
|
||||
data = mw.execShell('systemctl ' + method + ' httpd')
|
||||
if data[1] == '':
|
||||
return 'ok'
|
||||
return data[1]
|
||||
|
||||
|
||||
def op_submit_systemctl_restart():
|
||||
current_os = mw.getOs()
|
||||
if current_os.startswith("freebsd"):
|
||||
mw.execShell('service httpd restart')
|
||||
return True
|
||||
|
||||
mw.execShell('systemctl restart httpd')
|
||||
return True
|
||||
|
||||
|
||||
def op_submit_init_restart(file):
|
||||
mw.execShell(file + ' restart')
|
||||
|
||||
|
||||
def restyOp_restart():
|
||||
file = initDreplace()
|
||||
|
||||
# 启动时,先检查一下配置文件
|
||||
check = getServerDir() + "/httpd/bin/httpd -t"
|
||||
check_data = mw.execShell(check)
|
||||
if not check_data[1].find('Syntax OK') > -1:
|
||||
return 'ERROR: 配置出错<br><a style="color:red;">' + check_data[1].replace("\n", '<br>') + '</a>'
|
||||
|
||||
if not mw.isAppleSystem():
|
||||
threading.Timer(2, op_submit_systemctl_restart).start()
|
||||
return 'ok'
|
||||
|
||||
threading.Timer(2, op_submit_init_restart, args=(file,)).start()
|
||||
return 'ok'
|
||||
|
||||
|
||||
def start():
|
||||
return restyOp('start')
|
||||
|
||||
|
||||
def stop():
|
||||
r = restyOp('stop')
|
||||
|
||||
mw.execShell("ps -ef|grep httpd | grep -v grep | awk '{print $2}'|xargs -r kill")
|
||||
return r
|
||||
|
||||
|
||||
def restart():
|
||||
return restyOp_restart()
|
||||
|
||||
|
||||
def reload():
|
||||
confReplace()
|
||||
return restyOp('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 httpd | 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 httpd_enable="YES"')
|
||||
return 'ok'
|
||||
|
||||
mw.execShell('systemctl enable httpd')
|
||||
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 httpd_enable="NO"')
|
||||
return 'ok'
|
||||
|
||||
mw.execShell('systemctl disable httpd')
|
||||
return 'ok'
|
||||
|
||||
def getHttpdStatusPort():
|
||||
conf = mw.getServerDir() + '/apache/httpd/conf/httpd.conf'
|
||||
content = mw.readFile(conf)
|
||||
if not content:
|
||||
return None
|
||||
rep = r'^\s*Listen\s*(?:\d+\.\d+\.\d+\.\d+:)?(\d+)' # 匹配非注释行的 Listen 指令,忽略大小写
|
||||
tmp = re.search(rep, content, re.IGNORECASE | re.MULTILINE)
|
||||
if tmp:
|
||||
port = tmp.groups()[0].strip()
|
||||
return port
|
||||
return None
|
||||
|
||||
|
||||
def runInfoDone(data):
|
||||
result = {}
|
||||
if not data:
|
||||
return result
|
||||
|
||||
# 解析服务器状态数据
|
||||
lines = data.strip().split('\n')
|
||||
for line in lines:
|
||||
if ':' in line:
|
||||
key, value = line.split(':', 1)
|
||||
key = key.strip()
|
||||
value = value.strip()
|
||||
result[key] = value
|
||||
|
||||
return result
|
||||
|
||||
|
||||
def runInfo():
|
||||
op_status = status()
|
||||
if op_status == 'stop':
|
||||
return mw.returnJson(False, "未启动!")
|
||||
|
||||
port = getHttpdStatusPort()
|
||||
if not port:
|
||||
return mw.returnJson(False, "无法获取端口信息!")
|
||||
|
||||
# 取Openresty负载状态
|
||||
try:
|
||||
url = 'http://127.0.0.1:%s/server-status?auto' % port
|
||||
result = mw.httpGet(url, timeout=3)
|
||||
data = runInfoDone(result)
|
||||
return mw.getJson(data)
|
||||
except Exception as e:
|
||||
try:
|
||||
url = 'http://' + mw.getHostAddr() + ':%s/server-status?auto' % port
|
||||
result = mw.httpGet(url)
|
||||
data = runInfoDone(result)
|
||||
return mw.getJson(data)
|
||||
except Exception as e:
|
||||
return mw.returnJson(False, "apache异常!")
|
||||
|
||||
except Exception as e:
|
||||
return mw.returnJson(False, "apache not started!")
|
||||
|
||||
|
||||
def errorLogPath():
|
||||
return getServerDir() + '/httpd/logs/error.log'
|
||||
|
||||
|
||||
def getCfg():
|
||||
cfg = getConfMpm()
|
||||
content = mw.readFile(cfg)
|
||||
|
||||
unitrep = "[kmgKMG]"
|
||||
|
||||
# 获取当前 MPM 模块
|
||||
mpm_module = ""
|
||||
mpm_match = re.search(r"mpm_(\w+)_module", content)
|
||||
if mpm_match:
|
||||
mpm_module = mpm_match.group(1)
|
||||
|
||||
# MPM 配置参数
|
||||
mpm_cfg_args = {
|
||||
"prefork": [
|
||||
{"name": "StartServers", "ps": "服务器进程启动数量", 'type': 2},
|
||||
{"name": "MinSpareServers", "ps": "保持空闲的最小服务器进程数", 'type': 2},
|
||||
{"name": "MaxSpareServers", "ps": "保持空闲的最大服务器进程数", 'type': 2},
|
||||
{"name": "MaxRequestWorkers", "ps": "允许启动的最大服务器进程数", 'type': 2},
|
||||
{"name": "MaxConnectionsPerChild", "ps": "服务器进程服务的最大连接数", 'type': 2},
|
||||
],
|
||||
"worker": [
|
||||
{"name": "StartServers", "ps": "初始服务器进程数", 'type': 2},
|
||||
{"name": "MinSpareThreads", "ps": "保持空闲的最小工作线程数", 'type': 2},
|
||||
{"name": "MaxSpareThreads", "ps": "保持空闲的最大工作线程数", 'type': 2},
|
||||
{"name": "ThreadsPerChild", "ps": "每个服务器进程的工作线程数", 'type': 2},
|
||||
{"name": "MaxRequestWorkers", "ps": "最大工作线程数", 'type': 2},
|
||||
{"name": "MaxConnectionsPerChild", "ps": "服务器进程服务的最大连接数", 'type': 2},
|
||||
],
|
||||
"event": [
|
||||
{"name": "StartServers", "ps": "初始服务器进程数", 'type': 2},
|
||||
{"name": "MinSpareThreads", "ps": "保持空闲的最小工作线程数", 'type': 2},
|
||||
{"name": "MaxSpareThreads", "ps": "保持空闲的最大工作线程数", 'type': 2},
|
||||
{"name": "ThreadsPerChild", "ps": "每个服务器进程的工作线程数", 'type': 2},
|
||||
{"name": "MaxRequestWorkers", "ps": "最大工作线程数", 'type': 2},
|
||||
{"name": "MaxConnectionsPerChild", "ps": "服务器进程服务的最大连接数", 'type': 2},
|
||||
],
|
||||
"netware": [
|
||||
{"name": "ThreadStackSize", "ps": "每个工作线程分配的堆栈大小", 'type': 2},
|
||||
{"name": "StartThreads", "ps": "服务器启动时启动的工作线程数", 'type': 2},
|
||||
{"name": "MinSpareThreads", "ps": "保持空闲的最小线程数", 'type': 2},
|
||||
{"name": "MaxSpareThreads", "ps": "保持空闲的最大线程数", 'type': 2},
|
||||
{"name": "MaxThreads", "ps": "同时活跃的最大工作线程数", 'type': 2},
|
||||
{"name": "MaxConnectionsPerChild", "ps": "线程服务的最大连接数", 'type': 2},
|
||||
],
|
||||
"mpmt_os2": [
|
||||
{"name": "StartServers", "ps": "维护的服务器进程数", 'type': 2},
|
||||
{"name": "MinSpareThreads", "ps": "每个进程的最小空闲线程数", 'type': 2},
|
||||
{"name": "MaxSpareThreads", "ps": "每个进程的最大空闲线程数", 'type': 2},
|
||||
{"name": "MaxConnectionsPerChild", "ps": "每个服务器进程的最大连接数", 'type': 2},
|
||||
],
|
||||
"winnt": [
|
||||
{"name": "ThreadsPerChild", "ps": "服务器进程中的工作线程数", 'type': 2},
|
||||
{"name": "MaxConnectionsPerChild", "ps": "服务器进程服务的最大连接数", 'type': 2},
|
||||
]
|
||||
}
|
||||
|
||||
# 通用配置参数
|
||||
common_cfg_args = [
|
||||
{"name": "MaxMemFree", "ps": "每个分配器允许持有的最大空闲KB数", 'type': 2},
|
||||
]
|
||||
|
||||
# 合并配置参数
|
||||
cfg_args = []
|
||||
if mpm_module in mpm_cfg_args:
|
||||
cfg_args.extend(mpm_cfg_args[mpm_module])
|
||||
cfg_args.extend(common_cfg_args)
|
||||
|
||||
rdata = []
|
||||
for i in cfg_args:
|
||||
# 匹配 MPM 特定配置
|
||||
rep = r"<IfModule mpm_%s_module>.*?(%s)\s+(\w+).*?</IfModule>" % (mpm_module, i["name"])
|
||||
k = re.search(rep, content, re.DOTALL)
|
||||
|
||||
# 如果没有找到 MPM 特定配置,尝试匹配通用配置
|
||||
if not k:
|
||||
rep = r"(%s)\s+(\w+)" % i["name"]
|
||||
k = re.search(rep, content)
|
||||
|
||||
if not k:
|
||||
continue
|
||||
|
||||
key = k.group(1)
|
||||
v = k.group(2) if len(k.groups()) > 1 else ""
|
||||
|
||||
if re.search(unitrep, v):
|
||||
u = str.upper(v[-1])
|
||||
v = v[:-1]
|
||||
if len(u) == 1:
|
||||
psstr = u + "B," + i["ps"]
|
||||
else:
|
||||
psstr = u + "," + i["ps"]
|
||||
else:
|
||||
u = ""
|
||||
|
||||
kv = {"name": key, "value": v, "unit": u,
|
||||
"ps": i["ps"], "type": i["type"]}
|
||||
rdata.append(kv)
|
||||
return mw.returnJson(True, "ok", rdata)
|
||||
|
||||
def replaceChar(value, index, new_char):
|
||||
return value[:index] + new_char + value[index+1:]
|
||||
|
||||
def setCfg():
|
||||
args = getArgs()
|
||||
|
||||
# 检查参数,允许动态参数
|
||||
cfg = getConfMpm()
|
||||
mw.backFile(cfg)
|
||||
content = mw.readFile(cfg)
|
||||
|
||||
# 获取当前 MPM 模块
|
||||
mpm_module = ""
|
||||
mpm_match = re.search(r"mpm_(\w+)_module", content)
|
||||
if mpm_match:
|
||||
mpm_module = mpm_match.group(1)
|
||||
|
||||
# 验证参数值
|
||||
for k, v in args.items():
|
||||
# 检查是否为数字参数
|
||||
if not re.search(r"\d+", v):
|
||||
return mw.returnJson(False, '参数值错误,请输入数字整数')
|
||||
|
||||
# 替换 MPM 特定配置
|
||||
if mpm_module:
|
||||
def replace_mpm_config(match):
|
||||
return match.group(1) + k + match.group(2) + v + match.group(3)
|
||||
rep = r"(<IfModule mpm_%s_module>.*?)%s(\s+)\d+(.*?</IfModule>)" % (mpm_module, k)
|
||||
if re.search(rep, content, re.DOTALL):
|
||||
content = re.sub(rep, replace_mpm_config, content, flags=re.DOTALL)
|
||||
|
||||
# 替换通用配置
|
||||
def replace_common_config(match):
|
||||
return k + match.group(1) + v
|
||||
rep = r"%s(\s+)\d+" % k
|
||||
if re.search(rep, content):
|
||||
content = re.sub(rep, replace_common_config, content)
|
||||
|
||||
mw.writeFile(cfg, content)
|
||||
isError = mw.checkHttpdConfig()
|
||||
if (isError != True):
|
||||
mw.restoreFile(cfg)
|
||||
return mw.returnJson(False, 'ERROR: 配置出错<br><a style="color:red;">' + isError.replace("\n", '<br>') + '</a>')
|
||||
|
||||
mw.restartWeb()
|
||||
return mw.returnJson(True, '设置成功')
|
||||
|
||||
|
||||
def cronAddCheck():
|
||||
try:
|
||||
import tool_task
|
||||
tool_task.createBgTask()
|
||||
return mw.returnJson(True, '添加检查任务成功')
|
||||
except Exception as e:
|
||||
return mw.returnJson(False, '添加检查任务失败:'+str(e))
|
||||
|
||||
def cronDelCheck():
|
||||
try:
|
||||
import tool_task
|
||||
tool_task.removeBgTask()
|
||||
return mw.returnJson(True, '删除检查任务成功')
|
||||
except Exception as e:
|
||||
return mw.returnJson(False, '删除检查任务失败:'+str(e))
|
||||
|
||||
def cronCheck():
|
||||
return 'ok'
|
||||
|
||||
|
||||
def installPreInspection():
|
||||
return 'ok'
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
version = '2.4'
|
||||
version_pl = getServerDir() + "/version.pl"
|
||||
if os.path.exists(version_pl):
|
||||
version = mw.readFile(version_pl)
|
||||
|
||||
|
||||
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 == 'install_pre_inspection':
|
||||
print(installPreInspection())
|
||||
elif func == 'conf':
|
||||
print(getConf())
|
||||
elif func == 'get_os':
|
||||
print(getOs())
|
||||
elif func == 'run_info':
|
||||
print(runInfo())
|
||||
elif func == 'error_log':
|
||||
print(errorLogPath())
|
||||
elif func == 'get_cfg':
|
||||
print(getCfg())
|
||||
elif func == 'set_cfg':
|
||||
print(setCfg())
|
||||
elif func == 'check':
|
||||
print(cronCheck())
|
||||
elif func == 'cron_add_check':
|
||||
print(cronAddCheck())
|
||||
elif func == 'cron_del_check':
|
||||
print(cronDelCheck())
|
||||
else:
|
||||
print('error')
|
||||
@@ -1,18 +0,0 @@
|
||||
{
|
||||
"sort": 0,
|
||||
"title":"apache",
|
||||
"tip":"soft",
|
||||
"name":"apache",
|
||||
"type":"其他插件",
|
||||
"ps":"世界排名第一,快速、可靠并且可通过简单的API扩充",
|
||||
"shell":"install.sh",
|
||||
"install_pre_inspection":true,
|
||||
"checks":"server/apache",
|
||||
"path":"server/apache",
|
||||
"author":"apache",
|
||||
"home":"https://downloads.apache.org/httpd/",
|
||||
"date":"2026-04-11",
|
||||
"pid": "1",
|
||||
"versions": ["2.4"],
|
||||
"updates": ["2.4:66"]
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
[Unit]
|
||||
Description=Apache Web Server
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
Environment=LANG=C
|
||||
ExecStart={$SERVER_PATH}/apache/httpd/bin/httpd $OPTIONS -DFOREGROUND
|
||||
ExecReload={$SERVER_PATH}/apache/httpd/bin/httpd $OPTIONS -k graceful
|
||||
KillSignal=SIGWINCH
|
||||
|
||||
KillMode=mixed
|
||||
PrivateTmp=true
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
@@ -1,17 +0,0 @@
|
||||
[Unit]
|
||||
Description=Apache Web Server
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
Type=forking
|
||||
PIDFile={$SERVER_PATH}/apache/httpd/logs/httpd.pid
|
||||
ExecStart={$SERVER_PATH}/apache/httpd/bin/apachectl start
|
||||
ExecReload={$SERVER_PATH}/apache/httpd/bin/apachectl graceful
|
||||
ExecStop={$SERVER_PATH}/apache/httpd/bin/apachectl stop
|
||||
PrivateTmp=true
|
||||
User=www
|
||||
Group=www
|
||||
Restart=on-failure
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
@@ -1,47 +0,0 @@
|
||||
#!/bin/bash
|
||||
# chkconfig: 345 85 15
|
||||
# description: Apache Server
|
||||
|
||||
### BEGIN INIT INFO
|
||||
# Provides: apache
|
||||
# Required-Start: $network
|
||||
# Required-Stop: $network
|
||||
# Default-Start: 2 3 4 5
|
||||
# Default-Stop: 0 1 6
|
||||
# Description: Apache Server
|
||||
### END INIT INFO
|
||||
|
||||
|
||||
PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin:/opt/homebrew/bin
|
||||
# Apache 可执行文件的路径
|
||||
DAEMON={$SERVER_PATH}/apache/httpd/bin/httpd
|
||||
# 进程的 PID 文件路径
|
||||
PIDFILE={$SERVER_PATH}/apache/httpd/logs/httpd.pid
|
||||
|
||||
# 根据传入的参数执行不同操作
|
||||
case "$1" in
|
||||
start)
|
||||
echo "Starting Apache..."
|
||||
$DAEMON -k start
|
||||
;;
|
||||
stop)
|
||||
echo "Stopping Apache..."
|
||||
$DAEMON -k stop
|
||||
;;
|
||||
restart)
|
||||
echo "Restarting Apache..."
|
||||
$DAEMON -k restart
|
||||
;;
|
||||
status)
|
||||
if [ -f $PIDFILE ]; then
|
||||
echo "Apache is running."
|
||||
else
|
||||
echo "Apache is not running."
|
||||
fi
|
||||
;;
|
||||
*)
|
||||
echo "Usage: $0 {start|stop|restart|status}"
|
||||
exit 1
|
||||
esac
|
||||
|
||||
exit 0
|
||||
@@ -1,87 +0,0 @@
|
||||
#!/bin/bash
|
||||
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin:/opt/homebrew/bin
|
||||
export PATH
|
||||
|
||||
# cd /Users/midoks/Desktop/mwdev/server/mdserver-web/plugins/apache && bash install.sh install 2.4
|
||||
# cd /www/server/mdserver-web/plugins/apache && bash install.sh install 2.4
|
||||
# cd /www/server/mdserver-web/plugins/apache && bash install.sh upgrade 2.4
|
||||
|
||||
# curl -I -H "Accept-Encoding: br" http://localhost
|
||||
# curl -I -H "Accept-Encoding: zstd" http://localhost
|
||||
# curl --http3 -v https://www.xxx.com
|
||||
|
||||
# apt install ncat -y
|
||||
# nc -u -v www.xx.com 443
|
||||
|
||||
# cd /Users/midoks/Desktop/mwdev/server/mdserver-web/plugins/apache && python3 plugins/apache/index.py start
|
||||
# cd /www/server/mdserver-web && python3 plugins/apache/index.py start
|
||||
# cd /www/server/mdserver-web && python3 plugins/apache/index.py run_info
|
||||
# ps -ef|grep httpd | grep -v grep | awk '{print $2}'|xargs kill
|
||||
|
||||
curPath=`pwd`
|
||||
rootPath=$(dirname "$curPath")
|
||||
rootPath=$(dirname "$rootPath")
|
||||
serverPath=$(dirname "$rootPath")
|
||||
|
||||
sysName=`uname`
|
||||
action=$1
|
||||
type=$2
|
||||
|
||||
VERSION=$2
|
||||
openrestyDir=${serverPath}/source/apache
|
||||
|
||||
if id www &> /dev/null ;then
|
||||
echo "www uid is `id -u www`"
|
||||
echo "www shell is `grep "^www:" /etc/passwd |cut -d':' -f7 `"
|
||||
else
|
||||
groupadd www
|
||||
useradd -g www -s /bin/bash www
|
||||
fi
|
||||
|
||||
if [ "${action}" = "upgrade" ];then
|
||||
sh -x $curPath/versions/$2/install.sh $1
|
||||
|
||||
echo "${VERSION}" > $serverPath/apache/version.pl
|
||||
|
||||
mkdir -p $serverPath/web_conf/php/conf
|
||||
echo 'set $PHP_ENV 0;' > $serverPath/web_conf/php/conf/enable-php-00.conf
|
||||
|
||||
#初始化
|
||||
cd ${rootPath} && python3 ${rootPath}/plugins/apache/index.py start
|
||||
cd ${rootPath} && python3 ${rootPath}/plugins/apache/index.py initd_install
|
||||
exit 0
|
||||
fi
|
||||
|
||||
|
||||
if [ "${2}" = "" ];then
|
||||
echo '缺少安装脚本版本...'
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [ "${action}" = "uninstall" ];then
|
||||
if [ -f /usr/lib/systemd/system/httpd.service ] || [ -f /lib/systemd/system/httpd.service ];then
|
||||
systemctl stop httpd
|
||||
rm -rf /usr/systemd/system/httpd.service
|
||||
rm -rf /lib/systemd/system/httpd.service
|
||||
systemctl daemon-reload
|
||||
fi
|
||||
|
||||
if [ -f $serverPath/apache/init.d/httpd ];then
|
||||
$serverPath/apache/init.d/httpd stop
|
||||
fi
|
||||
|
||||
rm -rf $serverPath/apache
|
||||
fi
|
||||
|
||||
sh -x $curPath/versions/$2/install.sh $1
|
||||
|
||||
if [ "${action}" = "install" ] && [ -d $serverPath/apache ];then
|
||||
echo "${VERSION}" > $serverPath/apache/version.pl
|
||||
|
||||
mkdir -p $serverPath/web_conf/php/conf
|
||||
echo 'set $PHP_ENV 0;' > $serverPath/web_conf/php/conf/enable-php-00.conf
|
||||
|
||||
#初始化
|
||||
cd ${rootPath} && python3 ${rootPath}/plugins/apache/index.py start
|
||||
cd ${rootPath} && python3 ${rootPath}/plugins/apache/index.py initd_install
|
||||
fi
|
||||
@@ -1,278 +0,0 @@
|
||||
function httpPost(method, args, callback){
|
||||
var loadT = layer.msg('正在获取...', { icon: 16, time: 0, shade: 0.3 });
|
||||
$.post('/plugins/run', {name:'apache', func:method, args:JSON.stringify(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 httpPluginService(_name, version){
|
||||
var data = {name:_name, func:'status'}
|
||||
if ( typeof(version) != 'undefined' ){
|
||||
data['version'] = version;
|
||||
} else {
|
||||
version = '';
|
||||
}
|
||||
|
||||
httpPost('status', data, function(data){
|
||||
if (data.data == 'start'){
|
||||
orPluginSetService(_name, true, version);
|
||||
} else {
|
||||
orPluginSetService(_name, false, version);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function orPluginSetService(_name ,status, version){
|
||||
var serviceCon ='<p class="status">当前状态:<span>'+(status ? '开启' : '关闭' )+
|
||||
'</span><span style="color: '+
|
||||
(status?'#20a53a;':'red;')+
|
||||
' margin-left: 3px;" class="glyphicon ' + (status?'glyphicon glyphicon-play':'glyphicon-pause')+'"></span></p><div class="sfm-opt">\
|
||||
<button class="btn btn-default btn-sm" onclick="orPluginOpService(\''+_name+'\',\''+(status?'stop':'start')+'\',\''+version+'\')">'+(status?'停止':'启动')+'</button>\
|
||||
<button class="btn btn-default btn-sm" onclick="orPluginOpService(\''+_name+'\',\'restart\',\''+version+'\',\'yes\')">重启</button>\
|
||||
<button class="btn btn-default btn-sm" onclick="orPluginOpService(\''+_name+'\',\'reload\',\''+version+'\')">重载配置</button>\
|
||||
</div>';
|
||||
$(".soft-man-con").html(serviceCon);
|
||||
}
|
||||
|
||||
|
||||
function orPluginOpService(a, b, v,request_callback) {
|
||||
|
||||
var c = "name=" + a + "&func=" + b;
|
||||
if(v != ''){
|
||||
c = c + '&version='+v;
|
||||
}
|
||||
|
||||
var d = "";
|
||||
|
||||
switch(b) {
|
||||
case "stop":d = '停止';break;
|
||||
case "start":d = '启动';break;
|
||||
case "restart":d = '重启';break;
|
||||
case "reload":d = '重载';break;
|
||||
}
|
||||
layer.confirm( msgTpl('您真的要{1}{2}{3}服务吗?', [d,a,v]), {icon:3,closeBtn: 2}, function() {
|
||||
httpPost('get_os',{},function(data){
|
||||
var rdata = $.parseJSON(data.data);
|
||||
if (!rdata['auth']){
|
||||
layer.prompt({title: '检查到权限不足,需要输入密码!', formType: 1},function(pwd, index){
|
||||
|
||||
layer.close(index);
|
||||
var data = {'pwd':pwd};
|
||||
c += '&args='+JSON.stringify(data);
|
||||
orPluginOpServiceOp(a,b,c,d,a,v,request_callback);
|
||||
});
|
||||
} else {
|
||||
orPluginOpServiceOp(a,b,c,d,a,v,request_callback);
|
||||
|
||||
}
|
||||
});
|
||||
})
|
||||
}
|
||||
|
||||
function orPluginOpServiceOp(a,b,c,d,a,v,request_callback){
|
||||
|
||||
var request_path = "/plugins/run";
|
||||
if (request_callback == 'yes'){
|
||||
request_path = "/plugins/callback";
|
||||
}
|
||||
|
||||
var e = layer.msg(msgTpl('正在{1}{2}{3}服务,请稍候...',[d,a,v]), {icon: 16,time: 0});
|
||||
$.post(request_path, c, function(g) {
|
||||
layer.close(e);
|
||||
|
||||
var f = g.data == 'ok' ? msgTpl('{1}{2}服务已{3}',[a,v,d]) : msgTpl('{1}{2}服务{3}失败!',[a,v,d]);
|
||||
layer.msg(f, {icon: g.data == 'ok' ? 1 : 2});
|
||||
|
||||
if( b != "reload" && g.data == 'ok' ) {
|
||||
if ( b == 'start' ) {
|
||||
orPluginSetService(a, true, v);
|
||||
} else if ( b == 'stop' ){
|
||||
orPluginSetService(a, false, v);
|
||||
}
|
||||
}
|
||||
|
||||
if( g.status && g.data != 'ok' ) {
|
||||
layer.msg(g.data, {icon: 2,time: 10000,shade: 0.3});
|
||||
}
|
||||
|
||||
},'json').error(function() {
|
||||
layer.close(e);
|
||||
layer.msg('操作异常!', {icon: 2});
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
// 定时器变量
|
||||
var httpdStatusTimer = null;
|
||||
|
||||
//查看Apache负载状态
|
||||
function getHttpdStatus() {
|
||||
$.post('/plugins/run', {name:'apache', func:'run_info'}, function(data) {
|
||||
try {
|
||||
var rdata = $.parseJSON(data.data);
|
||||
|
||||
console.log(rdata);
|
||||
if ('status' in rdata && !rdata.status){
|
||||
showMsg(rdata.msg, function(){}, null,3000);
|
||||
return;
|
||||
}
|
||||
|
||||
var con = "<div><table class='table table-hover table-bordered'>\
|
||||
<tr><th>服务器版本(ServerVersion)</th><td>" + (rdata.ServerVersion || '-') + "</td></tr>\
|
||||
<tr><th>服务器MPM(ServerMPM)</th><td>" + (rdata.ServerMPM || '-') + "</td></tr>\
|
||||
<tr><th>服务器构建时间(Server Built)</th><td>" + (rdata['Server Built'] || '-') + "</td></tr>\
|
||||
<tr><th>当前时间(CurrentTime)</th><td>" + (rdata.CurrentTime || '-') + "</td></tr>\
|
||||
<tr><th>重启时间(RestartTime)</th><td>" + (rdata.RestartTime || '-') + "</td></tr>\
|
||||
<tr><th>服务器运行时间(ServerUptime)</th><td>" + (rdata.ServerUptime || '-') + "</td></tr>\
|
||||
<tr><th>服务器运行秒数(ServerUptimeSeconds)</th><td>" + (rdata.ServerUptimeSeconds || '-') + "</td></tr>\
|
||||
<tr><th>1分钟负载(Load1)</th><td>" + (rdata.Load1 || '-') + "</td></tr>\
|
||||
<tr><th>5分钟负载(Load5)</th><td>" + (rdata.Load5 || '-') + "</td></tr>\
|
||||
<tr><th>15分钟负载(Load15)</th><td>" + (rdata.Load15 || '-') + "</td></tr>\
|
||||
<tr><th>总访问次数(Total Accesses)</th><td>" + (rdata['Total Accesses'] || '-') + "</td></tr>\
|
||||
<tr><th>总流量(Total kBytes)</th><td>" + (rdata['Total kBytes'] || '-') + " KB</td></tr>\
|
||||
<tr><th>总请求时间(Total Duration)</th><td>" + (rdata['Total Duration'] || '-') + "</td></tr>\
|
||||
<tr><th>CPU用户时间(CPUUser)</th><td>" + (rdata.CPUUser || '-') + "</td></tr>\
|
||||
<tr><th>CPU系统时间(CPUSystem)</th><td>" + (rdata.CPUSystem || '-') + "</td></tr>\
|
||||
<tr><th>CPU负载(CPULoad)</th><td>" + (rdata.CPULoad || '-') + "</td></tr>\
|
||||
<tr><th>每秒请求数(ReqPerSec)</th><td>" + (rdata.ReqPerSec || '-') + "</td></tr>\
|
||||
<tr><th>每秒流量(BytesPerSec)</th><td>" + (rdata.BytesPerSec || '-') + "</td></tr>\
|
||||
<tr><th>每请求流量(BytesPerReq)</th><td>" + (rdata.BytesPerReq || '-') + "</td></tr>\
|
||||
<tr><th>每请求时间(DurationPerReq)</th><td>" + (rdata.DurationPerReq || '-') + "</td></tr>\
|
||||
<tr><th>活动工作进程(BusyWorkers)</th><td>" + (rdata.BusyWorkers || '-') + "</td></tr>\
|
||||
<tr><th>优雅关闭进程(GracefulWorkers)</th><td>" + (rdata.GracefulWorkers || '-') + "</td></tr>\
|
||||
<tr><th>空闲工作进程(IdleWorkers)</th><td>" + (rdata.IdleWorkers || '-') + "</td></tr>\
|
||||
<tr><th>进程数(Processes)</th><td>" + (rdata.Processes || '-') + "</td></tr>\
|
||||
<tr><th>总连接数(ConnsTotal)</th><td>" + (rdata.ConnsTotal || '-') + "</td></tr>\
|
||||
</table></div>";
|
||||
$(".soft-man-con").html(con);
|
||||
}catch(err){
|
||||
showMsg(data.data, function(){}, null,3000);
|
||||
}
|
||||
},'json');
|
||||
}
|
||||
|
||||
// 启动自动刷新
|
||||
function startHttpdStatusAutoRefresh() {
|
||||
// 先清除现有的定时器
|
||||
if (httpdStatusTimer) {
|
||||
clearInterval(httpdStatusTimer);
|
||||
}
|
||||
|
||||
// 立即执行一次
|
||||
getHttpdStatus();
|
||||
|
||||
// 设置定时器,每5秒刷新一次
|
||||
httpdStatusTimer = setInterval(getHttpdStatus, 5000);
|
||||
}
|
||||
|
||||
// 停止自动刷新
|
||||
function stopHttpdStatusAutoRefresh() {
|
||||
if (httpdStatusTimer) {
|
||||
clearInterval(httpdStatusTimer);
|
||||
httpdStatusTimer = null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function setOpCfg(){
|
||||
httpPost('get_cfg', {}, function(data){
|
||||
var rdata = $.parseJSON(data.data);
|
||||
var rdata = rdata.data;
|
||||
// console.log(rdata);
|
||||
|
||||
var mlist = '';
|
||||
for (var i = 0; i < rdata.length; i++) {
|
||||
var w = '70'
|
||||
var ibody = '<input style="width: ' + w + 'px;" class="bt-input-text mr5" name="' + rdata[i].name + '" value="' + rdata[i].value + '" type="text" >';
|
||||
switch (rdata[i].type) {
|
||||
case 0:
|
||||
var selected_1 = (rdata[i].value == 1) ? 'selected' : '';
|
||||
var selected_0 = (rdata[i].value == 0) ? 'selected' : '';
|
||||
ibody = '<select class="bt-input-text mr5" name="' + rdata[i].name + '" style="width: ' + w + 'px;">\
|
||||
<option value="1" ' + selected_1 + '>开启</option>\
|
||||
<option value="0" ' + selected_0 + '>关闭</option>\
|
||||
</select>';
|
||||
break;
|
||||
case 1:
|
||||
var selected_1 = (rdata[i].value == 'on') ? 'selected' : '';
|
||||
var selected_0 = (rdata[i].value == 'off') ? 'selected' : '';
|
||||
ibody = '<select class="bt-input-text mr5" name="' + rdata[i].name + '" style="width: ' + w + 'px;">\
|
||||
<option value="on" ' + selected_1 + '>开启</option>\
|
||||
<option value="off" ' + selected_0 + '>关闭</option>\
|
||||
</select>';
|
||||
break;
|
||||
}
|
||||
mlist += '<p style="margin-top:15px;"><span>' + rdata[i].name + '</span>' + ibody + "<b class='unit c9'>"+rdata[i].unit+"</b>" +', <font class="c9">' + rdata[i].ps + '</font></p>';
|
||||
}
|
||||
var con = '<style>.conf_p p{margin-bottom: 2px}</style><div class="conf_p" style="margin-bottom:0">\
|
||||
' + mlist + '\
|
||||
<div style="margin-top:10px; padding-right:15px" class="text-right">\
|
||||
<button class="btn btn-success btn-sm mr5" onclick="setOpCfg()">刷新</button>\
|
||||
<button class="btn btn-success btn-sm" onclick="submitConf()">保存</button>\
|
||||
</div>\
|
||||
</div>'
|
||||
$(".soft-man-con").html(con);
|
||||
});
|
||||
}
|
||||
|
||||
function submitConf() {
|
||||
var data = {};
|
||||
|
||||
// 收集所有配置参数
|
||||
$("input[name]").each(function() {
|
||||
data[$(this).attr('name')] = $(this).val();
|
||||
});
|
||||
|
||||
$("select[name]").each(function() {
|
||||
data[$(this).attr('name')] = $(this).val() || 'on';
|
||||
});
|
||||
|
||||
// console.log(data);
|
||||
httpPost('set_cfg', data, function(rdata){
|
||||
var rdata = $.parseJSON(rdata.data);
|
||||
// console.log(rdata);
|
||||
layer.msg(rdata.msg, { icon: rdata.status ? 1 : 2 });
|
||||
});
|
||||
}
|
||||
|
||||
function otherFunc(){
|
||||
var con = '<p class="conf_p" style="text-align:center;">\
|
||||
<button class="btn btn-default btn-sm" onclick="cronAddCheck()">添加检查任务</button> \
|
||||
<button class="btn btn-default btn-sm" onclick="cronDelCheck()">删除检查任务</button>\
|
||||
</p>';
|
||||
$(".soft-man-con").html(con);
|
||||
}
|
||||
|
||||
function cronAddCheck(){
|
||||
httpPost('cron_add_check', {}, function(data){
|
||||
var rdata = $.parseJSON(data.data);
|
||||
layer.msg(rdata.msg, { icon: rdata.status ? 1 : 2 });
|
||||
});
|
||||
}
|
||||
|
||||
function cronDelCheck(){
|
||||
httpPost('cron_del_check', {}, function(data){
|
||||
var rdata = $.parseJSON(data.data);
|
||||
layer.msg(rdata.msg, { icon: rdata.status ? 1 : 2 });
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,124 +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 'apache'
|
||||
|
||||
|
||||
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": "minute-n",
|
||||
"where1": "3",
|
||||
"hour": "0",
|
||||
"minute": "0",
|
||||
}
|
||||
|
||||
|
||||
def createBgTask():
|
||||
removeBgTask()
|
||||
createBgTaskByName(getPluginName())
|
||||
|
||||
|
||||
def createBgTaskByName(name):
|
||||
args = getConfigData()
|
||||
_name = "[apache]检查任务"
|
||||
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
|
||||
''' % (mw_dir, name, getServerDir(), getPluginDir())
|
||||
cmd += 'echo "bash $script_path/check.sh"' + "\n"
|
||||
cmd += 'cd $mw_dir && bash $script_path/check.sh' + "\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["status"]:
|
||||
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()
|
||||
@@ -1,172 +0,0 @@
|
||||
#!/bin/bash
|
||||
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin:/opt/homebrew/bin
|
||||
export PATH
|
||||
|
||||
# cd /Users/midoks/Desktop/mwdev/server/mdserver-web/plugins/apache && bash install.sh install 2.4
|
||||
# cd /www/server/mdserver-web/plugins/apache && bash install.sh install 2.4
|
||||
|
||||
curPath=`pwd`
|
||||
rootPath=$(dirname "$curPath")
|
||||
rootPath=$(dirname "$rootPath")
|
||||
serverPath=$(dirname "$rootPath")
|
||||
|
||||
sysName=`uname`
|
||||
action=$1
|
||||
type=$2
|
||||
|
||||
VERSION=2.4.68
|
||||
apacheDir=${serverPath}/source/apache
|
||||
|
||||
Install_App()
|
||||
{
|
||||
if [ "${action}" = "install" ];then
|
||||
if [ -d $serverPath/apache ];then
|
||||
exit 0
|
||||
fi
|
||||
fi
|
||||
|
||||
# ----- cpu start ------
|
||||
if [ -z "${cpuCore}" ]; then
|
||||
cpuCore="1"
|
||||
fi
|
||||
|
||||
if [ -f /proc/cpuinfo ];then
|
||||
cpuCore=`cat /proc/cpuinfo | grep "processor" | wc -l`
|
||||
fi
|
||||
|
||||
MEM_INFO=$(free -m|grep Mem|awk '{printf("%.f",($2)/1024)}')
|
||||
if [ "${cpuCore}" != "1" ] && [ "${MEM_INFO}" != "0" ];then
|
||||
if [ "${cpuCore}" -gt "${MEM_INFO}" ];then
|
||||
cpuCore="${MEM_INFO}"
|
||||
fi
|
||||
else
|
||||
cpuCore="1"
|
||||
fi
|
||||
|
||||
if [ "$cpuCore" -gt "2" ];then
|
||||
cpuCore=`echo "$cpuCore" | awk '{printf("%.f",($1)*0.8)}'`
|
||||
else
|
||||
cpuCore="1"
|
||||
fi
|
||||
# ----- cpu end ------
|
||||
|
||||
mkdir -p ${apacheDir}
|
||||
echo 'install script ...'
|
||||
OPTIONS=''
|
||||
|
||||
# 安装 APR 和 APR-util
|
||||
APR_VERSION=1.7.6
|
||||
APR_UTIL_VERSION=1.6.3
|
||||
|
||||
if [ ! -f ${apacheDir}/apr-${APR_VERSION}.tar.bz2 ];then
|
||||
wget --no-check-certificate -O ${apacheDir}/apr-${APR_VERSION}.tar.bz2 https://downloads.apache.org/apr/apr-${APR_VERSION}.tar.bz2 -T 3
|
||||
fi
|
||||
|
||||
if [ ! -f ${apacheDir}/apr-util-${APR_UTIL_VERSION}.tar.bz2 ];then
|
||||
wget --no-check-certificate -O ${apacheDir}/apr-util-${APR_UTIL_VERSION}.tar.bz2 https://downloads.apache.org/apr/apr-util-${APR_UTIL_VERSION}.tar.bz2 -T 3
|
||||
fi
|
||||
|
||||
# 安装 APR
|
||||
if [ ! -d ${serverPath}/apache/apr ];then
|
||||
cd ${apacheDir} && tar -jxf apr-${APR_VERSION}.tar.bz2
|
||||
cd ${apacheDir}/apr-${APR_VERSION} && ./configure --prefix=${serverPath}/apache/apr
|
||||
make -j${cpuCore} && make install
|
||||
if [ "$?" = "0" ];then
|
||||
# 检查 APR 配置文件
|
||||
APR_CONFIG=$(find ${serverPath}/apache/apr -name "apr-*config" | head -1)
|
||||
if [ -z "$APR_CONFIG" ];then
|
||||
echo "APR installation failed: apr-config not found"
|
||||
exit 1
|
||||
fi
|
||||
echo "APR installed successfully: $APR_CONFIG"
|
||||
else
|
||||
echo "APR configure failed"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
# 安装 APR-util
|
||||
if [ ! -d ${serverPath}/apache/apr-util ];then
|
||||
cd ${apacheDir} && tar -jxf apr-util-${APR_UTIL_VERSION}.tar.bz2
|
||||
cd ${apacheDir}/apr-util-${APR_UTIL_VERSION} && ./configure --prefix=${serverPath}/apache/apr-util --with-apr=${serverPath}/apache/apr
|
||||
make -j${cpuCore} && make install
|
||||
if [ "$?" = "0" ];then
|
||||
# 检查 APR-util 配置文件
|
||||
APU_CONFIG=$(find ${serverPath}/apache/apr-util -name "apu-*config" | head -1)
|
||||
if [ -z "$APU_CONFIG" ];then
|
||||
echo "APR-util installation failed: apu-config not found"
|
||||
find ${serverPath}/apache/apr-util -name "apu-*config"
|
||||
exit 1
|
||||
fi
|
||||
echo "APR-util installed successfully: $APU_CONFIG"
|
||||
else
|
||||
echo "APR-util configure failed"
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ ! -f ${apacheDir}/httpd-${VERSION}.tar.bz2 ];then
|
||||
wget --no-check-certificate -O ${apacheDir}/httpd-${VERSION}.tar.bz2 https://downloads.apache.org/httpd/httpd-${VERSION}.tar.bz2 -T 3
|
||||
fi
|
||||
|
||||
if [ ! -d ${apacheDir}/httpd-${VERSION} ];then
|
||||
cd ${apacheDir} && tar -jxf ${apacheDir}/httpd-${VERSION}.tar.bz2
|
||||
fi
|
||||
|
||||
|
||||
# 检查 APR 和 APR-util 可执行文件
|
||||
APR_CONFIG=$(find ${serverPath}/apache/apr -name "apr-*config" | head -1)
|
||||
APU_CONFIG=$(find ${serverPath}/apache/apr-util -name "apu-*config" | head -1)
|
||||
|
||||
if [ -z "$APR_CONFIG" ];then
|
||||
echo "APR config not found"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if [ -z "$APU_CONFIG" ];then
|
||||
echo "APR-util config not found"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
OPTIONS="${OPTIONS} --with-apr=$APR_CONFIG"
|
||||
OPTIONS="${OPTIONS} --with-apr-util=$APU_CONFIG"
|
||||
|
||||
# if [ "$sysName" != "Darwin" ];then
|
||||
# OPTIONS="${OPTIONS} --enable-systemd"
|
||||
# fi
|
||||
# echo "cd ${apacheDir}/httpd-${VERSION} && ./configure --prefix=$serverPath/apache $OPTIONS"
|
||||
cd ${apacheDir}/httpd-${VERSION} && ./configure \
|
||||
--prefix=$serverPath/apache/httpd \
|
||||
$OPTIONS
|
||||
|
||||
make -j${cpuCore} && make install && make clean
|
||||
|
||||
|
||||
if [ -d ${apacheDir}/apr-${APR_VERSION} ];then
|
||||
rm -rf ${apacheDir}/apr-${APR_VERSION}
|
||||
fi
|
||||
|
||||
if [ -d ${apacheDir}/apr-util-${APR_UTIL_VERSION} ];then
|
||||
rm -rf ${apacheDir}/apr-util-${APR_UTIL_VERSION}
|
||||
fi
|
||||
|
||||
if [ -d ${apacheDir}/httpd-${APR_UTIL_VERSION} ];then
|
||||
rm -rf ${apacheDir}/httpd-${APR_UTIL_VERSION}
|
||||
fi
|
||||
|
||||
echo 'installation of apache completed'
|
||||
}
|
||||
|
||||
Uninstall_App()
|
||||
{
|
||||
echo 'uninstalling apache completed'
|
||||
}
|
||||
|
||||
action=$1
|
||||
if [ "${1}" = "install" ];then
|
||||
Install_App
|
||||
elif [ "${1}" = "upgrade" ];then
|
||||
Install_App
|
||||
else
|
||||
Uninstall_App
|
||||
fi
|
||||
@@ -1,424 +0,0 @@
|
||||
# coding:utf-8
|
||||
|
||||
'''
|
||||
doc: https://docs.python.org/zh-cn/3/library/ftplib.html
|
||||
'''
|
||||
|
||||
import sys
|
||||
import io
|
||||
import os
|
||||
import time
|
||||
import re
|
||||
import json
|
||||
|
||||
import paramiko
|
||||
import ftplib
|
||||
|
||||
web_dir = os.getcwd() + "/web"
|
||||
if os.path.exists(web_dir):
|
||||
sys.path.append(web_dir)
|
||||
os.chdir(web_dir)
|
||||
|
||||
import core.mw as mw
|
||||
|
||||
DEBUG = True
|
||||
BLOCK_SIZE = 1024 * 1024 * 2
|
||||
# BLOCK_SIZE = 50
|
||||
PROGRESS_FILE_NAME = "PROGRESS_FILE_NAME"
|
||||
|
||||
"""
|
||||
=============自定义异常===================
|
||||
"""
|
||||
|
||||
|
||||
class OsError(Exception):
|
||||
"""OS端异常"""
|
||||
|
||||
|
||||
class ObjectNotFound(OsError):
|
||||
"""对象不存在时抛出的异常"""
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
message = "文件对象不存在。"
|
||||
super(ObjectNotFound, self).__init__(message, *args, **kwargs)
|
||||
|
||||
|
||||
class APIError(Exception):
|
||||
"""API参数错误异常"""
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
_api_error_msg = 'API资料校验失败,请核实!'
|
||||
super(APIError, self).__init__(_api_error_msg, *args, **kwargs)
|
||||
|
||||
|
||||
class FtpPSClient:
|
||||
_title = "FTP"
|
||||
_name = "ftp"
|
||||
__host = None
|
||||
__port = None
|
||||
__user = None
|
||||
__password = None
|
||||
default_port = 21
|
||||
default_backup_path = "/backup/"
|
||||
config_file = "cfg.json"
|
||||
|
||||
def __init__(self, load_config=True, timeout=600):
|
||||
self.timeout = timeout
|
||||
if load_config:
|
||||
data = self.get_config()
|
||||
self.injection_config(data)
|
||||
|
||||
def get_config(self):
|
||||
default_config = {
|
||||
"ftp_host": '',
|
||||
"ftp_user": '',
|
||||
"ftp_pass": '',
|
||||
"backup_path": self.default_backup_path
|
||||
}
|
||||
|
||||
cfg = mw.getServerDir() + "/backup_ftp/" + self.config_file
|
||||
if os.path.exists(cfg):
|
||||
data = mw.readFile(cfg)
|
||||
return json.loads(data)
|
||||
else:
|
||||
return default_config
|
||||
|
||||
def injection_config(self, data):
|
||||
self.__host = host = data["ftp_host"].strip()
|
||||
if host.find(':') == -1:
|
||||
self.__port = self.default_port
|
||||
else:
|
||||
self.__host = host.split(':')[0]
|
||||
self.__port = host.split(':')[1]
|
||||
|
||||
self.__user = data['ftp_user'].strip()
|
||||
self.__password = data['ftp_pass'].strip()
|
||||
bp = data['backup_path'].strip()
|
||||
if bp:
|
||||
self.backup_path = self.getPath(bp)
|
||||
else:
|
||||
self.backup_path = self.getPath(self.default_backup_path)
|
||||
|
||||
def authorize(self):
|
||||
try:
|
||||
if self.timeout is not None:
|
||||
ftp = ftplib.FTP(timeout=self.timeout)
|
||||
else:
|
||||
ftp = ftplib.FTP()
|
||||
|
||||
debuglevel = 0
|
||||
# if DEBUG:
|
||||
# debuglevel = 3
|
||||
ftp.set_debuglevel(debuglevel)
|
||||
# ftp.set_pasv(True)
|
||||
ftp.connect(self.__host, int(self.__port))
|
||||
ftp.login(self.__user, self.__password)
|
||||
return ftp
|
||||
except Exception as e:
|
||||
raise OsError("无法连接FTP客户端,请检查配置参数是否正确!")
|
||||
|
||||
# 取目录路径
|
||||
def getPath(self, path):
|
||||
if path[-1:] != '/':
|
||||
path += '/'
|
||||
if path[:1] != '/':
|
||||
path = '/' + path
|
||||
return path.replace('//', '/')
|
||||
|
||||
def generateDownloadUrl(self, object_name):
|
||||
|
||||
return 'ftp://' + \
|
||||
self.__user + ':' + \
|
||||
self.__password + '@' + \
|
||||
self.__host + ':' + \
|
||||
"/" + object_name
|
||||
|
||||
def buildDirName(self, data_type, file_name):
|
||||
import re
|
||||
prefix_dict = {
|
||||
"site": "web",
|
||||
"database": "db",
|
||||
"path": "path",
|
||||
}
|
||||
file_regx = prefix_dict.get(data_type) + r"_(.+)_20\d+_\d+\."
|
||||
sub_search = re.search(file_regx, file_name)
|
||||
sub_path_name = ""
|
||||
if sub_search:
|
||||
sub_path_name = sub_search.groups()[0]
|
||||
sub_path_name += '/'
|
||||
|
||||
# 构建OS存储路径
|
||||
object_name = self.backup_path + \
|
||||
data_type + '/' + \
|
||||
sub_path_name + \
|
||||
file_name
|
||||
return object_name
|
||||
|
||||
def uploadFile(self, filename, data_type=None, *args, **kwargs):
|
||||
|
||||
client = self.authorize()
|
||||
|
||||
local_file_name = filename
|
||||
filename = os.path.abspath(filename)
|
||||
dirname = os.path.dirname(filename)
|
||||
temp_name = os.path.split(filename)[1]
|
||||
|
||||
object_name = self.buildDirName(data_type, temp_name)
|
||||
|
||||
upload_tmp_dir = os.path.join(dirname, ".upload_tmp")
|
||||
if not os.path.exists(upload_tmp_dir):
|
||||
os.mkdir(upload_tmp_dir)
|
||||
|
||||
print("|-正在上传文件到 {}".format(object_name))
|
||||
|
||||
total_bytes = os.path.getsize(filename)
|
||||
object_md5_name = mw.md5(object_name)
|
||||
pg_file = os.path.join(upload_tmp_dir, object_md5_name + ".pl")
|
||||
|
||||
block_size = BLOCK_SIZE
|
||||
if kwargs.get("block_size"):
|
||||
try:
|
||||
block_size = float(kwargs.get("block_size"))
|
||||
except:
|
||||
pass
|
||||
|
||||
remote_file_size = None
|
||||
if not os.path.exists(pg_file):
|
||||
# import uuid
|
||||
# uid = str(uuid.uuid1())
|
||||
progress_info = {
|
||||
"filename": local_file_name,
|
||||
"total_bytes": total_bytes,
|
||||
"uploaded_bytes": 0,
|
||||
}
|
||||
mw.writeFile(pg_file, json.dumps(progress_info))
|
||||
else:
|
||||
progress_info = json.loads(mw.readFile(pg_file))
|
||||
if total_bytes == progress_info.get("total_bytes"):
|
||||
# 取远程文件大小
|
||||
_max_loop = 10
|
||||
while _max_loop > 0:
|
||||
try:
|
||||
time.sleep(1)
|
||||
remote_file_size = client.size(object_name)
|
||||
if remote_file_size > total_bytes:
|
||||
remote_file_size = None
|
||||
break
|
||||
except Exception as e:
|
||||
if DEBUG:
|
||||
print(type(e))
|
||||
print(e)
|
||||
_max_loop -= 1
|
||||
else:
|
||||
remote_file_size = None
|
||||
|
||||
uploaded_bytes = 0 if remote_file_size is None else remote_file_size
|
||||
|
||||
dir_name = os.path.split(object_name)[0]
|
||||
if dir_name:
|
||||
self.createDirP(dir_name)
|
||||
|
||||
upload_start = time.time()
|
||||
|
||||
try:
|
||||
if total_bytes > 1024 * 1024 * 1024:
|
||||
with open(local_file_name, 'rb') as file_handler:
|
||||
if remote_file_size is not None:
|
||||
file_handler.seek(remote_file_size)
|
||||
|
||||
client.voidcmd("TYPE I")
|
||||
datasock = ''
|
||||
esize = ''
|
||||
|
||||
datasock, esize = client.ntransfercmd(
|
||||
"STOR " + object_name, remote_file_size)
|
||||
|
||||
while True:
|
||||
buf = file_handler.read(block_size)
|
||||
if not len(buf):
|
||||
break
|
||||
datasock.sendall(buf)
|
||||
uploaded_bytes += len(buf)
|
||||
if DEBUG:
|
||||
print('\ruploading %.2f%%' %
|
||||
(float(uploaded_bytes) / total_bytes * 100))
|
||||
|
||||
print("uploaded_bytes", uploaded_bytes)
|
||||
if uploaded_bytes == total_bytes:
|
||||
break
|
||||
datasock.close()
|
||||
|
||||
if DEBUG:
|
||||
print('close data handle')
|
||||
try:
|
||||
client.voidcmd('NOOP')
|
||||
except Exception as e:
|
||||
if DEBUG:
|
||||
print("Send NOOP command error:")
|
||||
print(e)
|
||||
else:
|
||||
if DEBUG:
|
||||
print('keep alive cmd success')
|
||||
client.voidresp()
|
||||
if DEBUG:
|
||||
print('No loop cmd')
|
||||
else:
|
||||
# 小于1G文件直接上传
|
||||
file_handler = open(local_file_name, "rb")
|
||||
client.storbinary('STOR %s' % object_name,
|
||||
file_handler, blocksize=block_size)
|
||||
file_handler.close()
|
||||
except Exception as e:
|
||||
print(str(e))
|
||||
|
||||
completed_file_size = None
|
||||
_max_loop = 10
|
||||
while _max_loop > 0:
|
||||
try:
|
||||
time.sleep(1)
|
||||
completed_file_size = client.size(object_name)
|
||||
break
|
||||
except Exception as e:
|
||||
_max_loop -= 1
|
||||
if DEBUG:
|
||||
print("size error:" + str(e))
|
||||
|
||||
# 上传完成
|
||||
if completed_file_size == total_bytes:
|
||||
if DEBUG:
|
||||
upload_completed = time.time()
|
||||
upload_diff = upload_completed - upload_start
|
||||
print("文件上传成功, 耗时: {}s。".format(upload_diff))
|
||||
if os.path.exists(pg_file):
|
||||
os.remove(pg_file)
|
||||
return True
|
||||
else:
|
||||
if os.path.exists(pg_file):
|
||||
os.remove(pg_file)
|
||||
print("文件上传后大小不一致!")
|
||||
|
||||
print("completed_file_size:" + str(completed_file_size))
|
||||
print("total_bytes:", total_bytes)
|
||||
print("object_md5_name:", object_md5_name)
|
||||
print("pg_file:", pg_file)
|
||||
print("filename:", filename)
|
||||
print("dirname:", dirname)
|
||||
print("object_name:", object_name)
|
||||
return False
|
||||
|
||||
def createDirP(self, dir_name):
|
||||
"""创建远程目录
|
||||
|
||||
:param dir_name: 目录名称
|
||||
:return:
|
||||
"""
|
||||
try:
|
||||
dirnames = dir_name.split('/')
|
||||
ftp = self.authorize()
|
||||
# ftp.cwd(get.path);
|
||||
for dirname in dirnames:
|
||||
if not dirname or not dirname.strip():
|
||||
continue
|
||||
try:
|
||||
flist = ftp.nlst()
|
||||
if not dirname in flist:
|
||||
ftp.mkd(dirname)
|
||||
except:
|
||||
# print("mlsd mode.")
|
||||
try:
|
||||
flist = list(ftp.mlsd())[1:]
|
||||
for f in flist:
|
||||
if dirname == f[0]:
|
||||
break
|
||||
else:
|
||||
ftp.mkd(dirname)
|
||||
except:
|
||||
return False
|
||||
ftp.cwd(dirname)
|
||||
return True
|
||||
except:
|
||||
return False
|
||||
|
||||
def createDir(self, path, name):
|
||||
ftp = self.authorize()
|
||||
path = self.getPath(path)
|
||||
ftp.cwd(path)
|
||||
try:
|
||||
ftp.mkd(name)
|
||||
ftp.close()
|
||||
return True
|
||||
except Exception as e:
|
||||
print(str(e))
|
||||
ftp.close()
|
||||
return False
|
||||
|
||||
def deleteDir(self, path, dir_name):
|
||||
try:
|
||||
ftp = self.authorize()
|
||||
ftp.rmd(dir_name)
|
||||
return True
|
||||
except ftplib.error_perm as e:
|
||||
print("deleteDir:" + str(e) + ":" + dir_name)
|
||||
except Exception as e:
|
||||
print(e)
|
||||
return False
|
||||
|
||||
def deleteFile(self, filename):
|
||||
try:
|
||||
ftp = self.authorize()
|
||||
ftp.delete(filename)
|
||||
return True
|
||||
except Exception as e:
|
||||
print(str(e))
|
||||
return False
|
||||
|
||||
def getList(self, path="/"):
|
||||
ftp = self.authorize()
|
||||
path = self.getPath(path)
|
||||
ftp.cwd(path)
|
||||
mlsd = False
|
||||
try:
|
||||
files = list(ftp.mlsd())
|
||||
mlsd = True
|
||||
except:
|
||||
try:
|
||||
files = ftp.nlst(path)
|
||||
mlsd = False
|
||||
except:
|
||||
raise RuntimeError("FTP服务器数据返回异常!")
|
||||
ftp.close()
|
||||
# print(files)
|
||||
f_list = []
|
||||
dirs = []
|
||||
data = []
|
||||
default_time = '1971/01/01 01:01:01'
|
||||
for dt in files:
|
||||
# print(dt)
|
||||
if mlsd:
|
||||
dt_name = dt[0]
|
||||
dt_info = dt[1]
|
||||
else:
|
||||
if dt.find("/") >= 0:
|
||||
dt = dt.split("/")[-1]
|
||||
tmp = {}
|
||||
tmp['name'] = dt_name
|
||||
if dt_name == '.' or dt_name == '..':
|
||||
continue
|
||||
|
||||
tmp['time'] = dt_info['modify']
|
||||
try:
|
||||
tmp['size'] = dt_info['size']
|
||||
tmp['type'] = "File"
|
||||
tmp['download'] = self.generateDownloadUrl(path + dt_name)
|
||||
f_list.append(tmp)
|
||||
except:
|
||||
tmp['size'] = dt_info['sizd']
|
||||
tmp['type'] = None
|
||||
tmp['download'] = ''
|
||||
dirs.append(tmp)
|
||||
data = dirs + f_list
|
||||
|
||||
mlist = {}
|
||||
mlist['path'] = path
|
||||
mlist['list'] = data
|
||||
return mlist
|
||||
|
Before Width: | Height: | Size: 1.5 KiB |
@@ -1,101 +0,0 @@
|
||||
<style>
|
||||
.upyunCon {
|
||||
height: 428px;
|
||||
}
|
||||
|
||||
.up-place {
|
||||
height: 62px;
|
||||
border-bottom: 1px solid #ddd;
|
||||
}
|
||||
|
||||
.up-place .btn {
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
.up-place .place-input {
|
||||
background-color: #f3f3f3;
|
||||
border: 1px solid #ccc;
|
||||
height: 30px;
|
||||
line-height: 28px;
|
||||
overflow: hidden;
|
||||
margin: 1px 0 0 -1px;
|
||||
width: 340px;
|
||||
}
|
||||
|
||||
.place-input ul {
|
||||
display: inline-block;
|
||||
position: relative;
|
||||
width: auto;
|
||||
}
|
||||
|
||||
.place-input ul li {
|
||||
background: url("/static/img/ico/ico-ltr.png") no-repeat right center;
|
||||
float: left;
|
||||
padding-left: 10px;
|
||||
padding-right: 18px;
|
||||
}
|
||||
|
||||
.place-input ul li a {
|
||||
height: 28px;
|
||||
cursor: pointer;
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.upyunlist {
|
||||
height: 516px;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.up-bottom {
|
||||
background-color: #fafafa;
|
||||
border-top: 1px solid #eee;
|
||||
bottom: 0;
|
||||
position: absolute;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.up-use {
|
||||
line-height: 50px
|
||||
}
|
||||
|
||||
.list-list .cursor span {
|
||||
line-height: 30px;
|
||||
}
|
||||
|
||||
.btn-title {
|
||||
margin-top: 1px
|
||||
}
|
||||
|
||||
.tip {
|
||||
font-size: 10px;
|
||||
font-style: oblique;
|
||||
color: green;
|
||||
}
|
||||
</style>
|
||||
<div class="upyunCon">
|
||||
<div class="up-place pd15">
|
||||
<button id="backBtn" class="btn btn-default btn-sm glyphicon glyphicon-arrow-left pull-left" title="后退"></button>
|
||||
<input id="myPath" style="display:none;" type="text" value="">
|
||||
<div class="place-input pull-left">
|
||||
<div style="width:1400px;height:28px"><ul></ul></div>
|
||||
</div>
|
||||
<button class="refreshBtn btn btn-default btn-sm glyphicon glyphicon-refresh pull-left mr20" title="刷新" style="margin-left:-1px;"></button>
|
||||
<button class="btn btn-default btn-sm pull-right btn-title" onclick="upyunApi()">帐户设置</button>
|
||||
<button class="btn btn-default btn-sm pull-right mr20 btn-title" onclick="createDir()">新建文件夹</button>
|
||||
</div>
|
||||
|
||||
<div class="upyunlist pd15">
|
||||
<div class="divtable" style="margin-bottom:15px">
|
||||
<table class="table table-hover">
|
||||
<thead><tr><th>名称</th><th>大小</th><th>更新时间</th><th class="text-right">操作</th></tr></thead>
|
||||
<tbody class="list-list"></tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script type="text/javascript">
|
||||
$.getScript( "/plugins/file?name=backup_ftp&f=js/backup_ftp.js", function(){
|
||||
osList('/');
|
||||
});
|
||||
</script>
|
||||
@@ -1,311 +0,0 @@
|
||||
# coding:utf-8
|
||||
|
||||
import sys
|
||||
import io
|
||||
import os
|
||||
import time
|
||||
import re
|
||||
import json
|
||||
|
||||
# print(sys.platform)
|
||||
if sys.platform != "darwin":
|
||||
os.chdir("/www/server/mdserver-web")
|
||||
|
||||
|
||||
web_dir = os.getcwd() + "/web"
|
||||
if os.path.exists(web_dir):
|
||||
sys.path.append(web_dir)
|
||||
os.chdir(web_dir)
|
||||
|
||||
import core.mw as mw
|
||||
import core.db as db
|
||||
|
||||
app_debug = False
|
||||
if mw.isAppleSystem():
|
||||
app_debug = True
|
||||
|
||||
|
||||
def getPluginName():
|
||||
return 'backup_ftp'
|
||||
|
||||
|
||||
def getPluginDir():
|
||||
return mw.getPluginDir() + '/' + getPluginName()
|
||||
|
||||
|
||||
sys.path.append(getPluginDir() + "/class")
|
||||
from ftp_client import FtpPSClient
|
||||
|
||||
|
||||
def getServerDir():
|
||||
return mw.getServerDir() + '/' + getPluginName()
|
||||
|
||||
|
||||
def getArgs():
|
||||
args = sys.argv[2:]
|
||||
tmp = {}
|
||||
args_len = len(args)
|
||||
|
||||
if args_len == 1:
|
||||
t = args[0].strip('{').strip('}')
|
||||
t = t.split(':')
|
||||
tmp[t[0]] = t[1]
|
||||
elif args_len > 1:
|
||||
for i in range(len(args)):
|
||||
t = args[i].split(':', 1)
|
||||
tmp[t[0]] = t[1]
|
||||
return tmp
|
||||
|
||||
|
||||
def checkArgs(data, ck=[]):
|
||||
for i in range(len(ck)):
|
||||
if not ck[i] in data:
|
||||
return (False, mw.returnJson(False, '参数:(' + ck[i] + ')没有!'))
|
||||
return (True, mw.returnJson(True, 'ok'))
|
||||
|
||||
|
||||
def status():
|
||||
return 'start'
|
||||
|
||||
|
||||
def getConf():
|
||||
cfg = getServerDir() + "/cfg.json"
|
||||
if not os.path.exists(cfg):
|
||||
rdata = {
|
||||
'use_sftp':False,
|
||||
}
|
||||
return mw.returnJson(False, "未配置", rdata)
|
||||
data = mw.readFile(cfg)
|
||||
data = json.loads(data)
|
||||
return mw.returnJson(True, "OK", data)
|
||||
|
||||
|
||||
def setConf():
|
||||
args = getArgs()
|
||||
data = checkArgs(args, ['use_sftp', 'ftp_user',
|
||||
'ftp_pass', 'ftp_host', 'backup_path'])
|
||||
if not data[0]:
|
||||
return data[1]
|
||||
|
||||
cfg = getServerDir() + "/cfg.json"
|
||||
|
||||
values = ['ftp_user',
|
||||
'ftp_pass',
|
||||
'ftp_host']
|
||||
for v in values:
|
||||
if args[v] == '':
|
||||
return mw.returnJson(False, '必填资料不能为空,请核实!', [])
|
||||
|
||||
if args['backup_path'] == '':
|
||||
args['backup_path'] = "/backup"
|
||||
|
||||
try:
|
||||
ftp = FtpPSClient(load_config=False)
|
||||
ftp.injection_config(args)
|
||||
data = ftp.getList("/")
|
||||
if data:
|
||||
mw.writeFile(cfg, mw.getJson(args))
|
||||
return mw.returnJson(True, '设置成功', [])
|
||||
except Exception as e:
|
||||
return mw.returnJson(False, "FTP校验失败,请核实!\n" + str(e), [])
|
||||
|
||||
|
||||
def getList():
|
||||
cfg = getServerDir() + "/cfg.json"
|
||||
if not os.path.exists(cfg):
|
||||
return mw.returnJson(False, "未配置FTP,请点击`账户设置`", [])
|
||||
|
||||
args = getArgs()
|
||||
data = checkArgs(args, ['path'])
|
||||
if not data[0]:
|
||||
return data[1]
|
||||
|
||||
try:
|
||||
ftp = FtpPSClient()
|
||||
flist = ftp.getList(args['path'])
|
||||
return mw.returnJson(True, "ok", flist)
|
||||
except Exception as e:
|
||||
return mw.returnJson(False, str(e), [])
|
||||
|
||||
|
||||
def createDir():
|
||||
cfg = getServerDir() + "/cfg.json"
|
||||
if not os.path.exists(cfg):
|
||||
return mw.returnJson(False, "未配置FTP,请点击`账户设置`", [])
|
||||
|
||||
args = getArgs()
|
||||
data = checkArgs(args, ['path', 'name'])
|
||||
if not data[0]:
|
||||
return data[1]
|
||||
|
||||
ftp = FtpPSClient()
|
||||
isok = ftp.createDir(args['path'], args['name'])
|
||||
if isok:
|
||||
return mw.returnJson(True, "创建成功")
|
||||
return mw.returnJson(False, "创建失败")
|
||||
|
||||
|
||||
def deleteDir():
|
||||
args = getArgs()
|
||||
data = checkArgs(args, ['dir_name', 'path'])
|
||||
if not data[0]:
|
||||
return data[1]
|
||||
|
||||
ftp = FtpPSClient()
|
||||
isok = ftp.deleteDir(args['path'], args['dir_name'])
|
||||
if isok:
|
||||
return mw.returnJson(True, "删除成功")
|
||||
return mw.returnJson(False, "删除失败")
|
||||
|
||||
|
||||
def deleteFile():
|
||||
args = getArgs()
|
||||
data = checkArgs(args, ['path', 'filename'])
|
||||
if not data[0]:
|
||||
return data[1]
|
||||
|
||||
ftp = FtpPSClient()
|
||||
isok = ftp.deleteFile(args['path'] + "/" + args['filename'])
|
||||
if isok:
|
||||
return mw.returnJson(True, "删除成功")
|
||||
return mw.returnJson(False, "删除失败")
|
||||
|
||||
def findPathName(path, filename):
|
||||
f = os.scandir(path)
|
||||
l = []
|
||||
for ff in f:
|
||||
t = {}
|
||||
if ff.name.find(filename) > -1:
|
||||
t['filename'] = path + '/' + ff.name
|
||||
l.append(t)
|
||||
return l
|
||||
|
||||
def backupAllFunc(stype):
|
||||
|
||||
name = sys.argv[2]
|
||||
num = sys.argv[3]
|
||||
|
||||
prefix_dict = {
|
||||
"site": "web",
|
||||
"database": "db",
|
||||
"path": "path",
|
||||
}
|
||||
|
||||
backups = []
|
||||
# print("stype:", stype)
|
||||
# 提前获取-清理多余备份
|
||||
if stype == 'site':
|
||||
pid = mw.M('sites').where('name=?', (name,)).getField('id')
|
||||
backups = mw.M('backup').where('type=? and pid=?', ('0', pid)).field('id,filename').select()
|
||||
if stype == 'database':
|
||||
db_path = mw.getServerDir() + '/mysql'
|
||||
pid = mw.M('databases').dbPos(db_path, 'mysql').where('name=?', (name,)).getField('id')
|
||||
backups = mw.M('backup').where('type=? and pid=?', ('1', pid)).field('id,filename').select()
|
||||
if stype == 'path':
|
||||
backup_dir = mw.getBackupDir()
|
||||
backup_path = backup_dir + '/path'
|
||||
_name = 'path_{}'.format(os.path.basename(name))
|
||||
backups = findPathName(backup_path, _name)
|
||||
|
||||
# 其他类型关系性数据库(mysql类的)
|
||||
if stype.find('database_') > -1:
|
||||
plugin_name = stype.replace('database_', '')
|
||||
db_path = mw.getServerDir() + '/' + plugin_name
|
||||
pid = mw.M('databases').dbPos(db_path, 'mysql').where('name=?', (name,)).getField('id')
|
||||
backups = mw.M('backup').where('type=? and pid=?', ('1', pid)).field('id,filename').select()
|
||||
|
||||
args = stype + " " + name + " " + num
|
||||
cmd = 'python3 ' + mw.getPanelDir() + '/scripts/backup.py ' + args
|
||||
if stype.find('database_') > -1:
|
||||
plugin_name = stype.replace('database_', '')
|
||||
args = "database " + name + " " + num
|
||||
cmd = 'python3 ' + mw.getPanelDir() + '/plugins/' + plugin_name + '/scripts/backup.py ' + args
|
||||
|
||||
os.system(cmd)
|
||||
|
||||
# 开始执行上传信息.
|
||||
|
||||
if stype.find('database_') > -1:
|
||||
bk_name = 'database'
|
||||
plugin_name = stype.replace('database_', '')
|
||||
bk_prefix = plugin_name + '/db'
|
||||
stype = 'database'
|
||||
else:
|
||||
bk_prefix = prefix_dict[stype]
|
||||
bk_name = stype
|
||||
|
||||
find_path = mw.getBackupDir() + '/' + bk_name + '/' + bk_prefix + '_' + name
|
||||
if stype == 'path':
|
||||
_name = 'path_{}'.format(os.path.basename(name))
|
||||
find_path = mw.getBackupDir() + '/path/'+_name
|
||||
|
||||
find_new_file = "ls " + find_path + "_* | grep '.gz' | cut -d \\ -f 1 | awk 'END {print}'"
|
||||
|
||||
filename = mw.execShell(find_new_file)[0].strip()
|
||||
if filename == "":
|
||||
mw.echoInfo("not find upload file!")
|
||||
return ''
|
||||
|
||||
mw.echoInfo("准备上传文件 {}".format(filename))
|
||||
ftp = FtpPSClient()
|
||||
ftp.uploadFile(filename, stype)
|
||||
|
||||
# print(backups)
|
||||
backups = sorted(backups, key=lambda x: x['filename'], reverse=False)
|
||||
mw.echoStart('开始删除远程备份')
|
||||
num = int(num)
|
||||
sep = len(backups) - num
|
||||
if sep > -1:
|
||||
for backup in backups:
|
||||
fn = os.path.basename(backup['filename'])
|
||||
object_name = ftp.buildDirName(stype, fn)
|
||||
ftp.deleteFile(object_name)
|
||||
mw.echoInfo("已清理远程过期备份文件:" + object_name)
|
||||
sep -= 1
|
||||
if sep < 0:
|
||||
break
|
||||
mw.echoEnd('结束删除远程备份')
|
||||
|
||||
return ''
|
||||
|
||||
|
||||
def in_array(name, arr=[]):
|
||||
for x in arr:
|
||||
if name == x:
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def installPreInspection():
|
||||
return 'ok'
|
||||
|
||||
if __name__ == "__main__":
|
||||
func = sys.argv[1]
|
||||
if func == 'status':
|
||||
print(status())
|
||||
elif func == 'start':
|
||||
print(start())
|
||||
elif func == 'stop':
|
||||
print(stop())
|
||||
elif func == 'restart':
|
||||
print(restart())
|
||||
elif func == 'reload':
|
||||
print(reload())
|
||||
elif func == 'install_pre_inspection':
|
||||
print(installPreInspection())
|
||||
elif func == 'conf':
|
||||
print(getConf())
|
||||
elif func == 'set_config':
|
||||
print(setConf())
|
||||
elif func == "get_list":
|
||||
print(getList())
|
||||
elif func == "create_dir":
|
||||
print(createDir())
|
||||
elif func == "delete_dir":
|
||||
print(deleteDir())
|
||||
elif func == 'delete_file':
|
||||
print(deleteFile())
|
||||
elif in_array(func, ['site', 'database', 'path']) or func.find('database_') > -1:
|
||||
print(backupAllFunc(func))
|
||||
else:
|
||||
print('error')
|
||||
@@ -1,18 +0,0 @@
|
||||
{
|
||||
"sort":6,
|
||||
"title":"FTP存储空间",
|
||||
"hook":["backup"],
|
||||
"tip":"soft",
|
||||
"name":"backup_ftp",
|
||||
"type":"运行环境",
|
||||
"ps":"将网站或数据库打包备份到FTP存储空间",
|
||||
"versions":["1.0"],
|
||||
"install_pre_inspection":false,
|
||||
"shell":"install.sh",
|
||||
"checks":"server/backup_ftp",
|
||||
"path": "server/backup_ftp",
|
||||
"author":"midoks",
|
||||
"home":"https://github.com/midoks/mdserver-web",
|
||||
"date":"2022-10-23",
|
||||
"pid": "5"
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
#!/bin/bash
|
||||
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
|
||||
export PATH
|
||||
|
||||
curPath=`pwd`
|
||||
rootPath=$(dirname "$curPath")
|
||||
rootPath=$(dirname "$rootPath")
|
||||
serverPath=$(dirname "$rootPath")
|
||||
|
||||
sys_os=`uname`
|
||||
|
||||
|
||||
Install_App()
|
||||
{
|
||||
mkdir -p ${serverPath}/backup_ftp
|
||||
echo "${1}" > ${serverPath}/backup_ftp/version.pl
|
||||
echo '安装完成'
|
||||
|
||||
}
|
||||
|
||||
Uninstall_App()
|
||||
{
|
||||
rm -rf ${serverPath}/backup_ftp
|
||||
}
|
||||
|
||||
action=$1
|
||||
if [ "${1}" = 'install' ];then
|
||||
Install_App $2
|
||||
else
|
||||
Uninstall_App $2
|
||||
fi
|
||||
@@ -1,258 +0,0 @@
|
||||
|
||||
|
||||
function bkfPost(method,args,callback){
|
||||
var _args = null;
|
||||
if (typeof(args) == 'string'){
|
||||
_args = JSON.stringify(toArrayObject(args));
|
||||
} else {
|
||||
_args = JSON.stringify(args);
|
||||
}
|
||||
|
||||
var loadT = layer.msg('正在获取...', { icon: 16, time: 0, shade: 0.3 });
|
||||
$.post('/plugins/run', {name:'backup_ftp', func:method, args:_args}, function(data) {
|
||||
layer.close(loadT);
|
||||
if (!data.status){
|
||||
layer.msg(data.msg,{icon:0,time:2000,shade: [0.3, '#000']});
|
||||
return;
|
||||
}
|
||||
|
||||
if(typeof(callback) == 'function'){
|
||||
callback(data);
|
||||
}
|
||||
},'json');
|
||||
}
|
||||
|
||||
function getFtpLocalTime(data){
|
||||
var str = data.slice(0,4)+"/"+data.slice(4,6)+"/"+data.slice(6,8)
|
||||
+ " " + data.slice(8,10)+":"+data.slice(10,12)+":"+data.slice(12,14);
|
||||
return str;
|
||||
}
|
||||
|
||||
// 自定义部分
|
||||
var i = null;
|
||||
//设置API
|
||||
function upyunApi(){
|
||||
|
||||
bkfPost('conf', {}, function(rdata){
|
||||
var rdata = $.parseJSON(rdata.data);
|
||||
var token = rdata.data;
|
||||
var check_status = token.use_sftp;
|
||||
var sftp_checked = check_status === "true" ? " checked=\"checked\"" : "";
|
||||
|
||||
if (typeof(token.ftp_host) == 'undefined'){
|
||||
token.ftp_host = '';
|
||||
}
|
||||
|
||||
if (typeof(token.ftp_user) == 'undefined'){
|
||||
token.ftp_user = '';
|
||||
}
|
||||
|
||||
if (typeof(token.ftp_pass) == 'undefined'){
|
||||
token.ftp_pass = '';
|
||||
}
|
||||
|
||||
if (typeof(token.backup_path) == 'undefined'){
|
||||
token.backup_path = '';
|
||||
}
|
||||
|
||||
var apicon = '<div class="bingfa mtb15" style="padding-bottom:0px;">\
|
||||
<p>\
|
||||
<span class="span_tit">使用SFTP:</span>\ <input style="width: 20px; vertical-align:middle;" type="checkbox" name="use_sftp"'+sftp_checked+'> 是否使用SFTP进行数据传输 \
|
||||
</p>\
|
||||
<p>\
|
||||
<span class="span_tit">Host:</span>\
|
||||
<input placeholder="请输入主机地址" style="width: 200px;" type="text" name="upyun_service" value="'+token.ftp_host+'"> *服务器地址,FTP默认端口21, SFTP默认端口22\
|
||||
</p>\
|
||||
<p>\
|
||||
<span class="span_tit">用户名:</span>\
|
||||
<input style="width: 200px;" type="text" name="ftp_username" value="'+token.ftp_user+'"> *指定用户名\
|
||||
</p>\
|
||||
<p>\
|
||||
<span class="span_tit">密码:</span>\
|
||||
<input style="width: 200px;" type="password" name="ftp_password" value="'+token.ftp_pass+'"> *登录密码\
|
||||
</p>\
|
||||
<p>\
|
||||
<span class="span_tit">存储位置:</span>\
|
||||
<input placeholder="请输入存储位置" style="width: 200px;" type="text" name="backup_path" value="'+token.backup_path+'"> *相对于根目录的路径,默认是/backup\
|
||||
</p>\
|
||||
</div>';
|
||||
layer.open({
|
||||
type: 1,
|
||||
area: "600px",
|
||||
title: "FTP/SFTP帐户设置",
|
||||
closeBtn: 1,
|
||||
shift: 5,
|
||||
shadeClose: false,
|
||||
btn: ['确定','取消'],
|
||||
content:apicon,
|
||||
yes:function(index,layero){
|
||||
var data = {
|
||||
use_sftp:$("input[name='use_sftp']").prop('checked'),
|
||||
ftp_user:$("input[name='ftp_username']").val(),
|
||||
ftp_pass:$("input[name='ftp_password']").val(),
|
||||
ftp_host:$("input[name='upyun_service']").val(),
|
||||
backup_path:$("input[name='backup_path']").val()
|
||||
}
|
||||
bkfPost('set_config', data, function(rdata){
|
||||
var rdata = $.parseJSON(rdata.data);
|
||||
if (rdata.status){
|
||||
showMsg(rdata.msg,function(){
|
||||
layer.close(index);
|
||||
osList("/");
|
||||
},{icon:1},2000);
|
||||
} else{
|
||||
layer.msg(rdata.msg,{icon:2});
|
||||
}
|
||||
})
|
||||
},
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
function createDir(){
|
||||
layer.open({
|
||||
type: 1,
|
||||
area: "400px",
|
||||
title: "创建目录",
|
||||
closeBtn: 1,
|
||||
shift: 5,
|
||||
shadeClose: false,
|
||||
btn: ['确定','取消'],
|
||||
content:'<div class="bingfa bt-form c6" style="padding-bottom: 10px;">\
|
||||
<p>\
|
||||
<span class="span_tit">目录名称:</span>\
|
||||
<input style="width: 200px;" type="text" name="newPath" value="">\
|
||||
</p>\
|
||||
</div>',
|
||||
success:function(){
|
||||
$("input[name='newPath']").focus().keyup(function(e){
|
||||
if(e.keyCode == 13) $(".layui-layer-btn0").click();
|
||||
});
|
||||
},
|
||||
yes:function(index,layero){
|
||||
var name = $("input[name='newPath']").val();
|
||||
if(name == ''){
|
||||
layer.msg('目录名称不能为空!',{icon:2});
|
||||
return;
|
||||
}
|
||||
var path = $("#myPath").val();
|
||||
var dirname = name;
|
||||
// var loadT = layer.msg('正在创建目录['+dirname+']...',{icon:16,time:0,shade: [0.3, '#000']});
|
||||
bkfPost('create_dir', {path:path,name:dirname}, function(data){
|
||||
var rdata = $.parseJSON(data.data);
|
||||
if(rdata.status) {
|
||||
showMsg(rdata.msg, function(){
|
||||
layer.close(index);
|
||||
osList(path);
|
||||
} ,{icon:1}, 2000);
|
||||
} else{
|
||||
layer.msg(rdata.msg,{icon:2});
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
//删除文件
|
||||
function deleteFile(name, is_dir){
|
||||
if (is_dir === false){
|
||||
safeMessage('删除文件','删除后将无法恢复,真的要删除['+name+']吗?',function(){
|
||||
var path = $("#myPath").val();
|
||||
var filename = name;
|
||||
bkfPost('delete_file', {filename:filename,path:path}, function(rdata){
|
||||
var rdata = $.parseJSON(rdata.data);
|
||||
showMsg(rdata.msg,function(){
|
||||
osList(path);
|
||||
},{icon:rdata.status?1:2},2000);
|
||||
});
|
||||
});
|
||||
} else {
|
||||
safeMessage('删除文件夹','删除后将无法恢复,真的要删除['+name+']吗?',function(){
|
||||
var path = $("#myPath").val();
|
||||
bkfPost('delete_dir', {dir_name:name,path:path}, function(rdata){
|
||||
var rdata = $.parseJSON(rdata.data);
|
||||
showMsg(rdata.msg,function(){
|
||||
osList(path);
|
||||
},{icon:rdata.status?1:2},2000);
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function osList(path){
|
||||
bkfPost('get_list', {path:path}, function(rdata){
|
||||
|
||||
var rdata = $.parseJSON(rdata.data);
|
||||
if(rdata.status === false){
|
||||
showMsg(rdata.msg,function(){
|
||||
upyunApi();
|
||||
},{icon:2},2000);
|
||||
return;
|
||||
}
|
||||
|
||||
var mlist = rdata.data;
|
||||
// console.log(mlist);
|
||||
var listBody = ''
|
||||
var listFiles = ''
|
||||
for(var i=0;i<mlist.list.length;i++){
|
||||
if(mlist.list[i].type == null){
|
||||
listBody += '<tr><td class="cursor" onclick="osList(\''+(path+'/'+mlist.list[i].name).replace('//','/')+'\')"><span class="ico ico-folder"></span>\<span>'+mlist.list[i].name+'</span></td>\
|
||||
<td>-</td>\
|
||||
<td>-</td>\
|
||||
<td class="text-right"><a class="btlink" onclick="deleteFile(\''+mlist.list[i].name+'\', true)">删除</a></td></tr>'
|
||||
}else{
|
||||
listFiles += '<tr><td class="cursor"><span class="ico ico-file"></span>\<span>'+mlist.list[i].name+'</span></td>\
|
||||
<td>'+toSize(mlist.list[i].size)+'</td>\
|
||||
<td>'+getFtpLocalTime(mlist.list[i].time)+'</td>\
|
||||
<td class="text-right"><a target="_blank" href="'+mlist.list[i].download+'" class="btlink">下载</a> | <a class="btlink" onclick="deleteFile(\''+mlist.list[i].name+'\', false)">删除</a></td></tr>'
|
||||
}
|
||||
}
|
||||
listBody += listFiles;
|
||||
|
||||
var pathLi='';
|
||||
var tmp = path.split('/')
|
||||
var pathname = '';
|
||||
var n = 0;
|
||||
for(var i=0;i<tmp.length;i++){
|
||||
if(n > 0 && tmp[i] == '') continue;
|
||||
var dirname = tmp[i];
|
||||
if(dirname == '') {
|
||||
dirname = '根目录';
|
||||
n++;
|
||||
}
|
||||
pathname += '/' + tmp[i];
|
||||
pathname = pathname.replace('//','/');
|
||||
pathLi += '<li><a title="'+pathname+'" onclick="osList(\''+pathname+'\')">'+dirname+'</a></li>';
|
||||
}
|
||||
var um = 1;
|
||||
if(tmp[tmp.length-1] == '') um = 2;
|
||||
var backPath = tmp.slice(0,tmp.length-um).join('/') || '/';
|
||||
$('#myPath').val(path);
|
||||
$(".upyunCon .place-input ul").html(pathLi);
|
||||
$(".upyunlist .list-list").html(listBody);
|
||||
|
||||
upPathLeft();
|
||||
|
||||
$('#backBtn').unbind().click(function() {
|
||||
osList(backPath);
|
||||
});
|
||||
|
||||
$('.upyunCon .refreshBtn').unbind().click(function(){
|
||||
osList(path);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
//计算当前目录偏移
|
||||
function upPathLeft(){
|
||||
var UlWidth = $(".place-input ul").width();
|
||||
var SpanPathWidth = $(".place-input").width() - 20;
|
||||
var Ml = UlWidth - SpanPathWidth;
|
||||
if(UlWidth > SpanPathWidth ){
|
||||
$(".place-input ul").css("left",-Ml)
|
||||
}
|
||||
else{
|
||||
$(".place-input ul").css("left",0)
|
||||
}
|
||||
}
|
||||
// $('.layui-layer-page').css('height','670px');
|
||||
@@ -1,35 +0,0 @@
|
||||
#!/bin/bash
|
||||
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin:/opt/homebrew/bin
|
||||
export PATH
|
||||
|
||||
# OpenResty服务名称
|
||||
service_name="caddy"
|
||||
|
||||
# 检查OpenResty是否正在运行
|
||||
if systemctl is-active --quiet "$service_name"; then
|
||||
# 检查是否存在僵尸进程
|
||||
zombie_processes=$(ps -ef | grep -i caddy | grep -v grep | awk '{print $2}' | xargs ps -o state= -p 2>/dev/null | grep -c Z)
|
||||
if [ "$zombie_processes" -gt 0 ]; then
|
||||
echo "kill caddy 僵尸进程"
|
||||
ps -ef|grep caddy| grep -v grep| awk '{print $2}' | xargs kill -9
|
||||
echo "检测到OpenResty僵尸进程,正在重启服务..."
|
||||
systemctl restart "$service_name"
|
||||
echo "服务已重启"
|
||||
else
|
||||
echo "OpenResty运行正常"
|
||||
fi
|
||||
else
|
||||
echo "kill caddy"
|
||||
ps -ef|grep caddy| grep -v grep| awk '{print $2}' | xargs kill -9
|
||||
echo "caddy未运行,正在启动服务..."
|
||||
systemctl start "$service_name"
|
||||
echo "服务已启动"
|
||||
fi
|
||||
|
||||
NGINX_IDS=`ps -ef|grep caddy | grep -v grep| awk '{print $2}'`
|
||||
if [ "$NGINX_IDS" = "" ];then
|
||||
ps -ef|grep caddy| grep -v grep| awk '{print $2}' | xargs kill -9
|
||||
systemctl start "$service_name"
|
||||
echo "caddy未运行,正在启动服务..."
|
||||
fi
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
:8080 {
|
||||
# Set this path to your site's directory.
|
||||
root * /tmp
|
||||
|
||||
# Enable the static file server.
|
||||
file_server
|
||||
|
||||
# Another common task is to set up a reverse proxy:
|
||||
# reverse_proxy localhost:8080
|
||||
|
||||
# Or serve a PHP site through php-fpm:
|
||||
# php_fastcgi localhost:9000
|
||||
}
|
||||
|
Before Width: | Height: | Size: 3.5 KiB |
@@ -1,24 +0,0 @@
|
||||
<div class="bt-form">
|
||||
<!-- <div class='plugin_version'></div> -->
|
||||
<div class="bt-w-main">
|
||||
<div class="bt-w-menu">
|
||||
<p class="bgw" onclick="orPluginService('caddy',$('.plugin_version').attr('version'));">服务</p>
|
||||
<p onclick="pluginInitD('caddy', $('.plugin_version').attr('version'));">自启动</p>
|
||||
<p onclick="pluginConfig('caddy');">配置修改</p>
|
||||
<p onclick="getOpStatus();">负载状态</p>
|
||||
<p onclick="setOpCfg();">性能调整</p>
|
||||
<p onclick="pluginLogs('caddy');">错误日志</p>
|
||||
<p onclick="otherFunc();">维护功能</p>
|
||||
</div>
|
||||
<div class="bt-w-con pd15">
|
||||
<div class="soft-man-con"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<script type="text/javascript">
|
||||
$.getScript( "/plugins/file?name=caddy&f=js/caddy.js", function(){
|
||||
orPluginService('caddy');
|
||||
});
|
||||
</script>
|
||||
@@ -1,445 +0,0 @@
|
||||
# coding:utf-8
|
||||
|
||||
import sys
|
||||
import io
|
||||
import os
|
||||
import time
|
||||
import threading
|
||||
import subprocess
|
||||
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 'caddy'
|
||||
|
||||
|
||||
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 getArgs():
|
||||
args = sys.argv[2:]
|
||||
# print(args)
|
||||
tmp = {}
|
||||
args_len = len(args)
|
||||
|
||||
if args_len == 1:
|
||||
t = args[0].strip('{').strip('}')
|
||||
t = t.split(':',2)
|
||||
tmp[t[0]] = t[1]
|
||||
elif args_len > 1:
|
||||
for i in range(len(args)):
|
||||
t = args[i].split(':',2)
|
||||
tmp[t[0]] = t[1]
|
||||
# print(tmp)
|
||||
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 getOs():
|
||||
data = {}
|
||||
data['os'] = mw.getOs()
|
||||
data['auth'] = True
|
||||
return mw.getJson(data)
|
||||
|
||||
def getConf():
|
||||
path = getServerDir() + "/Caddyfile"
|
||||
return path
|
||||
|
||||
|
||||
def getConfTpl():
|
||||
path = getPluginDir() + '/conf/Caddyfile'
|
||||
return path
|
||||
|
||||
|
||||
def getInitDTpl():
|
||||
path = getPluginDir() + "/init.d/caddy.tpl"
|
||||
return path
|
||||
|
||||
def initDreplace():
|
||||
file_tpl = getInitDTpl()
|
||||
service_path = mw.getServerDir()
|
||||
initD_path = getServerDir() + '/init.d'
|
||||
|
||||
# init.d
|
||||
file_bin = initD_path + '/' + getPluginName()
|
||||
|
||||
if not os.path.exists(initD_path):
|
||||
os.mkdir(initD_path)
|
||||
|
||||
# initd replace
|
||||
content = mw.readFile(file_tpl)
|
||||
content = content.replace('{$SERVER_PATH}', service_path)
|
||||
mw.writeFile(file_bin, content)
|
||||
mw.execShell('chmod +x ' + file_bin)
|
||||
|
||||
caddy_file = getConf()
|
||||
if not os.path.exists(caddy_file):
|
||||
caddy_file_tpl = getConfTpl()
|
||||
content = mw.readFile(caddy_file_tpl)
|
||||
content = content.replace('{$SERVER_PATH}', service_path)
|
||||
mw.writeFile(caddy_file, content)
|
||||
|
||||
# systemd
|
||||
# /usr/lib/systemd/system
|
||||
systemDir = mw.systemdCfgDir()
|
||||
systemService = systemDir + '/caddy.service'
|
||||
if os.path.exists(systemDir) and not os.path.exists(systemService):
|
||||
systemServiceTpl = getPluginDir() + '/init.d/caddy.service.tpl'
|
||||
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 status():
|
||||
cmd = "ps -ef|grep 'caddy run' |grep -v grep | grep -v python | awk '{print $2}'"
|
||||
data = mw.execShell(cmd)
|
||||
if data[0] == '':
|
||||
return 'stop'
|
||||
return 'start'
|
||||
|
||||
|
||||
def caddyOp(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 caddy ' + method)
|
||||
if data[1] == '':
|
||||
return 'ok'
|
||||
return data[1]
|
||||
|
||||
data = mw.execShell('systemctl ' + method + ' caddy')
|
||||
if data[1] == '':
|
||||
return 'ok'
|
||||
return data[1]
|
||||
|
||||
def start():
|
||||
return caddyOp('start')
|
||||
|
||||
|
||||
def stop():
|
||||
r = caddyOp('stop')
|
||||
return r
|
||||
|
||||
|
||||
def restart():
|
||||
return restyOp_restart()
|
||||
|
||||
|
||||
def reload():
|
||||
confReplace()
|
||||
return caddyOp('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 caddy | 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 caddy')
|
||||
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 caddy')
|
||||
return 'ok'
|
||||
|
||||
|
||||
def runInfo():
|
||||
op_status = status()
|
||||
if op_status == 'stop':
|
||||
return mw.returnJson(False, "未启动!")
|
||||
|
||||
|
||||
def errorLogPath():
|
||||
return getServerDir() + '/nginx/logs/error.log'
|
||||
|
||||
|
||||
def getCfg():
|
||||
cfg = getConf()
|
||||
content = mw.readFile(cfg)
|
||||
|
||||
unitrep = "[kmgKMG]"
|
||||
cfg_args = [
|
||||
{"name": "worker_processes", "ps": "处理进程,auto表示自动,数字表示进程数", 'type': 2},
|
||||
{"name": "worker_connections", "ps": "最大并发链接数", 'type': 2},
|
||||
{"name": "keepalive_timeout", "ps": "连接超时时间", 'type': 2},
|
||||
{"name": "zstd", "ps": "是否开启zstd压缩传输", 'type': 1},
|
||||
{"name": "brotli", "ps": "是否开启brotli压缩传输", 'type': 1},
|
||||
{"name": "gzip", "ps": "是否开启gzip压缩传输", 'type': 1},
|
||||
{"name": "gzip_min_length", "ps": "最小压缩文件", 'type': 2},
|
||||
{"name": "gzip_comp_level", "ps": "压缩率", 'type': 2},
|
||||
{"name": "client_max_body_size", "ps": "最大上传文件", 'type': 2},
|
||||
{"name": "server_names_hash_bucket_size",
|
||||
"ps": "服务器名字的hash表大小", 'type': 2},
|
||||
{"name": "client_header_buffer_size", "ps": "客户端请求头buffer大小", 'type': 2},
|
||||
]
|
||||
|
||||
# {"name": "client_body_buffer_size", "ps": "请求主体缓冲区"}
|
||||
rdata = []
|
||||
for i in cfg_args:
|
||||
rep = r"(%s)\s+(\w+)" % i["name"]
|
||||
k = re.search(rep, content)
|
||||
if not k:
|
||||
return mw.returnJson(False, "获取 key {} 失败".format(k))
|
||||
k = k.group(1)
|
||||
v = re.search(rep, content)
|
||||
if not v:
|
||||
return mw.returnJson(False, "获取 value {} 失败".format(v))
|
||||
v = v.group(2)
|
||||
|
||||
if re.search(unitrep, v):
|
||||
u = str.upper(v[-1])
|
||||
v = v[:-1]
|
||||
if len(u) == 1:
|
||||
psstr = u + "B," + i["ps"]
|
||||
else:
|
||||
psstr = u + "," + i["ps"]
|
||||
else:
|
||||
u = ""
|
||||
|
||||
kv = {"name": k, "value": v, "unit": u,
|
||||
"ps": i["ps"], "type": i["type"]}
|
||||
rdata.append(kv)
|
||||
|
||||
return mw.returnJson(True, "ok", rdata)
|
||||
|
||||
def replaceChar(value, index, new_char):
|
||||
return value[:index] + new_char + value[index+1:]
|
||||
|
||||
def makeWorkerCpuAffinity(val):
|
||||
if val == "auto":
|
||||
return "auto"
|
||||
|
||||
if mw.isNumber(val):
|
||||
core_num = int(val)
|
||||
default_core_str = "0"*core_num
|
||||
core_num_arr = []
|
||||
for x in range(core_num):
|
||||
t = replaceChar(default_core_str, x , "1")
|
||||
core_num_arr.append(t)
|
||||
return " ".join(core_num_arr)
|
||||
|
||||
return 'auto'
|
||||
|
||||
def setCfg():
|
||||
|
||||
args = getArgs()
|
||||
data = checkArgs(args, [
|
||||
'worker_processes', 'worker_connections', 'keepalive_timeout','zstd','brotli',
|
||||
'gzip', 'gzip_min_length', 'gzip_comp_level', 'client_max_body_size',
|
||||
'server_names_hash_bucket_size', 'client_header_buffer_size'
|
||||
])
|
||||
if not data[0]:
|
||||
return data[1]
|
||||
|
||||
cfg = getConf()
|
||||
mw.backFile(cfg)
|
||||
content = mw.readFile(cfg)
|
||||
|
||||
unitrep = "[kmgKMG]"
|
||||
cfg_args = [
|
||||
{"name": "worker_processes", "ps": "处理进程,auto表示自动,数字表示进程数", 'type': 2},
|
||||
{"name": "worker_connections", "ps": "最大并发链接数", 'type': 2},
|
||||
{"name": "keepalive_timeout", "ps": "连接超时时间", 'type': 2},
|
||||
{"name": "zstd", "ps": "是否开启zstd压缩传输", 'type': 1},
|
||||
{"name": "brotli", "ps": "是否开启brotli压缩传输", 'type': 1},
|
||||
{"name": "gzip", "ps": "是否开启压缩传输", 'type': 1},
|
||||
{"name": "gzip_min_length", "ps": "最小压缩文件", 'type': 2},
|
||||
{"name": "gzip_comp_level", "ps": "压缩率", 'type': 2},
|
||||
{"name": "client_max_body_size", "ps": "最大上传文件", 'type': 2},
|
||||
{"name": "server_names_hash_bucket_size",
|
||||
"ps": "服务器名字的hash表大小", 'type': 2},
|
||||
{"name": "client_header_buffer_size", "ps": "客户端请求头buffer大小", 'type': 2},
|
||||
]
|
||||
|
||||
# print(args)
|
||||
for k, v in args.items():
|
||||
# print(k, v)
|
||||
rep = r"%s\s+[^kKmMgG\;\n]+" % k
|
||||
if k == "worker_processes" or k == "gzip":
|
||||
if not re.search(r"auto|on|off|\d+", v):
|
||||
return mw.returnJson(False, '参数值错误')
|
||||
elif k == "zstd" or k == "brotli":
|
||||
if not re.search(r"auto|on|off|\d+", v):
|
||||
return mw.returnJson(False, '参数值错误')
|
||||
else:
|
||||
if not re.search(r"\d+", v):
|
||||
return mw.returnJson(False, '参数值错误,请输入数字整数')
|
||||
|
||||
if k == "worker_processes" :
|
||||
k_wca = "worker_cpu_affinity"
|
||||
rep_wca = r"%s\s+[^\;\n]+" % k_wca
|
||||
v_wca = makeWorkerCpuAffinity(v)
|
||||
newconf = "%s %s" % (k_wca, v_wca)
|
||||
content = re.sub(rep_wca, newconf, content)
|
||||
|
||||
|
||||
if re.search(rep, content):
|
||||
newconf = "%s %s" % (k, v)
|
||||
content = re.sub(rep, newconf, content)
|
||||
elif re.search(rep, content):
|
||||
newconf = "%s %s" % (k, v)
|
||||
content = re.sub(rep, newconf, content)
|
||||
|
||||
mw.writeFile(cfg, content)
|
||||
isError = mw.checkWebConfig()
|
||||
if (isError != True):
|
||||
mw.restoreFile(cfg)
|
||||
return mw.returnJson(False, 'ERROR: 配置出错<br><a style="color:red;">' + isError.replace("\n", '<br>') + '</a>')
|
||||
|
||||
mw.restartWeb()
|
||||
return mw.returnJson(True, '设置成功')
|
||||
|
||||
|
||||
def cronAddCheck():
|
||||
try:
|
||||
import tool_task
|
||||
tool_task.createBgTask()
|
||||
return mw.returnJson(True, '添加检查任务成功')
|
||||
except Exception as e:
|
||||
return mw.returnJson(False, '添加检查任务失败:'+str(e))
|
||||
|
||||
def cronDelCheck():
|
||||
try:
|
||||
import tool_task
|
||||
tool_task.removeBgTask()
|
||||
return mw.returnJson(True, '删除检查任务成功')
|
||||
except Exception as e:
|
||||
return mw.returnJson(False, '删除检查任务失败:'+str(e))
|
||||
|
||||
def cronCheck():
|
||||
return 'ok'
|
||||
|
||||
|
||||
def installPreInspection():
|
||||
return 'ok'
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
version = '1.27.1'
|
||||
version_pl = getServerDir() + "/version.pl"
|
||||
if os.path.exists(version_pl):
|
||||
version = mw.readFile(version_pl)
|
||||
|
||||
|
||||
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 == 'install_pre_inspection':
|
||||
print(installPreInspection())
|
||||
elif func == 'conf':
|
||||
print(getConf())
|
||||
elif func == 'get_os':
|
||||
print(getOs())
|
||||
elif func == 'run_info':
|
||||
print(runInfo())
|
||||
elif func == 'error_log':
|
||||
print(errorLogPath())
|
||||
elif func == 'get_cfg':
|
||||
print(getCfg())
|
||||
elif func == 'set_cfg':
|
||||
print(setCfg())
|
||||
elif func == 'check':
|
||||
print(cronCheck())
|
||||
elif func == 'cron_add_check':
|
||||
print(cronAddCheck())
|
||||
elif func == 'cron_del_check':
|
||||
print(cronDelCheck())
|
||||
else:
|
||||
print('error')
|
||||
@@ -1,18 +0,0 @@
|
||||
{
|
||||
"sort": 0,
|
||||
"title":"caddy",
|
||||
"tip":"soft",
|
||||
"name":"caddy",
|
||||
"type":"其他插件",
|
||||
"ps":"一款功能强大、企业级、开源 Web 服务器,具有用 Go 编写的自动 HTTPS",
|
||||
"shell":"install.sh",
|
||||
"install_pre_inspection":true,
|
||||
"checks":"server/caddy",
|
||||
"path":"server/caddy",
|
||||
"author":"caddyserver",
|
||||
"home":"https://caddyserver.com/",
|
||||
"date":"2026-04-11",
|
||||
"pid": "1",
|
||||
"versions": ["2.11"],
|
||||
"updates": ["2.11.2"]
|
||||
}
|
||||
@@ -1,36 +0,0 @@
|
||||
# caddy.service
|
||||
#
|
||||
# For using Caddy with a config file.
|
||||
#
|
||||
# Make sure the ExecStart and ExecReload commands are correct
|
||||
# for your installation.
|
||||
#
|
||||
# See https://caddyserver.com/docs/install for instructions.
|
||||
#
|
||||
# WARNING: This service does not use the --resume flag, so if you
|
||||
# use the API to make changes, they will be overwritten by the
|
||||
# Caddyfile next time the service is restarted. If you intend to
|
||||
# use Caddy's API to configure it, add the --resume flag to the
|
||||
# `caddy run` command or use the caddy-api.service file instead.
|
||||
|
||||
[Unit]
|
||||
Description=Caddy
|
||||
Documentation=https://caddyserver.com/docs/
|
||||
After=network.target network-online.target
|
||||
Requires=network-online.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
User=www
|
||||
Group=www
|
||||
ExecStart={$SERVER_PATH}/caddy/bin/caddy run --environ --config {$SERVER_PATH}/caddy/Caddyfile
|
||||
ExecReload={$SERVER_PATH}/caddy/bin/caddy reload --config {$SERVER_PATH}/caddy/Caddyfile --force
|
||||
TimeoutStopSec=5s
|
||||
LimitNOFILE=1048576
|
||||
LimitNPROC=512
|
||||
PrivateTmp=true
|
||||
ProtectSystem=full
|
||||
AmbientCapabilities=CAP_NET_BIND_SERVICE
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
@@ -1,111 +0,0 @@
|
||||
#!/bin/sh
|
||||
#
|
||||
# /etc/rc.d/init.d/caddy
|
||||
#
|
||||
# Runs the caddy
|
||||
#
|
||||
#
|
||||
# chkconfig: - 85 15
|
||||
#
|
||||
|
||||
### BEGIN INIT INFO
|
||||
# Provides: caddy
|
||||
# Required-Start: $remote_fs $syslog
|
||||
# Required-Stop: $remote_fs $syslog
|
||||
# Should-Start: caddy
|
||||
# Should-Stop: caddy
|
||||
# Default-Start: 2 3 4 5
|
||||
# Default-Stop: 0 1 6
|
||||
# Short-Description: Start caddy at boot time.
|
||||
# Description: Control caddy.
|
||||
### END INIT INFO
|
||||
|
||||
# Source function library.
|
||||
if [ -f /etc/init.d/functions ];then
|
||||
. /etc/init.d/functions
|
||||
fi
|
||||
|
||||
if [ -f /etc/rc.d/init.d/functions ];then
|
||||
. /etc/rc.d/init.d/functions
|
||||
fi
|
||||
|
||||
# Default values
|
||||
export HOME={$HOME_DIR}
|
||||
export USER={$RUN_USER}
|
||||
NAME=caddy
|
||||
CADDY_HOME={$SERVER_PATH}/caddy
|
||||
CADDY_PATH=${CADDY_HOME}/bin/$NAME
|
||||
CADDY_USER={$RUN_USER}
|
||||
SERVICENAME="caddy"
|
||||
LOCKFILE=/tmp/caddy.lock
|
||||
LOGPATH=${CADDY_HOME}/log
|
||||
LOGFILE=${LOGPATH}/caddy.log
|
||||
RETVAL=0
|
||||
|
||||
if [ -d $LOGPATH ];then
|
||||
mkdir -p $LOGPATH
|
||||
fi
|
||||
|
||||
[ -r /etc/sysconfig/$NAME ] && . /etc/sysconfig/$NAME
|
||||
DAEMON_OPTS="--check $NAME"
|
||||
[ ! -z "$CADDY_USER" ] && DAEMON_OPTS="$DAEMON_OPTS --user=${CADDY_USER}"
|
||||
|
||||
status(){
|
||||
isStart=`ps -ef|grep 'caddy run' |grep -v grep|awk '{print $2}'`
|
||||
if [ "$isStart" == '' ];then
|
||||
echo -e "${SERVICENAME} not running"
|
||||
else
|
||||
echo -e "${SERVICENAME}(pid $(echo $isStart)) already running"
|
||||
fi
|
||||
}
|
||||
|
||||
start() {
|
||||
isStart=`ps -ef | grep 'caddy run' | grep -v grep | awk '{print $2}'`
|
||||
if [ "$isStart" != '' ];then
|
||||
echo "${SERVICENAME}(pid $(echo $isStart)) already running"
|
||||
return $RETVAL
|
||||
fi
|
||||
|
||||
cd ${CADDY_HOME}
|
||||
echo -e "starting ${SERVICENAME}: \c"
|
||||
${CADDY_PATH} run --environ --config ${CADDY_HOME}/Caddyfile > ${LOGFILE} 2>&1 &
|
||||
RETVAL=$?
|
||||
[ $RETVAL = 0 ] && touch ${LOCKFILE} && echo -e "\033[32mdone\033[0m"
|
||||
return $RETVAL
|
||||
}
|
||||
|
||||
stop() {
|
||||
pids=`ps -ef | grep 'caddy run' | grep -v grep | awk '{print $2}'`
|
||||
arr=($pids)
|
||||
echo -e "stopping caddy... \c"
|
||||
for p in ${arr[@]}
|
||||
do
|
||||
kill -9 $p
|
||||
done
|
||||
echo -e "\033[32mdone\033[0m"
|
||||
}
|
||||
|
||||
case "$1" in
|
||||
start)
|
||||
start
|
||||
;;
|
||||
stop)
|
||||
stop
|
||||
;;
|
||||
status)
|
||||
status
|
||||
;;
|
||||
restart)
|
||||
stop
|
||||
start
|
||||
;;
|
||||
reload)
|
||||
stop
|
||||
start
|
||||
;;
|
||||
*)
|
||||
echo "Usage: ${NAME} {start|stop|status|restart}"
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
exit $RETVAL
|
||||
@@ -1,76 +0,0 @@
|
||||
#!/bin/bash
|
||||
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin:/opt/homebrew/bin
|
||||
export PATH
|
||||
|
||||
# cd /Users/midoks/Desktop/mwdev/server/mdserver-web/plugins/caddy && bash install.sh install 1.21.4
|
||||
# cd /www/server/mdserver-web/plugins/caddy && bash install.sh install 2.11
|
||||
# cd /www/server/mdserver-web/plugins/caddy && bash install.sh install 1.29.2
|
||||
# cd /www/server/mdserver-web/plugins/caddy && bash install.sh upgrade 1.29.2
|
||||
|
||||
# curl -I -H "Accept-Encoding: br" http://localhost
|
||||
# curl -I -H "Accept-Encoding: zstd" http://localhost
|
||||
# curl --http3 -v https://www.xxx.com
|
||||
|
||||
# cd /www/server/mdserver-web && python3 plugins/caddy/index.py run_info
|
||||
|
||||
curPath=`pwd`
|
||||
rootPath=$(dirname "$curPath")
|
||||
rootPath=$(dirname "$rootPath")
|
||||
serverPath=$(dirname "$rootPath")
|
||||
|
||||
sysName=`uname`
|
||||
action=$1
|
||||
type=$2
|
||||
|
||||
VERSION=$2
|
||||
openrestyDir=${serverPath}/source/caddy
|
||||
|
||||
if id www &> /dev/null ;then
|
||||
echo "www uid is `id -u www`"
|
||||
echo "www shell is `grep "^www:" /etc/passwd |cut -d':' -f7 `"
|
||||
else
|
||||
groupadd www
|
||||
useradd -g www -s /bin/bash www
|
||||
fi
|
||||
|
||||
if [ "${action}" = "upgrade" ];then
|
||||
sh -x $curPath/versions/$2/install.sh $1
|
||||
|
||||
echo "${VERSION}" > $serverPath/caddy/version.pl
|
||||
|
||||
#初始化
|
||||
cd ${rootPath} && python3 ${rootPath}/plugins/caddy/index.py start
|
||||
cd ${rootPath} && python3 ${rootPath}/plugins/caddy/index.py initd_install
|
||||
exit 0
|
||||
fi
|
||||
|
||||
|
||||
if [ "${2}" = "" ];then
|
||||
echo '缺少安装脚本版本...'
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [ "${action}" = "uninstall" ];then
|
||||
if [ -f /usr/lib/systemd/system/caddy.service ] || [ -f /lib/systemd/system/caddy.service ];then
|
||||
systemctl stop caddy
|
||||
rm -rf /usr/systemd/system/caddy.service
|
||||
rm -rf /lib/systemd/system/caddy.service
|
||||
systemctl daemon-reload
|
||||
fi
|
||||
|
||||
if [ -f $serverPath/caddy/init.d/caddy ];then
|
||||
$serverPath/caddy/init.d/caddy stop
|
||||
fi
|
||||
|
||||
rm -rf $serverPath/caddy
|
||||
fi
|
||||
|
||||
sh -x $curPath/versions/$2/install.sh $1
|
||||
|
||||
if [ "${action}" = "install" ] && [ -d $serverPath/caddy ];then
|
||||
echo "${VERSION}" > $serverPath/caddy/version.pl
|
||||
|
||||
#初始化
|
||||
cd ${rootPath} && python3 ${rootPath}/plugins/caddy/index.py start
|
||||
cd ${rootPath} && python3 ${rootPath}/plugins/caddy/index.py initd_install
|
||||
fi
|
||||
@@ -1,238 +0,0 @@
|
||||
function caddyPost(method, args, callback){
|
||||
var loadT = layer.msg('正在获取...', { icon: 16, time: 0, shade: 0.3 });
|
||||
$.post('/plugins/run', {name:'caddy', func:method, args:JSON.stringify(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 orPluginService(_name, version){
|
||||
var data = {name:_name, func:'status'}
|
||||
if ( typeof(version) != 'undefined' ){
|
||||
data['version'] = version;
|
||||
} else {
|
||||
version = '';
|
||||
}
|
||||
|
||||
caddyPost('status', data, function(data){
|
||||
if (data.data == 'start'){
|
||||
orPluginSetService(_name, true, version);
|
||||
} else {
|
||||
orPluginSetService(_name, false, version);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function orPluginSetService(_name ,status, version){
|
||||
var serviceCon ='<p class="status">当前状态:<span>'+(status ? '开启' : '关闭' )+
|
||||
'</span><span style="color: '+
|
||||
(status?'#20a53a;':'red;')+
|
||||
' margin-left: 3px;" class="glyphicon ' + (status?'glyphicon glyphicon-play':'glyphicon-pause')+'"></span></p><div class="sfm-opt">\
|
||||
<button class="btn btn-default btn-sm" onclick="orPluginOpService(\''+_name+'\',\''+(status?'stop':'start')+'\',\''+version+'\')">'+(status?'停止':'启动')+'</button>\
|
||||
<button class="btn btn-default btn-sm" onclick="orPluginOpService(\''+_name+'\',\'restart\',\''+version+'\',\'yes\')">重启</button>\
|
||||
<button class="btn btn-default btn-sm" onclick="orPluginOpService(\''+_name+'\',\'reload\',\''+version+'\')">重载配置</button>\
|
||||
</div>';
|
||||
$(".soft-man-con").html(serviceCon);
|
||||
}
|
||||
|
||||
|
||||
function orPluginOpService(a, b, v,request_callback) {
|
||||
|
||||
var c = "name=" + a + "&func=" + b;
|
||||
if(v != ''){
|
||||
c = c + '&version='+v;
|
||||
}
|
||||
|
||||
var d = "";
|
||||
|
||||
switch(b) {
|
||||
case "stop":d = '停止';break;
|
||||
case "start":d = '启动';break;
|
||||
case "restart":d = '重启';break;
|
||||
case "reload":d = '重载';break;
|
||||
}
|
||||
layer.confirm( msgTpl('您真的要{1}{2}{3}服务吗?', [d,a,v]), {icon:3,closeBtn: 2}, function() {
|
||||
caddyPost('get_os',{},function(data){
|
||||
var rdata = $.parseJSON(data.data);
|
||||
if (!rdata['auth']){
|
||||
layer.prompt({title: '检查到权限不足,需要输入密码!', formType: 1},function(pwd, index){
|
||||
|
||||
layer.close(index);
|
||||
var data = {'pwd':pwd};
|
||||
c += '&args='+JSON.stringify(data);
|
||||
orPluginOpServiceOp(a,b,c,d,a,v,request_callback);
|
||||
});
|
||||
} else {
|
||||
orPluginOpServiceOp(a,b,c,d,a,v,request_callback);
|
||||
|
||||
}
|
||||
});
|
||||
})
|
||||
}
|
||||
|
||||
function orPluginOpServiceOp(a,b,c,d,a,v,request_callback){
|
||||
|
||||
var request_path = "/plugins/run";
|
||||
if (request_callback == 'yes'){
|
||||
request_path = "/plugins/callback";
|
||||
}
|
||||
|
||||
var e = layer.msg(msgTpl('正在{1}{2}{3}服务,请稍候...',[d,a,v]), {icon: 16,time: 0});
|
||||
$.post(request_path, c, function(g) {
|
||||
layer.close(e);
|
||||
|
||||
var f = g.data == 'ok' ? msgTpl('{1}{2}服务已{3}',[a,v,d]) : msgTpl('{1}{2}服务{3}失败!',[a,v,d]);
|
||||
layer.msg(f, {icon: g.data == 'ok' ? 1 : 2});
|
||||
|
||||
if( b != "reload" && g.data == 'ok' ) {
|
||||
if ( b == 'start' ) {
|
||||
orPluginSetService(a, true, v);
|
||||
} else if ( b == 'stop' ){
|
||||
orPluginSetService(a, false, v);
|
||||
}
|
||||
}
|
||||
|
||||
if( g.status && g.data != 'ok' ) {
|
||||
layer.msg(g.data, {icon: 2,time: 10000,shade: 0.3});
|
||||
}
|
||||
|
||||
},'json').error(function() {
|
||||
layer.close(e);
|
||||
layer.msg('操作异常!', {icon: 2});
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
//查看Nginx负载状态
|
||||
function getOpStatus() {
|
||||
var loadT = layer.msg('正在处理,请稍后...', { icon: 16, time: 0, shade: 0.3 });
|
||||
$.post('/plugins/run', {name:'openresty', func:'run_info'}, function(data) {
|
||||
layer.close(loadT);
|
||||
try {
|
||||
var rdata = $.parseJSON(data.data);
|
||||
if ('status' in rdata && !rdata.status){
|
||||
showMsg(rdata.msg, function(){}, null,3000);
|
||||
return;
|
||||
}
|
||||
|
||||
var con = "<div><table class='table table-hover table-bordered'>\
|
||||
<tr><th>活动连接(Active connections)</th><td>" + rdata.active + "</td></tr>\
|
||||
<tr><th>总连接次数(accepts)</th><td>" + rdata.accepts + "</td></tr>\
|
||||
<tr><th>总握手次数(handled)</th><td>" + rdata.handled + "</td></tr>\
|
||||
<tr><th>总请求数(requests)</th><td>" + rdata.requests + "</td></tr>\
|
||||
<tr><th>请求数(Reading)</th><td>" + rdata.Reading + "</td></tr>\
|
||||
<tr><th>响应数(Writing)</th><td>" + rdata.Writing + "</td></tr>\
|
||||
<tr><th>驻留进程(Waiting)</th><td>" + rdata.Waiting + "</td></tr>\
|
||||
</table></div>";
|
||||
$(".soft-man-con").html(con);
|
||||
}catch(err){
|
||||
showMsg(data.data, function(){}, null,3000);
|
||||
}
|
||||
},'json');
|
||||
}
|
||||
|
||||
|
||||
function setOpCfg(){
|
||||
caddyPost('get_cfg', {}, function(data){
|
||||
var rdata = $.parseJSON(data.data);
|
||||
var rdata = rdata.data;
|
||||
// console.log(rdata);
|
||||
|
||||
var mlist = '';
|
||||
for (var i = 0; i < rdata.length; i++) {
|
||||
var w = '70'
|
||||
var ibody = '<input style="width: ' + w + 'px;" class="bt-input-text mr5" name="' + rdata[i].name + '" value="' + rdata[i].value + '" type="text" >';
|
||||
switch (rdata[i].type) {
|
||||
case 0:
|
||||
var selected_1 = (rdata[i].value == 1) ? 'selected' : '';
|
||||
var selected_0 = (rdata[i].value == 0) ? 'selected' : '';
|
||||
ibody = '<select class="bt-input-text mr5" name="' + rdata[i].name + '" style="width: ' + w + 'px;">\
|
||||
<option value="1" ' + selected_1 + '>开启</option>\
|
||||
<option value="0" ' + selected_0 + '>关闭</option>\
|
||||
</select>';
|
||||
break;
|
||||
case 1:
|
||||
var selected_1 = (rdata[i].value == 'on') ? 'selected' : '';
|
||||
var selected_0 = (rdata[i].value == 'off') ? 'selected' : '';
|
||||
ibody = '<select class="bt-input-text mr5" name="' + rdata[i].name + '" style="width: ' + w + 'px;">\
|
||||
<option value="on" ' + selected_1 + '>开启</option>\
|
||||
<option value="off" ' + selected_0 + '>关闭</option>\
|
||||
</select>';
|
||||
break;
|
||||
}
|
||||
mlist += '<p style="margin-top:15px;"><span>' + rdata[i].name + '</span>' + ibody + "<b class='unit c9'>"+rdata[i].unit+"</b>" +', <font class="c9">' + rdata[i].ps + '</font></p>';
|
||||
}
|
||||
var con = '<style>.conf_p p{margin-bottom: 2px}</style><div class="conf_p" style="margin-bottom:0">\
|
||||
' + mlist + '\
|
||||
<div style="margin-top:10px; padding-right:15px" class="text-right">\
|
||||
<button class="btn btn-success btn-sm mr5" onclick="setOpCfg()">刷新</button>\
|
||||
<button class="btn btn-success btn-sm" onclick="submitConf()">保存</button>\
|
||||
</div>\
|
||||
</div>'
|
||||
$(".soft-man-con").html(con);
|
||||
});
|
||||
}
|
||||
|
||||
function submitConf() {
|
||||
var data = {
|
||||
worker_processes: $("input[name='worker_processes']").val(),
|
||||
worker_connections: $("input[name='worker_connections']").val(),
|
||||
keepalive_timeout: $("input[name='keepalive_timeout']").val(),
|
||||
zstd: $("select[name='zstd']").val() || 'on',
|
||||
brotli: $("select[name='brotli']").val() || 'on',
|
||||
gzip: $("select[name='gzip']").val() || 'on',
|
||||
gzip_min_length: $("input[name='gzip_min_length']").val(),
|
||||
gzip_comp_level: $("input[name='gzip_comp_level']").val(),
|
||||
client_max_body_size: $("input[name='client_max_body_size']").val(),
|
||||
server_names_hash_bucket_size: $("input[name='server_names_hash_bucket_size']").val(),
|
||||
client_header_buffer_size: $("input[name='client_header_buffer_size']").val(),
|
||||
};
|
||||
|
||||
// console.log(data);
|
||||
caddyPost('set_cfg', data, function(rdata){
|
||||
var rdata = $.parseJSON(rdata.data);
|
||||
// console.log(rdata);
|
||||
layer.msg(rdata.msg, { icon: rdata.status ? 1 : 2 });
|
||||
});
|
||||
}
|
||||
|
||||
function otherFunc(){
|
||||
var con = '<p class="conf_p" style="text-align:center;">\
|
||||
<button class="btn btn-default btn-sm" onclick="cronAddCheck()">添加检查任务</button> \
|
||||
<button class="btn btn-default btn-sm" onclick="cronDelCheck()">删除检查任务</button>\
|
||||
</p>';
|
||||
$(".soft-man-con").html(con);
|
||||
}
|
||||
|
||||
function cronAddCheck(){
|
||||
caddyPost('cron_add_check', {}, function(data){
|
||||
var rdata = $.parseJSON(data.data);
|
||||
layer.msg(rdata.msg, { icon: rdata.status ? 1 : 2 });
|
||||
});
|
||||
}
|
||||
|
||||
function cronDelCheck(){
|
||||
caddyPost('cron_del_check', {}, function(data){
|
||||
var rdata = $.parseJSON(data.data);
|
||||
layer.msg(rdata.msg, { icon: rdata.status ? 1 : 2 });
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,124 +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 'caddy'
|
||||
|
||||
|
||||
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": "minute-n",
|
||||
"where1": "3",
|
||||
"hour": "0",
|
||||
"minute": "0",
|
||||
}
|
||||
|
||||
|
||||
def createBgTask():
|
||||
removeBgTask()
|
||||
createBgTaskByName(getPluginName())
|
||||
|
||||
|
||||
def createBgTaskByName(name):
|
||||
args = getConfigData()
|
||||
_name = "[caddy]检查任务"
|
||||
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
|
||||
''' % (mw_dir, name, getServerDir(), getPluginDir())
|
||||
cmd += 'echo "bash $script_path/check.sh"' + "\n"
|
||||
cmd += 'cd $mw_dir && bash $script_path/check.sh' + "\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["status"]:
|
||||
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()
|
||||
@@ -1,88 +0,0 @@
|
||||
#!/bin/bash
|
||||
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin:/opt/homebrew/bin
|
||||
export PATH
|
||||
|
||||
# cd /Users/midoks/Desktop/mwdev/server/mdserver-web/plugins/caddy && bash install.sh install 2.11
|
||||
# cd /Users/midoks/Desktop/mwdev/server/mdserver-web/plugins/caddy && bash install.sh upgrade 2.11
|
||||
# cd /www/server/mdserver-web/plugins/caddy && bash install.sh install 2.11
|
||||
|
||||
curPath=`pwd`
|
||||
rootPath=$(dirname "$curPath")
|
||||
rootPath=$(dirname "$rootPath")
|
||||
serverPath=$(dirname "$rootPath")
|
||||
|
||||
sysName=`uname`
|
||||
action=$1
|
||||
type=$2
|
||||
|
||||
OS=`uname`
|
||||
OSNAME=''
|
||||
case $(uname) in
|
||||
Darwin) OSNAME="mac" ;;
|
||||
Linux) OSNAME="linux" ;;
|
||||
esac
|
||||
|
||||
ARCH=`uname -m`
|
||||
ARCH_NAME=''
|
||||
case $(uname -m) in
|
||||
i386) ARCH_NAME="386" ;;
|
||||
i686) ARCH_NAME="386" ;;
|
||||
x86_64) ARCH_NAME="amd64" ;;
|
||||
arm) ARCH_NAME="arm64" ;;
|
||||
arm64) ARCH_NAME="arm64" ;;
|
||||
aarch64) ARCH_NAME="arm64" ;;
|
||||
esac
|
||||
|
||||
VERSION=2.11.2
|
||||
|
||||
caddyDir=${serverPath}/source/caddy
|
||||
|
||||
Install_App()
|
||||
{
|
||||
if [ "${action}" = "install" ];then
|
||||
if [ -d $serverPath/caddy ];then
|
||||
exit 0
|
||||
fi
|
||||
fi
|
||||
|
||||
mkdir -p ${caddyDir}
|
||||
# mkdir -p ${serverPath}/caddy
|
||||
echo 'install scripts ...'
|
||||
|
||||
FILE_NAME="caddy_${VERSION}_${OSNAME}_${ARCH_NAME}.tar.gz"
|
||||
|
||||
if [ ! -f ${caddyDir}/${FILE_NAME} ];then
|
||||
wget --no-check-certificate -O ${caddyDir}/${FILE_NAME} "https://github.com/caddyserver/caddy/releases/download/v${VERSION}/${FILE_NAME}"
|
||||
fi
|
||||
|
||||
|
||||
echo "cd $serverPath/source/caddy/caddy && tar -zxvf ${caddyDir}/$FILE_NAME"
|
||||
|
||||
mkdir -p cd $serverPath/source/caddy/caddy
|
||||
cd $serverPath/source/caddy/caddy && tar -zxvf ${caddyDir}/$FILE_NAME
|
||||
if [ "$OSNAME" = "mac" ];then
|
||||
xattr -cr caddy
|
||||
fi
|
||||
|
||||
if [ ! -f $serverPath/caddy/bin ];then
|
||||
mkdir -p $serverPath/caddy/bin
|
||||
mv $serverPath/source/caddy/caddy/* $serverPath/caddy/bin
|
||||
chmod +x $serverPath/caddy/bin/caddy
|
||||
fi
|
||||
|
||||
echo 'Installation of caddy completed'
|
||||
}
|
||||
|
||||
Uninstall_App()
|
||||
{
|
||||
echo 'Uninstalling caddy completed'
|
||||
}
|
||||
|
||||
action=$1
|
||||
if [ "${1}" = "install" ];then
|
||||
Install_App
|
||||
elif [ "${1}" = "upgrade" ];then
|
||||
Install_App
|
||||
else
|
||||
Uninstall_App
|
||||
fi
|
||||
|
Before Width: | Height: | Size: 254 B |
@@ -1,74 +0,0 @@
|
||||
<div class="bt-form">
|
||||
<div class="bt-w-main">
|
||||
<div class="bt-w-menu">
|
||||
<p class="bgw" onclick="pluginService('clean');">服务</p>
|
||||
<p onclick="pluginConfig('clean');">配置修改</p>
|
||||
<p onclick="commonFunc();">常用功能</p>
|
||||
<p onclick="pluginLogs('clean',null,'run_log');">运行日志</p>
|
||||
<p onclick="pRead()">说明</p>
|
||||
</div>
|
||||
<div class="bt-w-con pd15">
|
||||
<div class="soft-man-con"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script type="text/javascript">
|
||||
|
||||
pluginService('clean');
|
||||
|
||||
function cleanPost(method, version, args, callback){
|
||||
var loadT = layer.msg('正在获取...', { icon: 16, time: 0, shade: 0.3 });
|
||||
|
||||
var req_data = {};
|
||||
req_data['name'] = 'clean';
|
||||
req_data['func'] = method;
|
||||
req_data['version'] = version;
|
||||
|
||||
if (typeof(args) == 'string'){
|
||||
req_data['args'] = JSON.stringify(toArrayObject(args));
|
||||
} else {
|
||||
req_data['args'] = JSON.stringify(args);
|
||||
}
|
||||
|
||||
$.post('/plugins/run', req_data, function(data) {
|
||||
layer.close(loadT);
|
||||
if (!data.status){
|
||||
layer.msg(data.msg,{icon:0,time:2000,shade: [10, '#000']});
|
||||
return;
|
||||
}
|
||||
|
||||
if(typeof(callback) == 'function'){
|
||||
callback(data);
|
||||
}
|
||||
},'json');
|
||||
}
|
||||
|
||||
function cleanRun(){
|
||||
cleanPost('clean_run', '', {}, function(rdata){
|
||||
// var rdata = $.parseJSON(rdata.data);
|
||||
// layer.msg(rdata.msg, { icon: rdata.status ? 1 : 2 });
|
||||
});
|
||||
|
||||
layer.msg("执行成功!", { icon: 1});
|
||||
}
|
||||
|
||||
function commonFunc(){
|
||||
|
||||
con = '<hr/><p class="conf_p" style="text-align:center;">\
|
||||
<button class="btn btn-default btn-sm" onclick="cleanRun()">手动执行</button> \
|
||||
</p>';
|
||||
|
||||
$(".soft-man-con").html(con);
|
||||
}
|
||||
|
||||
function pRead(){
|
||||
var readme = '<ul class="help-info-text c7">';
|
||||
readme += '<li>启动后,加入到计划任务每隔7天[0:15]执行。</li>';
|
||||
readme += '<li>【*】代表要删除。</li>';
|
||||
readme += '<li>添加/删除清除日志,可在`配置修改`中添加/删除。</li>';
|
||||
readme += '<li>如果是目录,会寻找【.log】后缀的文件清空。</li>';
|
||||
readme += '</ul>';
|
||||
|
||||
$('.soft-man-con').html(readme);
|
||||
}
|
||||
</script>
|
||||
@@ -1,304 +0,0 @@
|
||||
# coding:utf-8
|
||||
|
||||
import sys
|
||||
import io
|
||||
import os
|
||||
import time
|
||||
|
||||
web_dir = os.getcwd() + "/web"
|
||||
if os.path.exists(web_dir):
|
||||
sys.path.append(web_dir)
|
||||
os.chdir(web_dir)
|
||||
|
||||
import core.mw as mw
|
||||
|
||||
app_debug = False
|
||||
if mw.isAppleSystem():
|
||||
app_debug = True
|
||||
|
||||
|
||||
def getPluginName():
|
||||
return 'clean'
|
||||
|
||||
|
||||
def getPluginDir():
|
||||
return mw.getPluginDir() + '/' + getPluginName()
|
||||
|
||||
|
||||
def getServerDir():
|
||||
return mw.getServerDir() + '/' + getPluginName()
|
||||
|
||||
|
||||
def getInitDFile():
|
||||
if app_debug:
|
||||
return '/tmp/' + getPluginName()
|
||||
return '/etc/init.d/' + getPluginName()
|
||||
|
||||
|
||||
def getConf():
|
||||
path = getServerDir() + "/clean.conf"
|
||||
return path
|
||||
|
||||
|
||||
def getArgs():
|
||||
args = sys.argv[2:]
|
||||
tmp = {}
|
||||
args_len = len(args)
|
||||
|
||||
if args_len == 1:
|
||||
t = args[0].strip('{').strip('}')
|
||||
t = t.split(':')
|
||||
tmp[t[0]] = t[1]
|
||||
elif args_len > 1:
|
||||
for i in range(len(args)):
|
||||
t = args[i].split(':')
|
||||
tmp[t[0]] = t[1]
|
||||
|
||||
return tmp
|
||||
|
||||
|
||||
def getLockFile():
|
||||
return getServerDir() + "/installed_lock.pl"
|
||||
|
||||
|
||||
def runLog():
|
||||
return getServerDir() + "/clean.log"
|
||||
|
||||
|
||||
def status():
|
||||
initConf()
|
||||
if os.path.exists(getLockFile()):
|
||||
return "start"
|
||||
return 'stop'
|
||||
|
||||
|
||||
def initConf():
|
||||
conf = getConf()
|
||||
if not os.path.exists(conf):
|
||||
content = ""
|
||||
|
||||
clog = [
|
||||
"/var/log/cron-*",
|
||||
"/var/log/maillog-*",
|
||||
"/var/log/secure-*",
|
||||
"/var/log/spooler-*",
|
||||
"/var/log/yum.log-*",
|
||||
"/var/log/messages-*",
|
||||
"/var/log/btmp-*",
|
||||
"/var/log/auth.*",
|
||||
"/var/log/messages.*",
|
||||
"/var/log/debug.*",
|
||||
"/var/log/syslog.*",
|
||||
"/var/log/syslog",
|
||||
"/var/log/mail.*",
|
||||
"/var/log/mail.err.*",
|
||||
"/var/log/mail.info.*",
|
||||
"/var/log/mail.warn.*",
|
||||
"/var/log/btmp.*",
|
||||
"/var/log/sa/sa*",
|
||||
"/var/log/sysstat/sa*",
|
||||
"/var/log/atop/atop*",
|
||||
"/var/log/anaconda/*.log",
|
||||
|
||||
"/var/log/dpkg.log.*",
|
||||
"/var/log/alternatives.log.*",
|
||||
"/var/log/user.log.*",
|
||||
"/var/log/kern.log.*",
|
||||
"/var/log/daemon.log.*",
|
||||
|
||||
"/var/log/*.gz",
|
||||
"/var/log/*.xz",
|
||||
"/var/log/*.log.*",
|
||||
|
||||
"/var/log/audit/audit.log.*",
|
||||
"/var/log/hawkey.log-*",
|
||||
"/var/log/apt/*.gz",
|
||||
"/var/log/apt/*.xz",
|
||||
"/var/log/rhsm/rhsm.log-*",
|
||||
"/var/log/rhsm/rhsmcertd.log-*",
|
||||
"/var/log/exim4/*.gz",
|
||||
"/var/log/journal/*",
|
||||
"/var/spool/clientmqueue/*",
|
||||
|
||||
"/tmp/yum_save_*",
|
||||
"/tmp/tmp.*",
|
||||
|
||||
"/www/server/dztasks/logs/dztasks.*.log",
|
||||
"/www/server/dztasks/logs/dztasks_*",
|
||||
]
|
||||
for i in clog:
|
||||
content += i + "\n"
|
||||
|
||||
# 常用日志
|
||||
clogcom = [
|
||||
"/var/log/messages",
|
||||
"/var/log/btmp",
|
||||
"/var/log/wtmp",
|
||||
"/var/log/secure",
|
||||
"/var/log/lastlog",
|
||||
"/var/log/syslog",
|
||||
"/var/log/cron",
|
||||
"/www/wwwlogs",
|
||||
"/www/server/rsyncd",
|
||||
"/www/server/acme_pandominassl_apply",
|
||||
"/www/server/sphinx/index",
|
||||
"/www/server/mongodb/logs",
|
||||
"/www/server/php/53/var/log",
|
||||
"/www/server/php/54/var/log",
|
||||
"/www/server/php/55/var/log",
|
||||
"/www/server/php/56/var/log",
|
||||
"/www/server/php/70/var/log",
|
||||
"/www/server/php/71/var/log",
|
||||
"/www/server/php/72/var/log",
|
||||
"/www/server/php/73/var/log",
|
||||
"/www/server/php/74/var/log",
|
||||
"/www/server/php/80/var/log",
|
||||
"/www/server/php/81/var/log",
|
||||
"/www/server/php/82/var/log",
|
||||
"/www/server/php/83/var/log",
|
||||
"/www/server/php/84/var/log",
|
||||
"/www/server/php/85/var/log",
|
||||
"/www/server/openresty/nginx/logs",
|
||||
"/www/server/phpmyadmin",
|
||||
"/www/server/apache/httpd/logs",
|
||||
"/www/server/redis/data",
|
||||
"/www/server/alist/data/log",
|
||||
"/www/server/dztasks/logs",
|
||||
"/www/server/rsyncd/lsyncd.log",
|
||||
"/www/server/mdserver-web/logs",
|
||||
]
|
||||
for i in clogcom:
|
||||
if os.path.exists(i):
|
||||
content += i + "\n"
|
||||
|
||||
content += "/www/server/cron\n"
|
||||
# 清理日志
|
||||
rootDir = "/var/log"
|
||||
|
||||
l = os.listdir(rootDir)
|
||||
for x in range(len(l)):
|
||||
abspath = rootDir + "/" + l[x]
|
||||
content += abspath + "\n"
|
||||
mw.writeFile(conf, content)
|
||||
|
||||
|
||||
def start():
|
||||
initConf()
|
||||
|
||||
lock_file = getLockFile()
|
||||
if not os.path.exists(lock_file):
|
||||
mw.writeFile(lock_file, "")
|
||||
|
||||
import tool_task
|
||||
tool_task.createBgTask()
|
||||
return 'ok'
|
||||
|
||||
return 'fail'
|
||||
|
||||
|
||||
def stop():
|
||||
initConf()
|
||||
lock_file = getLockFile()
|
||||
if os.path.exists(lock_file):
|
||||
os.remove(lock_file)
|
||||
import tool_task
|
||||
tool_task.removeBgTask()
|
||||
return 'ok'
|
||||
|
||||
return 'fail'
|
||||
|
||||
|
||||
def reload():
|
||||
return 'ok'
|
||||
|
||||
|
||||
def get_filePath_fileName_fileExt(filename):
|
||||
(filepath, tempfilename) = os.path.split(filename)
|
||||
(shotname, extension) = os.path.splitext(tempfilename)
|
||||
return filepath, shotname, extension
|
||||
|
||||
|
||||
def cleanFileLog(path):
|
||||
filepath, shotname, extension = get_filePath_fileName_fileExt(path)
|
||||
if extension == ".log":
|
||||
cmd = "echo \"\" > " + path
|
||||
tmp = mw.execShell(cmd)
|
||||
if tmp[1] != "":
|
||||
cmd += " | error:" + tmp[1].strip()
|
||||
print(cmd)
|
||||
|
||||
|
||||
def cleanSelfFileLog(path):
|
||||
filepath, shotname, extension = get_filePath_fileName_fileExt(path)
|
||||
if extension == ".log":
|
||||
cmd = "echo \"\" > " + path
|
||||
tmp = mw.execShell(cmd)
|
||||
if tmp[1] != "":
|
||||
cmd += " | error:" + tmp[1].strip()
|
||||
print(cmd)
|
||||
|
||||
|
||||
def cleanDirLog(path):
|
||||
l = os.listdir(path)
|
||||
for x in range(len(l)):
|
||||
abspath = path + "/" + l[x]
|
||||
if os.path.isfile(abspath):
|
||||
cleanFileLog(abspath)
|
||||
if os.path.isdir(abspath):
|
||||
cleanDirLog(abspath)
|
||||
|
||||
|
||||
def cleanRun():
|
||||
plugin_dir = getPluginDir()
|
||||
log_file = getServerDir()+'/clean.log'
|
||||
cmd = 'cd '+mw.getPanelDir()+' && python3 '+plugin_dir+'/index.py clean > '+log_file
|
||||
os.system(cmd)
|
||||
return mw.returnJson(True, '执行成功!')
|
||||
|
||||
def cleanLog():
|
||||
conf = getConf()
|
||||
clist = mw.readFile(conf).strip()
|
||||
clist = clist.split("\n")
|
||||
|
||||
for x in clist:
|
||||
abspath = x.strip()
|
||||
|
||||
if abspath.find('*') > 1:
|
||||
cmd = 'rm -rf ' + abspath
|
||||
print(cmd)
|
||||
data = mw.execShell(cmd)
|
||||
# print(data)
|
||||
continue
|
||||
|
||||
if os.path.exists(abspath):
|
||||
if os.path.isfile(abspath):
|
||||
cleanSelfFileLog(abspath)
|
||||
continue
|
||||
|
||||
if os.path.isdir(abspath):
|
||||
cleanDirLog(abspath)
|
||||
continue
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
func = sys.argv[1]
|
||||
if func == 'status':
|
||||
print(status())
|
||||
elif func == 'start':
|
||||
print(start())
|
||||
elif func == 'stop':
|
||||
print(stop())
|
||||
elif func == 'restart':
|
||||
print(restart())
|
||||
elif func == 'reload':
|
||||
print(reload())
|
||||
elif func == 'conf':
|
||||
print(getConf())
|
||||
elif func == 'run_log':
|
||||
print(runLog())
|
||||
elif func == 'clean':
|
||||
cleanLog()
|
||||
elif func == 'clean_run':
|
||||
print(cleanRun())
|
||||
else:
|
||||
print('error')
|
||||
@@ -1,17 +0,0 @@
|
||||
{
|
||||
"sort":2,
|
||||
"title":"日志清理",
|
||||
"tip":"soft",
|
||||
"name":"clean",
|
||||
"type":"运行环境",
|
||||
"ps":"日志清理",
|
||||
"versions":"1.0",
|
||||
"updates":"1.0",
|
||||
"shell":"install.sh",
|
||||
"checks":"server/clean",
|
||||
"path": "server/clean",
|
||||
"author":"clean",
|
||||
"home":"https://github.com/midoks",
|
||||
"date":"2021-11-23",
|
||||
"pid": "5"
|
||||
}
|
||||
@@ -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
|
||||
. ${rootPath}/bin/activate
|
||||
fi
|
||||
|
||||
|
||||
Install_app()
|
||||
{
|
||||
echo '正在安装脚本文件...'
|
||||
mkdir -p $serverPath/clean
|
||||
|
||||
cd ${rootPath} && python3 ${rootPath}/plugins/clean/index.py start
|
||||
echo "${VERSION}" > $serverPath/clean/version.pl
|
||||
echo '安装完成'
|
||||
}
|
||||
|
||||
Uninstall_app()
|
||||
{
|
||||
cd ${rootPath} && python3 ${rootPath}/plugins/clean/index.py stop
|
||||
rm -rf $serverPath/clean
|
||||
echo "Uninstall_clean"
|
||||
}
|
||||
|
||||
action=$1
|
||||
if [ "${1}" = 'install' ];then
|
||||
Install_app
|
||||
else
|
||||
Uninstall_app
|
||||
fi
|
||||
@@ -1,129 +0,0 @@
|
||||
# coding:utf-8
|
||||
|
||||
import sys
|
||||
import io
|
||||
import os
|
||||
import time
|
||||
import json
|
||||
|
||||
web_dir = os.getcwd() + "/web"
|
||||
if os.path.exists(web_dir):
|
||||
sys.path.append(web_dir)
|
||||
os.chdir(web_dir)
|
||||
|
||||
import core.mw as mw
|
||||
from utils.crontab import crontab as MwCrontab
|
||||
|
||||
|
||||
app_debug = False
|
||||
if mw.isAppleSystem():
|
||||
app_debug = True
|
||||
|
||||
|
||||
def getPluginName():
|
||||
return 'clean'
|
||||
|
||||
|
||||
def getPluginDir():
|
||||
return mw.getPluginDir() + '/' + getPluginName()
|
||||
|
||||
|
||||
def getServerDir():
|
||||
return mw.getServerDir() + '/' + getPluginName()
|
||||
|
||||
|
||||
def getTaskConf():
|
||||
conf = getServerDir() + "/task_config.json"
|
||||
return conf
|
||||
|
||||
|
||||
def getConfigData():
|
||||
conf = getTaskConf()
|
||||
if os.path.exists(conf):
|
||||
return json.loads(mw.readFile(getTaskConf()))
|
||||
return {
|
||||
"task_id": -1,
|
||||
"period": "day-n",
|
||||
"where1": "7",
|
||||
"hour": "0",
|
||||
"minute": "15",
|
||||
}
|
||||
|
||||
|
||||
def createBgTask():
|
||||
removeBgTask()
|
||||
createBgTaskByName(getPluginName())
|
||||
|
||||
|
||||
def createBgTaskByName(name):
|
||||
args = getConfigData()
|
||||
_name = "[勿删]日志清理[" + name + "]"
|
||||
res = mw.M("crontab").field("id, name").where("name=?", (_name,)).find()
|
||||
if res:
|
||||
return True
|
||||
|
||||
if "task_id" in args and args["task_id"] > 0:
|
||||
res = mw.M("crontab").field("id, name").where(
|
||||
"id=?", (args["task_id"],)).find()
|
||||
if res and res["id"] == args["task_id"]:
|
||||
print("计划任务已经存在!")
|
||||
return True
|
||||
|
||||
mw_dir = mw.getPanelDir()
|
||||
cmd = '''
|
||||
mw_dir=%s
|
||||
rname=%s
|
||||
plugin_path=%s
|
||||
script_path=%s
|
||||
logs_file=$plugin_path/${rname}.log
|
||||
''' % (mw_dir, name, getServerDir(), getPluginDir())
|
||||
cmd += 'echo "★【`date +"%Y-%m-%d %H:%M:%S"`】 STSRT★" >> $logs_file' + "\n"
|
||||
cmd += 'echo ">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>" >> $logs_file' + "\n"
|
||||
cmd += 'echo "python3 $script_path/index.py clean >> $logs_file 2>&1"' + "\n"
|
||||
cmd += 'cd $mw_dir && python3 $script_path/index.py clean >> $logs_file 2>&1' + "\n"
|
||||
cmd += 'echo "【`date +"%Y-%m-%d %H:%M:%S"`】 END★" >> $logs_file' + "\n"
|
||||
cmd += 'echo "<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<" >> $logs_file' + "\n"
|
||||
|
||||
params = {
|
||||
'name': _name,
|
||||
'type': args['period'],
|
||||
'week': "",
|
||||
'where1': args['where1'],
|
||||
'hour': args['hour'],
|
||||
'minute': args['minute'],
|
||||
'save': "",
|
||||
'backup_to': "",
|
||||
'stype': "toShell",
|
||||
'sname': '',
|
||||
'sbody': cmd,
|
||||
'url_address': '',
|
||||
}
|
||||
|
||||
task_id = MwCrontab.instance().add(params)
|
||||
if task_id > 0:
|
||||
args["task_id"] = task_id
|
||||
args["name"] = name
|
||||
mw.writeFile(getTaskConf(), json.dumps(args))
|
||||
|
||||
|
||||
def removeBgTask():
|
||||
cfg = getConfigData()
|
||||
if "task_id" in cfg and cfg["task_id"] > 0:
|
||||
res = mw.M("crontab").field("id, name").where(
|
||||
"id=?", (cfg["task_id"],)).find()
|
||||
if res and res["id"] == cfg["task_id"]:
|
||||
data = MwCrontab.instance().delete(cfg["task_id"])
|
||||
if data["status"]:
|
||||
cfg["task_id"] = -1
|
||||
mw.writeFile(getTaskConf(), json.dumps(cfg))
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
if len(sys.argv) > 1:
|
||||
action = sys.argv[1]
|
||||
if action == "remove":
|
||||
removeBgTask()
|
||||
elif action == "add":
|
||||
createBgTask()
|
||||
|
Before Width: | Height: | Size: 31 KiB |
@@ -1,31 +0,0 @@
|
||||
<style>
|
||||
.overflow_hide {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
vertical-align: middle;
|
||||
}
|
||||
</style>
|
||||
|
||||
<div class="bt-form">
|
||||
<div class='plugin_version'></div>
|
||||
<div class="bt-w-main">
|
||||
<div class="bt-w-menu">
|
||||
<p class="bgw" onclick="pluginService('cloudreve');">服务</p>
|
||||
<p onclick="pluginInitD('cloudreve');">自启动</p>
|
||||
<p onclick="alistCommonFunc();">常用功能</p>
|
||||
<p onclick="pluginConfigTpl('cloudreve',$('.plugin_version').attr('version'));">配置修改</p>
|
||||
<p onclick="pluginLogs('cloudreve','','run_log');">运行日志</p>
|
||||
<p onclick="alistReadme();">相关说明</p>
|
||||
|
||||
</div>
|
||||
<div class="bt-w-con pd15">
|
||||
<div class="soft-man-con" style="height: 520px; overflow: auto;"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<script type="text/javascript">
|
||||
$.getScript( "/plugins/file?name=cloudreve&f=js/cloudreve.js", function(){
|
||||
pluginService('cloudreve', $('.plugin_version').attr('version'));
|
||||
});
|
||||
</script>
|
||||
@@ -1,292 +0,0 @@
|
||||
# coding:utf-8
|
||||
|
||||
import sys
|
||||
import io
|
||||
import os
|
||||
import time
|
||||
import re
|
||||
|
||||
web_dir = os.getcwd() + "/web"
|
||||
if os.path.exists(web_dir):
|
||||
sys.path.append(web_dir)
|
||||
os.chdir(web_dir)
|
||||
|
||||
import core.mw as mw
|
||||
|
||||
app_debug = False
|
||||
if mw.isAppleSystem():
|
||||
app_debug = True
|
||||
|
||||
|
||||
def getPluginName():
|
||||
return 'cloudreve'
|
||||
|
||||
|
||||
def getPluginDir():
|
||||
return mw.getPluginDir() + '/' + getPluginName()
|
||||
|
||||
|
||||
def getServerDir():
|
||||
return mw.getServerDir() + '/' + getPluginName()
|
||||
|
||||
|
||||
def getInitDFile():
|
||||
current_os = mw.getOs()
|
||||
if current_os == 'darwin':
|
||||
return '/tmp/' + getPluginName()
|
||||
|
||||
if current_os.startswith('freebsd'):
|
||||
return '/etc/rc.d/' + getPluginName()
|
||||
|
||||
return '/etc/init.d/' + getPluginName()
|
||||
|
||||
|
||||
def getInitDTpl():
|
||||
path = getPluginDir() + "/init.d/" + getPluginName() + ".tpl"
|
||||
return path
|
||||
|
||||
|
||||
def getArgs():
|
||||
args = sys.argv[3:]
|
||||
tmp = {}
|
||||
args_len = len(args)
|
||||
|
||||
if args_len == 1:
|
||||
t = args[0].strip('{').strip('}')
|
||||
if t.strip() == '':
|
||||
tmp = []
|
||||
else:
|
||||
t = t.split(':')
|
||||
tmp[t[0]] = t[1]
|
||||
tmp[t[0]] = t[1]
|
||||
elif args_len > 1:
|
||||
for i in range(len(args)):
|
||||
t = args[i].split(':')
|
||||
tmp[t[0]] = t[1]
|
||||
return tmp
|
||||
|
||||
def checkArgs(data, ck=[]):
|
||||
for i in range(len(ck)):
|
||||
if not ck[i] in data:
|
||||
return (False, mw.returnJson(False, '参数:(' + ck[i] + ')没有!'))
|
||||
return (True, mw.returnJson(True, 'ok'))
|
||||
|
||||
|
||||
def getConf():
|
||||
path = getServerDir() + "/conf.ini"
|
||||
return path
|
||||
|
||||
def configTpl():
|
||||
path = getPluginDir() + '/tpl'
|
||||
pathFile = os.listdir(path)
|
||||
tmp = []
|
||||
for one in pathFile:
|
||||
file = path + '/' + one
|
||||
tmp.append(file)
|
||||
return mw.getJson(tmp)
|
||||
|
||||
|
||||
def readConfigTpl():
|
||||
args = getArgs()
|
||||
data = checkArgs(args, ['file'])
|
||||
if not data[0]:
|
||||
return data[1]
|
||||
|
||||
content = mw.readFile(args['file'])
|
||||
content = contentReplace(content)
|
||||
return mw.returnJson(True, 'ok', content)
|
||||
|
||||
|
||||
def status():
|
||||
data = mw.execShell(
|
||||
"ps -ef|grep cloudreve |grep -v grep | grep -v python | grep -v mdserver-web | awk '{print $2}'")
|
||||
|
||||
if data[0] == '':
|
||||
return 'stop'
|
||||
return 'start'
|
||||
|
||||
def contentReplace(content):
|
||||
service_path = mw.getServerDir()
|
||||
content = content.replace('{$ROOT_PATH}', mw.getFatherDir())
|
||||
content = content.replace('{$SERVER_PATH}', service_path)
|
||||
content = content.replace('{$SERVER_APP}', service_path + '/'+getPluginName())
|
||||
return content
|
||||
|
||||
def initDreplace():
|
||||
|
||||
file_tpl = getInitDTpl()
|
||||
service_path = mw.getServerDir()
|
||||
|
||||
initD_path = getServerDir() + '/init.d'
|
||||
if not os.path.exists(initD_path):
|
||||
os.mkdir(initD_path)
|
||||
file_bin = initD_path + '/' + getPluginName()
|
||||
|
||||
# initd replace
|
||||
if not os.path.exists(file_bin):
|
||||
content = mw.readFile(file_tpl)
|
||||
content = content.replace('{$SERVER_PATH}', service_path)
|
||||
mw.writeFile(file_bin, content)
|
||||
mw.execShell('chmod +x ' + file_bin)
|
||||
|
||||
# systemd
|
||||
systemDir = mw.systemdCfgDir()
|
||||
systemService = systemDir + '/' + getPluginName() + '.service'
|
||||
if os.path.exists(systemDir) and not os.path.exists(systemService):
|
||||
systemServiceTpl = getPluginDir() + '/init.d/' + getPluginName() + '.service.tpl'
|
||||
service_path = mw.getServerDir()
|
||||
se_content = mw.readFile(systemServiceTpl)
|
||||
se_content = se_content.replace('{$SERVER_PATH}', service_path)
|
||||
mw.writeFile(systemService, se_content)
|
||||
mw.execShell('systemctl daemon-reload')
|
||||
|
||||
return file_bin
|
||||
|
||||
|
||||
def alistOp(method):
|
||||
file = initDreplace()
|
||||
|
||||
current_os = mw.getOs()
|
||||
if current_os == "darwin":
|
||||
data = mw.execShell(file + ' ' + method)
|
||||
if data[1] == '':
|
||||
return 'ok'
|
||||
return data[1]
|
||||
|
||||
if current_os.startswith("freebsd"):
|
||||
data = mw.execShell('service ' + getPluginName() + ' ' + method)
|
||||
if data[1] == '':
|
||||
return 'ok'
|
||||
return data[1]
|
||||
|
||||
data = mw.execShell('systemctl ' + method + ' ' + getPluginName())
|
||||
if data[1] == '':
|
||||
return 'ok'
|
||||
return data[1]
|
||||
|
||||
|
||||
def start():
|
||||
return alistOp('start')
|
||||
|
||||
|
||||
def stop():
|
||||
return alistOp('stop')
|
||||
|
||||
|
||||
def restart():
|
||||
status = alistOp('restart')
|
||||
return status
|
||||
|
||||
|
||||
def reload():
|
||||
return alistOp('reload')
|
||||
|
||||
|
||||
def initdStatus():
|
||||
current_os = mw.getOs()
|
||||
if current_os == 'darwin':
|
||||
return "Apple Computer does not support"
|
||||
|
||||
if current_os.startswith('freebsd'):
|
||||
initd_bin = getInitDFile()
|
||||
if os.path.exists(initd_bin):
|
||||
return 'ok'
|
||||
|
||||
shell_cmd = 'systemctl status ' + \
|
||||
getPluginName() + ' | grep loaded | grep "enabled;"'
|
||||
data = mw.execShell(shell_cmd)
|
||||
if data[0] == '':
|
||||
return 'fail'
|
||||
return 'ok'
|
||||
|
||||
|
||||
def initdInstall():
|
||||
current_os = mw.getOs()
|
||||
if current_os == 'darwin':
|
||||
return "Apple Computer does not support"
|
||||
|
||||
# freebsd initd install
|
||||
if current_os.startswith('freebsd'):
|
||||
import shutil
|
||||
source_bin = initDreplace()
|
||||
initd_bin = getInitDFile()
|
||||
shutil.copyfile(source_bin, initd_bin)
|
||||
mw.execShell('chmod +x ' + initd_bin)
|
||||
mw.execShell('sysrc ' + getPluginName() + '_enable="YES"')
|
||||
return 'ok'
|
||||
|
||||
mw.execShell('systemctl enable ' + getPluginName())
|
||||
return 'ok'
|
||||
|
||||
|
||||
def initdUinstall():
|
||||
current_os = mw.getOs()
|
||||
if current_os == 'darwin':
|
||||
return "Apple Computer does not support"
|
||||
|
||||
if current_os.startswith('freebsd'):
|
||||
initd_bin = getInitDFile()
|
||||
os.remove(initd_bin)
|
||||
mw.execShell('sysrc ' + getPluginName() + '_enable="NO"')
|
||||
return 'ok'
|
||||
|
||||
mw.execShell('systemctl disable ' + getPluginName())
|
||||
return 'ok'
|
||||
|
||||
|
||||
def runLog():
|
||||
return getServerDir() + '/logs.pl'
|
||||
|
||||
def pSqliteDb(dbname='databases'):
|
||||
pos_file = getServerDir() + '/data/'
|
||||
file = pos_file + '/data.db'
|
||||
name = 'data'
|
||||
conn = mw.M(dbname).dbPos(pos_file, name)
|
||||
return conn
|
||||
|
||||
def getCloudrevePort():
|
||||
file = getConf()
|
||||
content = mw.readFile(file)
|
||||
rep = r'Listen\s*=\s*(.*)'
|
||||
tmp = re.search(rep, content)
|
||||
return tmp.groups()[0].strip()
|
||||
|
||||
def homePage():
|
||||
http_port = getCloudrevePort()
|
||||
ip = mw.getLocalIp()
|
||||
if mw.isAppleSystem():
|
||||
ip = '127.0.0.1'
|
||||
url = 'http://'+ip+""+str(http_port)
|
||||
# print(url)
|
||||
return mw.returnJson(True, 'ok!', url)
|
||||
|
||||
if __name__ == "__main__":
|
||||
func = sys.argv[1]
|
||||
if func == 'status':
|
||||
print(status())
|
||||
elif func == 'start':
|
||||
print(start())
|
||||
elif func == 'stop':
|
||||
print(stop())
|
||||
elif func == 'restart':
|
||||
print(restart())
|
||||
elif func == 'reload':
|
||||
print(reload())
|
||||
elif func == 'initd_status':
|
||||
print(initdStatus())
|
||||
elif func == 'initd_install':
|
||||
print(initdInstall())
|
||||
elif func == 'initd_uninstall':
|
||||
print(initdUinstall())
|
||||
elif func == 'conf':
|
||||
print(getConf())
|
||||
elif func == 'run_log':
|
||||
print(runLog())
|
||||
elif func == 'config_tpl':
|
||||
print(configTpl())
|
||||
elif func == 'read_config_tpl':
|
||||
print(readConfigTpl())
|
||||
elif func == 'home_page':
|
||||
print(homePage())
|
||||
else:
|
||||
print('error')
|
||||
@@ -1,17 +0,0 @@
|
||||
{
|
||||
"sort": 7,
|
||||
"ps": "一个支持多种存储的文件列表程序",
|
||||
"name": "cloudreve",
|
||||
"title": "cloudreve",
|
||||
"shell": "install.sh",
|
||||
"versions":["3.8.3"],
|
||||
"tip": "soft",
|
||||
"checks": "server/cloudreve",
|
||||
"path": "server/cloudreve",
|
||||
"display": 1,
|
||||
"author": "cloudreve",
|
||||
"date": "2024-10-14",
|
||||
"home": "https://cloudreve.org/",
|
||||
"type": 0,
|
||||
"pid": "5"
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
[Unit]
|
||||
Description=A file list program that supports multiple storage
|
||||
After=network.target
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
WorkingDirectory={$SERVER_PATH}/cloudreve
|
||||
ExecStart={$SERVER_PATH}/cloudreve/cloudreve
|
||||
ExecReload=/bin/kill -USR2 $MAINPID
|
||||
Restart=on-failure
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
@@ -1,46 +0,0 @@
|
||||
#!/bin/sh
|
||||
# chkconfig: 2345 55 25
|
||||
# description: cloudreve Service
|
||||
|
||||
### BEGIN INIT INFO
|
||||
# Provides: cloudreve
|
||||
# Required-Start: $all
|
||||
# Required-Stop: $all
|
||||
# Default-Start: 2 3 4 5
|
||||
# Default-Stop: 0 1 6
|
||||
# Short-Description: starts cloudreve
|
||||
# Description: starts the MDW-Web
|
||||
### END INIT INFO
|
||||
|
||||
# Simple cloudreve init.d script conceived to work on Linux systems
|
||||
# as it does use of the /proc filesystem.
|
||||
|
||||
app_start(){
|
||||
echo "Starting cloudreve server..."
|
||||
cd {$SERVER_PATH}/cloudreve
|
||||
./cloudreve >> {$SERVER_PATH}/cloudreve/logs.pl 2>&1 &
|
||||
}
|
||||
|
||||
app_stop(){
|
||||
echo "dztasks stopped"
|
||||
ps -ef| grep cloudreve | grep -v grep | grep -v python | grep -v sh | awk '{print $2}'| xargs kill
|
||||
}
|
||||
|
||||
|
||||
case "$1" in
|
||||
start)
|
||||
app_start
|
||||
;;
|
||||
stop)
|
||||
app_stop
|
||||
;;
|
||||
restart|reload)
|
||||
app_stop
|
||||
sleep 0.3
|
||||
app_start
|
||||
;;
|
||||
*)
|
||||
echo "Please use start or stop as first argument"
|
||||
;;
|
||||
esac
|
||||
|
||||
@@ -1,90 +0,0 @@
|
||||
#!/bin/bash
|
||||
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin:/opt/homebrew/bin
|
||||
export PATH
|
||||
|
||||
curPath=`pwd`
|
||||
rootPath=$(dirname "$curPath")
|
||||
rootPath=$(dirname "$rootPath")
|
||||
serverPath=$(dirname "$rootPath")
|
||||
|
||||
# cd /Users/midoks/Desktop/mwdev/server/mdserver-web/plugins/cloudreve && bash install.sh install 3.8.3
|
||||
# cd /www/server/mdserver-web/plugins/cloudreve && bash install.sh install 3.8.3
|
||||
|
||||
VERSION=$2
|
||||
|
||||
sysArch=`arch`
|
||||
sysName=`uname`
|
||||
|
||||
ALIST_ARCH_NAME=amd64
|
||||
if [ "$sysArch" = "arm64" ];then
|
||||
ALIST_ARCH_NAME=arm64
|
||||
elif [ "$sysArch" = "x86_64" ]; then
|
||||
ALIST_ARCH_NAME=amd64
|
||||
elif [ "$sysArch" = "aarch64" ]; then
|
||||
ALIST_ARCH_NAME=aarch64
|
||||
fi
|
||||
|
||||
ALIST_NAME=linux
|
||||
if [ "$sysName" = "Darwin" ];then
|
||||
ALIST_NAME=darwin
|
||||
fi
|
||||
|
||||
Install_App()
|
||||
{
|
||||
echo '正在安装脚本文件...'
|
||||
|
||||
mkdir -p $serverPath/source
|
||||
mkdir -p $serverPath/source/cloudreve
|
||||
|
||||
FILE_TGZ=cloudreve_${VERSION}_${ALIST_NAME}_${ALIST_ARCH_NAME}.tar.gz
|
||||
CLOUDREVE_DIR=$serverPath/source/cloudreve
|
||||
|
||||
if [ ! -f $CLOUDREVE_DIR/${FILE_TGZ} ];then
|
||||
wget -O $CLOUDREVE_DIR/${FILE_TGZ} https://github.com/cloudreve/Cloudreve/releases/download/${VERSION}/${FILE_TGZ}
|
||||
fi
|
||||
|
||||
mkdir -p $serverPath/cloudreve
|
||||
|
||||
cd $CLOUDREVE_DIR && tar -zxvf ${FILE_TGZ} -C $serverPath/cloudreve
|
||||
echo "${VERSION}" > $serverPath/cloudreve/version.pl
|
||||
|
||||
cd ${rootPath} && python3 ${rootPath}/plugins/cloudreve/index.py start
|
||||
cd ${rootPath} && python3 ${rootPath}/plugins/cloudreve/index.py initd_install
|
||||
|
||||
# cd $serverPath/cloudreve && ./alist admin set admin
|
||||
echo '安装cloudreve完成'
|
||||
}
|
||||
|
||||
Uninstall_App()
|
||||
{
|
||||
if [ -f /usr/lib/systemd/system/cloudreve.service ];then
|
||||
systemctl stop cloudreve
|
||||
systemctl disable cloudreve
|
||||
rm -rf /usr/lib/systemd/system/cloudreve.service
|
||||
systemctl daemon-reload
|
||||
fi
|
||||
|
||||
if [ -f /lib/systemd/system/cloudreve.service ];then
|
||||
systemctl stop cloudreve
|
||||
systemctl disable cloudreve
|
||||
rm -rf /lib/systemd/system/cloudreve.service
|
||||
systemctl daemon-reload
|
||||
fi
|
||||
|
||||
if [ -f $serverPath/cloudreve/initd/cloudreve ];then
|
||||
$serverPath/cloudreve/initd/cloudreve stop
|
||||
fi
|
||||
|
||||
if [ -d $serverPath/cloudreve ];then
|
||||
rm -rf $serverPath/cloudreve
|
||||
fi
|
||||
|
||||
echo "卸载cloudreve成功"
|
||||
}
|
||||
|
||||
action=$1
|
||||
if [ "${1}" = 'install' ];then
|
||||
Install_App
|
||||
else
|
||||
Uninstall_App
|
||||
fi
|
||||
@@ -1,82 +0,0 @@
|
||||
function alistPost(method, version, args,callback){
|
||||
var loadT = layer.msg('正在获取...', { icon: 16, time: 0, shade: 0.3 });
|
||||
|
||||
var req_data = {};
|
||||
req_data['name'] = 'cloudreve';
|
||||
req_data['func'] = method;
|
||||
req_data['version'] = version;
|
||||
|
||||
if (typeof(args) == 'string'){
|
||||
req_data['args'] = JSON.stringify(toArrayObject(args));
|
||||
} else {
|
||||
req_data['args'] = JSON.stringify(args);
|
||||
}
|
||||
|
||||
$.post('/plugins/run', req_data, function(data) {
|
||||
layer.close(loadT);
|
||||
if (!data.status){
|
||||
//错误展示10S
|
||||
layer.msg(data.msg,{icon:0,time:2000,shade: [10, '#000']});
|
||||
return;
|
||||
}
|
||||
|
||||
if(typeof(callback) == 'function'){
|
||||
callback(data);
|
||||
}
|
||||
},'json');
|
||||
}
|
||||
|
||||
function alistPostCallbak(method, version, args,callback){
|
||||
var loadT = layer.msg('正在获取...', { icon: 16, time: 0, shade: 0.3 });
|
||||
|
||||
var req_data = {};
|
||||
req_data['name'] = 'cloudreve';
|
||||
req_data['func'] = method;
|
||||
args['version'] = version;
|
||||
|
||||
if (typeof(args) == 'string'){
|
||||
req_data['args'] = JSON.stringify(toArrayObject(args));
|
||||
} else {
|
||||
req_data['args'] = JSON.stringify(args);
|
||||
}
|
||||
|
||||
$.post('/plugins/callback', req_data, function(data) {
|
||||
layer.close(loadT);
|
||||
if (!data.status){
|
||||
layer.msg(data.msg,{icon:0,time:2000,shade: [0.3, '#000']});
|
||||
return;
|
||||
}
|
||||
|
||||
if(typeof(callback) == 'function'){
|
||||
callback(data);
|
||||
}
|
||||
},'json');
|
||||
}
|
||||
|
||||
|
||||
function commonHomePage(){
|
||||
alistPost('home_page', '', {}, function(data){
|
||||
var rdata = $.parseJSON(data.data);
|
||||
window.open(rdata.data);
|
||||
});
|
||||
}
|
||||
|
||||
function alistCommonFunc(){
|
||||
var con = '';
|
||||
con += '<hr/><p class="conf_p" style="text-align:center;">\
|
||||
<button class="btn btn-default btn-sm" onclick="commonHomePage()">主页</button>\
|
||||
</p>';
|
||||
|
||||
$(".soft-man-con").html(con);
|
||||
}
|
||||
|
||||
function alistReadme(){
|
||||
|
||||
|
||||
var readme = '<ul class="help-info-text c7">';
|
||||
readme += '<li>记住安装时生成的用户+密码</li>';
|
||||
readme += '</ul>';
|
||||
|
||||
$('.soft-man-con').html(readme);
|
||||
}
|
||||
|
||||
@@ -1,78 +0,0 @@
|
||||
# plugins_cryptocurrency_trade
|
||||
|
||||
- https://docs.ccxt.com/en/latest/install.html#python-proxies
|
||||
|
||||
数字货币量化交易插件
|
||||
|
||||
```
|
||||
cd /www/server/mdserver-web && python3 plugins/cryptocurrency_trade/ccxt/okex/strategy/st_demo.py
|
||||
|
||||
python3 plugins/cryptocurrency_trade/ccxt/okex/strategy/st_demo.py
|
||||
|
||||
miniconda3
|
||||
|
||||
|
||||
|
||||
```
|
||||
|
||||
# 免费TV策略
|
||||
https://cn.tradingview.com/scripts/?script_type=strategies
|
||||
|
||||
|
||||
### tradingview pine 指标
|
||||
```
|
||||
// This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
|
||||
// © TenCloud
|
||||
|
||||
//@version=5
|
||||
indicator("第一个指标",shorttitle = "开心吧")
|
||||
|
||||
ema10 = ta.ema(close, 25)
|
||||
ema100 = ta.ema(close, 100)
|
||||
l1=plot(ema10, color=color.red,title = "ema10")
|
||||
l2=plot(ema100,color = color.green, title = "ema100")
|
||||
|
||||
|
||||
buy = ta.crossover(ema10,ema100)
|
||||
sell = ta.crossunder(ema10,ema100)
|
||||
|
||||
plotchar(buy, text = "buy", color=color.green)
|
||||
plotchar(sell, text="sell", color=color.red)
|
||||
|
||||
l_color = ema10>ema100 ? color.green:color.red
|
||||
|
||||
fill(l1,l2,color=color.new(l_color,70))
|
||||
|
||||
```
|
||||
|
||||
### tradingview pine 策略
|
||||
```
|
||||
// This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
|
||||
// © TenCloud
|
||||
|
||||
//@version=5
|
||||
strategy("第一个指标",shorttitle = "开心吧", overlay = true, initial_capital = 1000)
|
||||
|
||||
ema10 = ta.ema(close, 25)
|
||||
ema100 = ta.ema(close, 100)
|
||||
l1=plot(ema10, color=color.red,title = "ema10")
|
||||
l2=plot(ema100,color = color.green, title = "ema100")
|
||||
|
||||
|
||||
buy = ta.crossover(ema10,ema100)
|
||||
sell = ta.crossunder(ema10,ema100)
|
||||
|
||||
if buy
|
||||
strategy.entry("long1", strategy.long,1)
|
||||
|
||||
if sell
|
||||
strategy.close("long1", qty_percent = 100,comment = "平仓多单")
|
||||
// plotchar(buy, text = "buy", color=color.green)
|
||||
// plotchar(sell, text="sell", color=color.red)
|
||||
|
||||
// l_color = ema10>ema100 ? color.green:color.red
|
||||
|
||||
// fill(l1,l2,color=color.new(l_color,70))
|
||||
|
||||
|
||||
```
|
||||
@@ -1,555 +0,0 @@
|
||||
import ccxt
|
||||
|
||||
|
||||
from datetime import datetime
|
||||
import time
|
||||
import sys
|
||||
import json
|
||||
import os
|
||||
import glob
|
||||
import threading
|
||||
from pprint import pprint
|
||||
|
||||
|
||||
# print(os.getcwd())
|
||||
sys.path.append(os.getcwd() + "/class/core")
|
||||
import mw
|
||||
|
||||
# cd /www/server/mdserver-web && . bin/activate
|
||||
# python3 plugins/cryptocurrency_trade/ccxt/public_data/data.py run
|
||||
# python3 plugins/cryptocurrency_trade/ccxt/public_data/data.py long
|
||||
|
||||
# 查看支持的交易所
|
||||
# print(ccxt.exchanges)
|
||||
|
||||
# 代理设置
|
||||
# exchange = ccxt.poloniex({
|
||||
# 'proxies': {
|
||||
# 'http': 'http://127.0.0.1:1088'
|
||||
# },
|
||||
# })
|
||||
|
||||
# exchange = ccxt.poloniex()
|
||||
exchange = ccxt.okex()
|
||||
|
||||
|
||||
def getPluginName():
|
||||
return 'cryptocurrency_trade'
|
||||
|
||||
|
||||
def getPluginDir():
|
||||
return mw.getPluginDir() + '/' + getPluginName()
|
||||
|
||||
|
||||
def getServerDir():
|
||||
return mw.getServerDir() + '/' + getPluginName()
|
||||
|
||||
|
||||
def getConfigData():
|
||||
cfg_path = getServerDir() + "/data.cfg"
|
||||
if not os.path.exists(cfg_path):
|
||||
mw.writeFile(cfg_path, '{}')
|
||||
t = mw.readFile(cfg_path)
|
||||
return json.loads(t)
|
||||
|
||||
|
||||
def writeLog(log_str):
|
||||
if __name__ == "__main__":
|
||||
print(log_str)
|
||||
|
||||
log_file = getServerDir() + '/logs/datasource.log'
|
||||
mw.writeFileLog(log_str, log_file, 1 * 1024 * 1024)
|
||||
return True
|
||||
|
||||
|
||||
def isSetDbConf():
|
||||
data = getConfigData()
|
||||
if 'db' in data:
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def beforeDate(day=360):
|
||||
dd = time.time() - day * 86400
|
||||
d = datetime.fromtimestamp(dd)
|
||||
day = d.strftime("%Y-%m-%d")
|
||||
return day
|
||||
|
||||
|
||||
def getTextTimeShow(time):
|
||||
d = datetime.fromtimestamp(time)
|
||||
day = d.strftime("%Y-%m-%d %H:%M:%S")
|
||||
return day
|
||||
|
||||
|
||||
def isSqlError(mysqlMsg):
|
||||
# 检测数据库执行错误
|
||||
mysqlMsg = str(mysqlMsg)
|
||||
if "MySQLdb" in mysqlMsg:
|
||||
return mw.returnJson(False, 'MySQLdb组件缺失! <br>进入SSH命令行输入: pip install mysql-python | pip install mysqlclient==2.0.3')
|
||||
if "2002," in mysqlMsg:
|
||||
return mw.returnJson(False, '数据库连接失败,请检查数据库服务是否启动!')
|
||||
if "2003," in mysqlMsg:
|
||||
return mw.returnJson(False, "Can't connect to MySQL server on '127.0.0.1' (61)")
|
||||
if "using password:" in mysqlMsg:
|
||||
return mw.returnJson(False, '数据库密码错误')
|
||||
if "1045" in mysqlMsg:
|
||||
return mw.returnJson(False, '连接错误!')
|
||||
if "SQL syntax" in mysqlMsg:
|
||||
return mw.returnJson(False, 'SQL语法错误!')
|
||||
if "Connection refused" in mysqlMsg:
|
||||
return mw.returnJson(False, '数据库连接失败,请检查数据库服务是否启动!')
|
||||
if "1133" in mysqlMsg:
|
||||
return mw.returnJson(False, '数据库用户不存在!')
|
||||
if "1007" in mysqlMsg:
|
||||
return mw.returnJson(False, '数据库已经存在!')
|
||||
return None
|
||||
|
||||
|
||||
def pMysqlDb():
|
||||
# pymysql
|
||||
db = mw.getMyORM()
|
||||
data = getConfigData()
|
||||
db_data = data['db']
|
||||
db.setHost(db_data['db_host'])
|
||||
db.setPort(db_data['db_port'])
|
||||
db.setUser(db_data['db_user'])
|
||||
db.setPwd(db_data['db_pass'])
|
||||
db.setDbName(db_data['db_name'])
|
||||
return db
|
||||
|
||||
|
||||
def makeInsertSql(table, item):
|
||||
sql = "insert into " + table
|
||||
keyStr = '('
|
||||
valueStr = ' values('
|
||||
for i in item:
|
||||
# print i, item[i]
|
||||
keyStr += '`' + str(i) + '`,'
|
||||
valueStr += "'" + str(item[i]) + "',"
|
||||
|
||||
keyStrLen = len(keyStr)
|
||||
keyStr = keyStr[0:keyStrLen - 1]
|
||||
keyStr += ') '
|
||||
|
||||
valueStrLen = len(valueStr)
|
||||
valueStr = valueStr[0:valueStrLen - 1]
|
||||
valueStr += ') '
|
||||
|
||||
sql += keyStr
|
||||
sql += valueStr
|
||||
|
||||
return sql
|
||||
|
||||
|
||||
def makeReplaceSql(table, item):
|
||||
sql = "replace into " + table
|
||||
keyStr = '('
|
||||
valueStr = ' values('
|
||||
for i in item:
|
||||
# print i, item[i]
|
||||
keyStr += '`' + str(i) + '`,'
|
||||
valueStr += "'" + str(item[i]) + "',"
|
||||
|
||||
keyStrLen = len(keyStr)
|
||||
keyStr = keyStr[0:keyStrLen - 1]
|
||||
keyStr += ') '
|
||||
|
||||
valueStrLen = len(valueStr)
|
||||
valueStr = valueStr[0:valueStrLen - 1]
|
||||
valueStr += ') '
|
||||
|
||||
sql += keyStr
|
||||
sql += valueStr
|
||||
|
||||
return sql
|
||||
|
||||
|
||||
def makeUpdateSql(table, item, mid):
|
||||
sql = "update " + table + " set "
|
||||
keyStr = ''
|
||||
for i in item:
|
||||
keyStr += '`' + str(i) + '`=' + "'" + item[i] + "',"
|
||||
|
||||
keyStrLen = len(keyStr)
|
||||
keyStr = keyStr[0:keyStrLen - 1]
|
||||
sql += keyStr
|
||||
sql += " where id = '" + str(mid) + "'"
|
||||
return sql
|
||||
|
||||
|
||||
def dataToDb(table_name, data):
|
||||
pdb = pMysqlDb()
|
||||
for i in data:
|
||||
rdata = {
|
||||
'addtime': int(i[0] / 1000),
|
||||
'open': i[1],
|
||||
'high': i[2],
|
||||
'low': i[3],
|
||||
'close': i[4],
|
||||
'vol': i[5],
|
||||
}
|
||||
|
||||
# sql = """SELECT id FROM %s where addtime='%s' LIMIT 1""" % (
|
||||
# table_name, rdata['addtime'])
|
||||
# fdata = pdb.query(sql)
|
||||
# if fdata:
|
||||
# print(table_name + ":" + str(rdata['addtime']) + ", old to db ok")
|
||||
# else:
|
||||
# isql = makeReplaceSql(table_name, rdata)
|
||||
# r = pdb.execute(isql)
|
||||
# print(table_name + ":" + str(rdata['addtime']) + ", go to db ok")
|
||||
|
||||
isql = makeReplaceSql(table_name, rdata)
|
||||
r = pdb.execute(isql)
|
||||
# print(table_name + ":" + str(rdata['addtime']) + ", go to db ok")
|
||||
return True
|
||||
|
||||
|
||||
def makeTableName(input_type="btc", input_tf="1m"):
|
||||
table_name = "ct_%s_%s" % (input_type, input_tf)
|
||||
return table_name
|
||||
|
||||
|
||||
def isHasTable(input_type="btc", input_tf="1m"):
|
||||
pdb = pMysqlDb()
|
||||
|
||||
# input_type = 'btc'
|
||||
# input_tf = '1m'
|
||||
table_name = makeTableName(input_type, input_tf)
|
||||
mtable = pdb.query("show tables like '%s'" % (table_name,))
|
||||
if len(mtable) != 0:
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def createSql(input_type="btc", input_tf="1m"):
|
||||
pdb = pMysqlDb()
|
||||
|
||||
# input_type = 'btc'
|
||||
# input_tf = '1m'
|
||||
table_name = makeTableName(input_type, input_tf)
|
||||
|
||||
mtable = pdb.query("show tables like '%s'" % (table_name,))
|
||||
if len(mtable) != 0:
|
||||
return True
|
||||
|
||||
sql_tpl = getPluginDir() + "/conf/create.sql"
|
||||
content = mw.readFile(sql_tpl)
|
||||
content = content.replace("xx1", input_type)
|
||||
content = content.replace("xx2", input_tf)
|
||||
|
||||
res = pdb.execute(content)
|
||||
# pprint(res)
|
||||
return True
|
||||
|
||||
|
||||
def getDataFetch(symbol, start_time="2023-3-1", input_tf="1m", limit=500):
|
||||
start_time = datetime.strptime(start_time, '%Y-%m-%d')
|
||||
end_time = datetime.strptime(end_time, '%Y-%m-%d')
|
||||
|
||||
start_time_stamp = int(time.mktime(start_time.timetuple())) * 1000
|
||||
data = exchange.fetch_ohlcv(symbol, timeframe=input_tf,
|
||||
since=start_time_stamp, limit=limit)
|
||||
return data
|
||||
|
||||
|
||||
def getPointData(symbol, start_time=1677713220000, input_tf="1m", limit=500):
|
||||
data = exchange.fetch_ohlcv(
|
||||
symbol, timeframe=input_tf, since=start_time, limit=limit)
|
||||
return data
|
||||
|
||||
|
||||
def toUnixTimeSecond(tf="1m"):
|
||||
if tf.find("m") > -1:
|
||||
v = int(tf.replace("m", ''))
|
||||
return v * 60
|
||||
|
||||
if tf.find("h") > -1:
|
||||
v = int(tf.replace("h", ''))
|
||||
return v * 3600
|
||||
|
||||
if tf.find("d") > -1:
|
||||
v = int(tf.replace("d", ''))
|
||||
return v * 86400
|
||||
return 0
|
||||
|
||||
|
||||
def findAndUpdateData(tag, input_tf="1m", start_time="2023-1-1", limit=300):
|
||||
pdb = pMysqlDb()
|
||||
table_name = makeTableName(tag, input_tf)
|
||||
sql = 'SELECT addtime FROM ' + table_name + ' order by addtime desc LIMIT 1'
|
||||
qdata = pdb.query(sql)
|
||||
|
||||
start_time = datetime.strptime(start_time, '%Y-%m-%d')
|
||||
start_time = int(time.mktime(start_time.timetuple())) * 1000
|
||||
if len(qdata) != 0:
|
||||
start_time = int(qdata[0]['addtime']) * 1000
|
||||
|
||||
pre_time = toUnixTimeSecond(input_tf) * 5 * 1000
|
||||
start_time = start_time - pre_time
|
||||
# pprint(start_time)
|
||||
symbol = tag.upper() + "/USDT"
|
||||
data = getPointData(symbol, start_time, input_tf, limit)
|
||||
|
||||
# pprint(data)
|
||||
# print("------------lini===========")
|
||||
# pprint(data[1:])
|
||||
|
||||
if len(qdata) == 1:
|
||||
data = data[1:]
|
||||
|
||||
dataToDb(table_name, data)
|
||||
|
||||
now = time.strftime("%m/%d %H:%M:%S")
|
||||
data_len = len(data)
|
||||
msg_head = now + "|虚拟币:" + tag + ",tf:" + \
|
||||
input_tf + "!,总数:" + str(data_len)
|
||||
writeLog(msg_head)
|
||||
if data_len > 0:
|
||||
# print("new data time", data[data_len - 1][0])
|
||||
dt = getTextTimeShow(data[data_len - 1][0] / 1000)
|
||||
msg = now + "|最新日期:" + dt
|
||||
writeLog(msg)
|
||||
|
||||
table_name = makeTableName(tag, input_tf)
|
||||
|
||||
sql = "SELECT addtime FROM " + table_name + \
|
||||
" order by addtime desc limit 10000,1"
|
||||
qdata = pdb.query(sql)
|
||||
|
||||
if len(qdata) > 0:
|
||||
print(qdata)
|
||||
|
||||
# input_tf_time = toUnixTimeSecond(input_tf)
|
||||
# now_t = int(time.time())
|
||||
|
||||
# del_before_t = now_t - input_tf_time * 1000
|
||||
|
||||
sql = "delete from %s where addtime<'%d' " % (
|
||||
table_name, qdata[0]['addtime'],)
|
||||
writeLog("删除冗余数据:" + str(sql))
|
||||
pdb.execute(sql)
|
||||
return True
|
||||
|
||||
|
||||
def dataToDbTpl(tag='btc', input_tf="1m", start_time="2023-1-1", limit=300):
|
||||
# tag = "btc"
|
||||
# tf = "1m"
|
||||
# start_time = "2023-1-1"
|
||||
# end_time = "2023-3-2"
|
||||
symbol = tag.lower() + '/USDT'
|
||||
if not isHasTable(tag, input_tf):
|
||||
createSql(tag, input_tf)
|
||||
data = getDataFetch(symbol, start_time, input_tf, limit)
|
||||
table_name = makeTableName(tag, input_tf)
|
||||
|
||||
dataToDb(table_name, data)
|
||||
now = time.strftime("%m/%d %H:%M:%S")
|
||||
data_len = len(data)
|
||||
writeLog(now + "|数据获取成功!,总数:" + str(data_len))
|
||||
else:
|
||||
findAndUpdateData(tag, input_tf, start_time, limit)
|
||||
|
||||
return True
|
||||
|
||||
|
||||
def dataToDbList(input_tf="1m", start_time="2023-1-1"):
|
||||
|
||||
data = getConfigData()
|
||||
if not 'token' in data:
|
||||
writeLog("未设置同步配置,需要添加币种!")
|
||||
return
|
||||
|
||||
for t in data['token']:
|
||||
dataToDbTpl(t, input_tf, start_time)
|
||||
time.sleep(4)
|
||||
|
||||
|
||||
def dataRunToDb():
|
||||
data = getConfigData()
|
||||
if not 'db' in data:
|
||||
writeLog("数据库未设置!")
|
||||
return
|
||||
|
||||
tag = "btc"
|
||||
symbol = tag.upper() + '/USDT'
|
||||
|
||||
# pprint(exchange.fetch_ticker('XRP/USDT'))
|
||||
limit_count = 1
|
||||
start_time = "2023-1-1"
|
||||
end_time = "2023-3-2"
|
||||
tf = "1m"
|
||||
|
||||
if not isHasTable(tag, tf):
|
||||
createSql(tag, tf)
|
||||
data = getDataFetch(symbol, start_time, end_time, tf, limit_count)
|
||||
table_name = makeTableName(tag, tf)
|
||||
|
||||
dataToDb(table_name, data)
|
||||
china_datetime = str(datetime.now())
|
||||
data_len = len(data)
|
||||
writeLog(china_datetime + ":数据获取成功!,总数:" + str(data_len))
|
||||
else:
|
||||
findAndUpdateData(tag, tf, start_time, 30)
|
||||
return data
|
||||
|
||||
|
||||
def startTask():
|
||||
# 任务队列
|
||||
try:
|
||||
while True:
|
||||
time.sleep(5)
|
||||
except Exception as e:
|
||||
time.sleep(60)
|
||||
startTask()
|
||||
|
||||
|
||||
def setDaemon(t):
|
||||
if sys.version_info.major == 3 and sys.version_info.minor >= 10:
|
||||
t.daemon = True
|
||||
else:
|
||||
t.setDaemon(True)
|
||||
return t
|
||||
|
||||
|
||||
def dataDay():
|
||||
try:
|
||||
while True:
|
||||
if not isSetDbConf():
|
||||
print("数据库未设置!")
|
||||
else:
|
||||
start_time = beforeDate(2 * 365)
|
||||
dataToDbList("1d", start_time=start_time)
|
||||
time.sleep(3600)
|
||||
# time.sleep(10)
|
||||
except Exception as e:
|
||||
time.sleep(60)
|
||||
dataDay()
|
||||
|
||||
|
||||
def data4h():
|
||||
try:
|
||||
while True:
|
||||
if not isSetDbConf():
|
||||
print("数据库未设置!")
|
||||
else:
|
||||
start_time = beforeDate(365)
|
||||
dataToDbList("4h", start_time=start_time)
|
||||
time.sleep(10)
|
||||
except Exception as e:
|
||||
time.sleep(60)
|
||||
data4h()
|
||||
|
||||
|
||||
def data1h():
|
||||
try:
|
||||
while True:
|
||||
if not isSetDbConf():
|
||||
print("数据库未设置!")
|
||||
else:
|
||||
start_time = beforeDate(365)
|
||||
dataToDbList("1h", start_time=start_time)
|
||||
time.sleep(10)
|
||||
except Exception as e:
|
||||
print(e)
|
||||
time.sleep(60)
|
||||
data1h()
|
||||
|
||||
|
||||
def data15m():
|
||||
try:
|
||||
while True:
|
||||
if not isSetDbConf():
|
||||
print("数据库未设置!")
|
||||
else:
|
||||
start_time = beforeDate(365)
|
||||
dataToDbList("15m", start_time=start_time)
|
||||
time.sleep(10)
|
||||
except Exception as e:
|
||||
time.sleep(60)
|
||||
data15m()
|
||||
|
||||
|
||||
def data5m():
|
||||
try:
|
||||
while True:
|
||||
if not isSetDbConf():
|
||||
print("数据库未设置!")
|
||||
else:
|
||||
start_time = beforeDate(180)
|
||||
dataToDbList("5m", start_time=start_time)
|
||||
time.sleep(10)
|
||||
except Exception as e:
|
||||
time.sleep(60)
|
||||
data5m()
|
||||
|
||||
|
||||
def data1m():
|
||||
try:
|
||||
while True:
|
||||
if not isSetDbConf():
|
||||
print("数据库未设置!")
|
||||
else:
|
||||
start_time = beforeDate(30)
|
||||
dataToDbList("1m", start_time=start_time)
|
||||
time.sleep(10)
|
||||
except Exception as e:
|
||||
time.sleep(60)
|
||||
data1m()
|
||||
|
||||
|
||||
def longRun():
|
||||
|
||||
# 日线
|
||||
d1_tt = threading.Thread(target=dataDay)
|
||||
d1_tt = setDaemon(d1_tt)
|
||||
d1_tt.start()
|
||||
|
||||
# 4h
|
||||
h4_tt = threading.Thread(target=data4h)
|
||||
h4_tt = setDaemon(h4_tt)
|
||||
h4_tt.start()
|
||||
|
||||
# 1h
|
||||
h1_tt = threading.Thread(target=data1h)
|
||||
h1_tt = setDaemon(h1_tt)
|
||||
h1_tt.start()
|
||||
|
||||
# 15m
|
||||
m15_tt = threading.Thread(target=data15m)
|
||||
m15_tt = setDaemon(m15_tt)
|
||||
m15_tt.start()
|
||||
|
||||
# 5m
|
||||
m5_tt = threading.Thread(target=data5m)
|
||||
m5_tt = setDaemon(m5_tt)
|
||||
m5_tt.start()
|
||||
|
||||
# 1m
|
||||
# h1m_tt = threading.Thread(target=data1m)
|
||||
# h1m_tt = setDaemon(h1m_tt)
|
||||
# h1m_tt.start()
|
||||
|
||||
startTask()
|
||||
|
||||
|
||||
def dataRun():
|
||||
do_num = 0
|
||||
while True:
|
||||
if do_num > 1:
|
||||
break
|
||||
do_num = do_num + 1
|
||||
dataRunToDb()
|
||||
time.sleep(6)
|
||||
|
||||
if __name__ == "__main__":
|
||||
func = sys.argv[1]
|
||||
if func == 'run':
|
||||
data5m()()
|
||||
elif func == 'long':
|
||||
longRun()
|
||||
elif func == 'demo':
|
||||
writeLog('111')
|
||||
else:
|
||||
print('error')
|
||||
@@ -1,12 +0,0 @@
|
||||
#!/bin/bash
|
||||
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
|
||||
export PATH
|
||||
curPath=`pwd`
|
||||
|
||||
# bash plugins/cryptocurrency_trade/ccxt/public_data/data.sh
|
||||
|
||||
if [ -f ${curPath}/bin/activate ];then
|
||||
. ${curPath}/bin/activate
|
||||
fi
|
||||
|
||||
python3 plugins/cryptocurrency_trade/ccxt/public_data/data.py long
|
||||
@@ -1,19 +0,0 @@
|
||||
import time
|
||||
|
||||
from datetime import datetime
|
||||
china_datetime = datetime.now()
|
||||
print(china_datetime)
|
||||
|
||||
|
||||
# date()
|
||||
tt = time.time() - 180 * 86400
|
||||
|
||||
# print(date("%Y-%m-%d", time.time()))
|
||||
# t = datetime.strptime(str(time.time() - 180 * 86400), "%Y-%m-%d")
|
||||
# print(t)
|
||||
|
||||
|
||||
d = datetime.fromtimestamp(tt)
|
||||
# 精确到毫秒
|
||||
str1 = d.strftime("%Y-%m-%d")
|
||||
print(str1)
|
||||
@@ -1,352 +0,0 @@
|
||||
import ccxt
|
||||
|
||||
|
||||
import time
|
||||
import sys
|
||||
import json
|
||||
import os
|
||||
import glob
|
||||
import threading
|
||||
|
||||
import pandas as pd
|
||||
from decimal import Decimal
|
||||
from pprint import pprint
|
||||
from datetime import datetime
|
||||
|
||||
# print(os.getcwd())
|
||||
sys.path.append(os.getcwd() + "/class/core")
|
||||
import mw
|
||||
|
||||
exchange = ccxt.poloniex()
|
||||
|
||||
|
||||
def calc_ClosingPriceWithOutStopLossPrice(open_price, stype='buy', profit=0.5):
|
||||
# profit 百分比 %
|
||||
v = 0
|
||||
vf_len = len(str(open_price).split('.')[1])
|
||||
if stype == 'buy':
|
||||
v = open_price * (100 + profit) / 100
|
||||
else:
|
||||
v = open_price * (100 - profit) / 100
|
||||
return round(v, vf_len)
|
||||
|
||||
|
||||
def roundVal(price, compare_price):
|
||||
price = float(price)
|
||||
csplite = str(compare_price).split('.')
|
||||
clen = len(csplite)
|
||||
if clen == 2:
|
||||
vf_len = len(csplite[1])
|
||||
return round(price, vf_len)
|
||||
return price
|
||||
|
||||
|
||||
def roundValCeil(price, compare_price):
|
||||
price = float(price)
|
||||
csplite = str(compare_price).split('.')
|
||||
clen = len(csplite)
|
||||
if clen == 2:
|
||||
vf_len = len(csplite[1]) - 1
|
||||
return round(price, vf_len)
|
||||
return price
|
||||
|
||||
|
||||
def multiply(a1, a2):
|
||||
v = Decimal(str(a1)) * Decimal(str(a2))
|
||||
return v
|
||||
|
||||
|
||||
def addition(a1, a2):
|
||||
v = Decimal(str(a1)) + Decimal(str(a2))
|
||||
return v
|
||||
|
||||
|
||||
def subtract(a1, a2):
|
||||
v = Decimal(str(a1)) - Decimal(str(a2))
|
||||
return v
|
||||
|
||||
|
||||
def divided(a1, a2):
|
||||
v = Decimal(str(a1)) / Decimal(str(a2))
|
||||
return v
|
||||
|
||||
|
||||
def calc_ClosingPrice(open_price, stop_loss_price, stype='buy', profit=0.5):
|
||||
# profit 百分比 %
|
||||
v = 0
|
||||
vf_len = len(str(open_price).split('.')[1])
|
||||
if stype == 'buy':
|
||||
v = open_price * (100 + profit) / 100
|
||||
diff = open_price - stop_loss_price
|
||||
profit_price = open_price + diff * 1.5
|
||||
if profit_price > v:
|
||||
return round(profit_price, vf_len)
|
||||
else:
|
||||
v = open_price * (100 - profit) / 100
|
||||
diff = stop_loss_price - open_price
|
||||
profit_price = open_price - diff * 1.5
|
||||
if profit_price < v:
|
||||
return round(profit_price, vf_len)
|
||||
return round(v, vf_len)
|
||||
|
||||
|
||||
def toDateFromInt(time_unix, tf_format="%Y-%m-%d %H:%M:%S", time_zone="Asia/Shanghai"):
|
||||
# 取格式时间
|
||||
import time
|
||||
os.environ['TZ'] = time_zone
|
||||
time_str = time.localtime(time_unix)
|
||||
time.tzset()
|
||||
|
||||
return time.strftime(tf_format, time_str)
|
||||
|
||||
|
||||
def getPluginName():
|
||||
return 'cryptocurrency_trade'
|
||||
|
||||
|
||||
def getPluginDir():
|
||||
return mw.getPluginDir() + '/' + getPluginName()
|
||||
|
||||
|
||||
def getServerDir():
|
||||
return mw.getServerDir() + '/' + getPluginName()
|
||||
|
||||
|
||||
def getConfigData():
|
||||
cfg_path = getServerDir() + "/data.cfg"
|
||||
if not os.path.exists(cfg_path):
|
||||
mw.writeFile(cfg_path, '{}')
|
||||
t = mw.readFile(cfg_path)
|
||||
return json.loads(t)
|
||||
|
||||
|
||||
def getUserCfgData():
|
||||
data = getConfigData()
|
||||
if 'user' in data:
|
||||
try:
|
||||
udata = mw.deDoubleCrypt('mw', data['user'])
|
||||
udata = json.loads(udata)
|
||||
return udata
|
||||
except Exception as e:
|
||||
pass
|
||||
return []
|
||||
|
||||
|
||||
def initEx():
|
||||
data = getUserCfgData()
|
||||
# print(data)
|
||||
if (len(data) > 0):
|
||||
exchange = ccxt.okex({
|
||||
"apiKey": data['app_key'],
|
||||
"secret": data['secret'],
|
||||
"password": data['password'],
|
||||
})
|
||||
return exchange
|
||||
else:
|
||||
print("初始化失败,检查原因!")
|
||||
exchange = ccxt.poloniex()
|
||||
return exchange
|
||||
|
||||
|
||||
def toUnixTimeSecond(tf="1m"):
|
||||
if tf.find("m") > -1:
|
||||
v = int(tf.replace("m", ''))
|
||||
return v * 60
|
||||
|
||||
if tf.find("h") > -1:
|
||||
v = int(tf.replace("h", ''))
|
||||
return v * 3600
|
||||
|
||||
if tf.find("d") > -1:
|
||||
v = int(tf.replace("d", ''))
|
||||
return v * 86400
|
||||
return 0
|
||||
|
||||
|
||||
def notifyMsg(msg, tf='15m', tag='btc'):
|
||||
trigger_time = toUnixTimeSecond(tf)
|
||||
return mw.notifyMessage(msg, '量化交易/' + tag, trigger_time)
|
||||
|
||||
|
||||
def makeTableName(input_type="btc", input_tf="1m"):
|
||||
table_name = "ct_%s_%s" % (input_type, input_tf,)
|
||||
return table_name
|
||||
|
||||
|
||||
def writeLog(log_str):
|
||||
if __name__ == "__main__":
|
||||
print(log_str)
|
||||
|
||||
log_file = getServerDir() + '/logs/strategy.log'
|
||||
mw.writeFileLog(log_str, log_file)
|
||||
return True
|
||||
|
||||
|
||||
def writeLogEx(log_str, tag='btc'):
|
||||
tag = tag.replace('/', '_')
|
||||
# 各种币的详细API日志
|
||||
if __name__ == "__main__":
|
||||
print(log_str)
|
||||
|
||||
log_file = getServerDir() + '/logs/strategy_' + tag + '.log'
|
||||
mw.writeFileLog(log_str, log_file)
|
||||
return True
|
||||
|
||||
|
||||
def writeLogErrorEx(log_str, tag='btc'):
|
||||
tag = tag.replace('/', '_')
|
||||
# 各种币的详细API日志
|
||||
if __name__ == "__main__":
|
||||
print(log_str)
|
||||
|
||||
log_file = getServerDir() + '/logs/strategy_' + tag + '.err.log'
|
||||
mw.writeFileLog(log_str, log_file)
|
||||
return True
|
||||
|
||||
|
||||
def pMysqlDb():
|
||||
# pymysql
|
||||
db = mw.getMyORM()
|
||||
data = getConfigData()
|
||||
db_data = data['db']
|
||||
|
||||
# print(db_data)
|
||||
db.setHost(db_data['db_host'])
|
||||
db.setPort(db_data['db_port'])
|
||||
db.setUser(db_data['db_user'])
|
||||
db.setPwd(db_data['db_pass'])
|
||||
db.setDbName(db_data['db_name'])
|
||||
return db
|
||||
|
||||
|
||||
def getOnlineData(symbol, input_tf="15m", limit=200):
|
||||
bars = exchange.fetch_ohlcv(symbol, timeframe=input_tf, limit=limit)
|
||||
df = pd.DataFrame(bars[:], columns=['timestamp',
|
||||
'open', 'high', 'low', 'close', 'volume'])
|
||||
df['dt'] = pd.to_datetime(df['timestamp'], unit='ms')
|
||||
return df
|
||||
|
||||
|
||||
def toDataFrame(data):
|
||||
# print(data)
|
||||
data = sorted(data, key=lambda x: x["addtime"], reverse=False)
|
||||
|
||||
dfield = ['addtime', 'open', 'high', 'low', 'close']
|
||||
v = {}
|
||||
for dx in dfield:
|
||||
v[dx] = []
|
||||
|
||||
for x in data:
|
||||
for i in range(len(x)):
|
||||
field = dfield[i]
|
||||
# print(i, field)
|
||||
v[field].append(x[field])
|
||||
# pprint(data)
|
||||
frame = pd.DataFrame(v)
|
||||
# frame = frame.sort_values(by=['addtime'])
|
||||
|
||||
frame['dt'] = pd.to_datetime(frame['addtime'], unit='s')
|
||||
frame.set_index('dt', inplace=True)
|
||||
frame.index = frame.index.tz_localize('UTC').tz_convert('Asia/Shanghai')
|
||||
return frame
|
||||
|
||||
|
||||
def getDataFromDb(tf="1m", tag='btc', limit=1000):
|
||||
tn = makeTableName(tag, tf)
|
||||
sql = 'select addtime,open,high,low,close from ' + \
|
||||
tn + ' order by addtime desc limit ' + str(limit)
|
||||
|
||||
pdb = pMysqlDb()
|
||||
fdata = pdb.query(sql)
|
||||
# print(fdata)
|
||||
return fdata
|
||||
|
||||
|
||||
def getDataFromDb_DF(tf="5m", tag='btc', limit=1000):
|
||||
data = getDataFromDb(tf, tag, limit)
|
||||
rdata = toDataFrame(data)
|
||||
return rdata
|
||||
|
||||
|
||||
# 消息模板类
|
||||
class MsgTpl():
|
||||
|
||||
__name = ''
|
||||
__strategy_name = ''
|
||||
__time_frame = ''
|
||||
__content = ''
|
||||
__open_time = ''
|
||||
__strategy_dt = ''
|
||||
|
||||
__stop_loss_price = ''
|
||||
__closing_price = ''
|
||||
__open_price = ''
|
||||
|
||||
__msg = ''
|
||||
|
||||
def setName(self, name):
|
||||
self.__name = name
|
||||
|
||||
def setStrategyName(self, name):
|
||||
self.__strategy_name = name
|
||||
|
||||
def setTimeFrame(self, tf):
|
||||
self.__time_frame = tf
|
||||
|
||||
def setContent(self, content):
|
||||
self.__content = content
|
||||
|
||||
def setOpenTime(self, time):
|
||||
self.__open_time = toDateFromInt(time)
|
||||
|
||||
def setStrategicDt(self, stype='buy'):
|
||||
if stype == 'buy':
|
||||
self.__strategy_dt = '做多'
|
||||
else:
|
||||
self.__strategy_dt = '做空'
|
||||
|
||||
def setStopLossPrice(self, price):
|
||||
self.__stop_loss_price = price
|
||||
|
||||
def setClosingPrice(self, price):
|
||||
self.__closing_price = price
|
||||
|
||||
def setOpenPrice(self, price):
|
||||
self.__open_price = price
|
||||
|
||||
def setMsg(self, msg):
|
||||
self.__msg = msg
|
||||
|
||||
def toText(self):
|
||||
msg = ''
|
||||
msg += '名称:' + self.__name + "\n"
|
||||
|
||||
if self.__strategy_name != '':
|
||||
msg += '策略名称:' + self.__strategy_name + "\n"
|
||||
|
||||
if self.__time_frame != '':
|
||||
msg += '时间周期:' + self.__time_frame + "\n"
|
||||
|
||||
if self.__content != '':
|
||||
msg += '策略描述:' + self.__content + "\n"
|
||||
|
||||
if self.__open_time != '':
|
||||
msg += '开盘时间:' + self.__open_time + "\n"
|
||||
|
||||
if self.__strategy_dt != '':
|
||||
msg += '开仓方向:' + self.__strategy_dt + "\n"
|
||||
|
||||
if self.__open_price != '':
|
||||
msg += '开仓价:' + str(self.__open_price) + "\n"
|
||||
|
||||
if self.__stop_loss_price != '':
|
||||
msg += '止损价:' + str(self.__stop_loss_price) + "\n"
|
||||
|
||||
if self.__closing_price != '':
|
||||
msg += '止盈价:' + str(self.__closing_price) + "\n"
|
||||
|
||||
msg += '发送时间:' + mw.getDateFromNow() + "\n"
|
||||
|
||||
if self.__msg != '':
|
||||
msg += __msg
|
||||
return msg
|
||||
@@ -1,57 +0,0 @@
|
||||
# import ccxt
|
||||
# import talib
|
||||
|
||||
import sys
|
||||
import os
|
||||
import time
|
||||
# import pandas as pd
|
||||
from pprint import pprint
|
||||
from decimal import Decimal
|
||||
|
||||
sys.path.append(os.getcwd() + "/plugins/cryptocurrency_trade/strategy")
|
||||
import common
|
||||
|
||||
# cd /www/server/mdserver-web && . bin/activate
|
||||
# python3 plugins/cryptocurrency_trade/ccxt/strategy/func_test.py run
|
||||
|
||||
# common.notifyMsg("任务开始")
|
||||
|
||||
|
||||
def toUnixTimeSecond(tf="1m"):
|
||||
if tf.find("m") > -1:
|
||||
v = int(tf.replace("m", ''))
|
||||
return v * 60
|
||||
|
||||
if tf.find("h") > -1:
|
||||
v = int(tf.replace("h", ''))
|
||||
return v * 3600
|
||||
|
||||
if tf.find("d") > -1:
|
||||
v = int(tf.replace("d", ''))
|
||||
return v * 86400
|
||||
return 0
|
||||
|
||||
|
||||
def multiply(a1, a2):
|
||||
v = Decimal(str(a1)) * Decimal(str(a2))
|
||||
return v
|
||||
|
||||
# print(toUnixTimeSecond("1d"))
|
||||
|
||||
# f = 19911.2
|
||||
# s = common.calc_ClosingPrice(f, 19890.2, 'buy')
|
||||
# print(s)
|
||||
|
||||
# print(sys.version_info)
|
||||
# os.environ['TZ'] = 'Europe/London'
|
||||
# time.tzset()
|
||||
# t = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime())
|
||||
# print(t)
|
||||
|
||||
# v = multiply(26236.8, 0.003)
|
||||
# print(float(v))
|
||||
# print(v)
|
||||
|
||||
print(float('0.00255234') + float('-0.00000255234'))
|
||||
v1 = common.addition('0.00255234', '-0.00000255234')
|
||||
print(v1)
|
||||
@@ -1,440 +0,0 @@
|
||||
# cd /www/server/mdserver-web && . bin/activate
|
||||
# python3 plugins/cryptocurrency_trade/ccxt/strategy/gate_100.py run
|
||||
# python3 plugins/cryptocurrency_trade/ccxt/strategy/gate_100.py long
|
||||
|
||||
import ccxt
|
||||
import talib
|
||||
|
||||
import sys
|
||||
import os
|
||||
import time
|
||||
import json
|
||||
import pandas as pd
|
||||
# import pandas_ta as ta
|
||||
from pprint import pprint
|
||||
import numpy as np
|
||||
|
||||
sys.path.append(os.getcwd() + "/plugins/cryptocurrency_trade/strategy")
|
||||
import common
|
||||
|
||||
sys.path.append(os.getcwd() + "/class/core")
|
||||
import mw
|
||||
|
||||
exchange = ccxt.gate({
|
||||
"apiKey": '756e1ff80526cb0ac9620c75680fc506',
|
||||
"secret": '95ac89362056bcf12ce37aabff6eb7ec78185483105f39b4a35e8dc8db8b4d3c',
|
||||
})
|
||||
|
||||
exchange.load_markets()
|
||||
|
||||
# 默认开仓数据
|
||||
# default_open_num = 10
|
||||
|
||||
default_open = {
|
||||
"BTC/USDT": 0.01,
|
||||
'ETH/USDT': 30,
|
||||
'DOT/USDT': 30,
|
||||
'CEL/USDT': 30,
|
||||
}
|
||||
|
||||
default_sell = {
|
||||
"BTC/USDT": 0.01,
|
||||
'ETH/USDT': 0.01,
|
||||
'DOT/USDT': 5,
|
||||
'CEL/USDT': 85,
|
||||
}
|
||||
|
||||
|
||||
# 做多开仓
|
||||
def onBuyOrderTry(symbol, stop_loss_price, profit=0.005, timeframe='15m'):
|
||||
|
||||
# 信号只在一个周期内执行一次|start
|
||||
lock_file = common.getServerDir() + '/signal.json'
|
||||
if not os.path.exists(lock_file):
|
||||
mw.writeFile(lock_file, '{}')
|
||||
|
||||
stype = symbol.replace('/', '_') + '_' + timeframe
|
||||
trigger_time = common.toUnixTimeSecond(timeframe)
|
||||
lock_data = json.loads(mw.readFile(lock_file))
|
||||
if stype in lock_data:
|
||||
diff_time = time.time() - lock_data[stype]['do_time']
|
||||
if diff_time >= trigger_time:
|
||||
lock_data[stype]['do_time'] = time.time()
|
||||
else:
|
||||
return False, 0, 0
|
||||
else:
|
||||
lock_data[stype] = {'do_time': time.time()}
|
||||
|
||||
mw.writeFile(lock_file, json.dumps(lock_data))
|
||||
# 信号只在一个周期内执行一次|end
|
||||
|
||||
common.writeLogEx('------做多----------------------------------', symbol)
|
||||
|
||||
default_open_num = default_open[symbol]
|
||||
# 做多开仓 | 市价
|
||||
data = exchange.createMarketBuyOrder(
|
||||
symbol, default_open_num, {"tdMode": "cross"})
|
||||
|
||||
common.writeLogEx('开仓数据:', symbol)
|
||||
common.writeLogEx(json.dumps(data), symbol)
|
||||
|
||||
order_id = data['info']['ordId']
|
||||
order_data = exchange.fetchOrder(order_id, symbol)
|
||||
|
||||
common.writeLogEx('订单数据:', symbol)
|
||||
common.writeLogEx(json.dumps(order_data), symbol)
|
||||
|
||||
# 实际开场平均价
|
||||
open_price = order_data['info']['avgPx']
|
||||
|
||||
# 做多-止损价大于开仓价,重设止损价
|
||||
if float(stop_loss_price) <= float(open_price):
|
||||
stop_loss_price = float(open_price) * float((1 - 0.003))
|
||||
|
||||
# property_val = float(order_data['info']['accFillSz']) + float(order_data['info']['fee'])
|
||||
property_val = common.addition(
|
||||
order_data['info']['accFillSz'], order_data['info']['fee'])
|
||||
property_val = float(property_val)
|
||||
|
||||
# 可平仓的数量
|
||||
property_val = common.roundValCeil(
|
||||
property_val, order_data['info']['accFillSz'])
|
||||
|
||||
common.writeLogEx('可平仓资产:' + str(property_val), symbol)
|
||||
|
||||
# 止盈价
|
||||
diff = float(open_price) - float(stop_loss_price)
|
||||
closing_price_c = float(open_price) + (diff * 2)
|
||||
closing_price = float(open_price) * float((1 + profit))
|
||||
# 选择盈利多的
|
||||
if closing_price_c > closing_price:
|
||||
closing_price = closing_price_c
|
||||
|
||||
closing_price = common.roundVal(closing_price, stop_loss_price)
|
||||
# stop_loss_price = common.roundVal(stop_loss_price, open_price)
|
||||
common.writeLogEx('实际开仓价:' + str(open_price), symbol)
|
||||
common.writeLogEx('止盈价:' + str(closing_price), symbol)
|
||||
common.writeLogEx('止损价:' + str(stop_loss_price), symbol)
|
||||
|
||||
# 设置 - 止损价/止盈价
|
||||
sl_exchange_params = {
|
||||
'ccy': "USDT",
|
||||
'reduceOnly': True,
|
||||
'tdMode': "cross",
|
||||
'slOrdPx': "-1",
|
||||
'slTriggerPx': stop_loss_price,
|
||||
}
|
||||
|
||||
# 止损条件单
|
||||
common.writeLogEx('止损参数:' + json.dumps([symbol, 'limit', 'sell',
|
||||
property_val, stop_loss_price, sl_exchange_params]), symbol)
|
||||
sl_cond_data = exchange.create_order(
|
||||
symbol, 'limit', 'sell', property_val, stop_loss_price, sl_exchange_params)
|
||||
|
||||
common.writeLogEx('止损价数据:', symbol)
|
||||
common.writeLogEx(json.dumps(sl_cond_data), symbol)
|
||||
|
||||
# 止赢条件单
|
||||
tp_exchange_params = {
|
||||
'ccy': "USDT",
|
||||
'reduceOnly': True,
|
||||
'tdMode': "cross",
|
||||
'tpOrdPx': "-1",
|
||||
'tpTriggerPx': closing_price,
|
||||
}
|
||||
|
||||
common.writeLogEx('止盈参数:' + json.dumps([symbol, 'limit', 'sell',
|
||||
property_val, closing_price, tp_exchange_params]), symbol)
|
||||
tp_cond_data = exchange.create_order(
|
||||
symbol, 'limit', 'sell', property_val, closing_price, tp_exchange_params)
|
||||
|
||||
common.writeLogEx('止赢数据:', symbol)
|
||||
common.writeLogEx(json.dumps(tp_cond_data), symbol)
|
||||
|
||||
common.writeLogEx('------做多 end----------------------------------', symbol)
|
||||
return True, open_price, closing_price
|
||||
|
||||
|
||||
def onBuyOrder(symbol, stop_loss_price, profit=0.005, timeframe='15m'):
|
||||
# 做多开仓
|
||||
# profit 百分比
|
||||
try:
|
||||
return onBuyOrderTry(symbol, stop_loss_price, profit, timeframe)
|
||||
except Exception as e:
|
||||
common.writeLogErrorEx(mw.getTracebackInfo(), symbol)
|
||||
return False, 0, 0
|
||||
|
||||
|
||||
def onSellOrderTry(symbol, stop_loss_price, profit=0.005, timeframe='15m'):
|
||||
|
||||
# 信号只在一个周期内执行一次|start
|
||||
lock_file = common.getServerDir() + '/signal.json'
|
||||
if not os.path.exists(lock_file):
|
||||
mw.writeFile(lock_file, '{}')
|
||||
|
||||
stype = symbol.replace('/', '_') + '_' + timeframe
|
||||
trigger_time = common.toUnixTimeSecond(timeframe)
|
||||
lock_data = json.loads(mw.readFile(lock_file))
|
||||
if stype in lock_data:
|
||||
diff_time = time.time() - lock_data[stype]['do_time']
|
||||
if diff_time >= trigger_time:
|
||||
lock_data[stype]['do_time'] = time.time()
|
||||
else:
|
||||
return False, 0, 0
|
||||
else:
|
||||
lock_data[stype] = {'do_time': time.time()}
|
||||
|
||||
mw.writeFile(lock_file, json.dumps(lock_data))
|
||||
# 信号只在一个周期内执行一次|end
|
||||
common.writeLogEx('------做空----------------------------------', symbol)
|
||||
|
||||
# 计算借币卖币多多少,以USDT为基准
|
||||
# sell_num = float(default_open_num) / float(stop_loss_price)
|
||||
# sell_num = round(sell_num, 8)
|
||||
|
||||
sell_num = default_sell[symbol]
|
||||
# 做空开仓 | 市价
|
||||
data = exchange.createMarketSellOrder(
|
||||
symbol, sell_num, {"tdMode": "cross", 'ccy': "USDT"})
|
||||
|
||||
common.writeLogEx('开仓数据:', symbol)
|
||||
common.writeLogEx(json.dumps(data), symbol)
|
||||
|
||||
order_id = data['info']['ordId']
|
||||
order_data = exchange.fetchOrder(order_id, symbol)
|
||||
|
||||
common.writeLogEx('订单数据:', symbol)
|
||||
common.writeLogEx(json.dumps(order_data), symbol)
|
||||
|
||||
# 实际开场平均价
|
||||
open_price = order_data['info']['avgPx']
|
||||
|
||||
common.writeLogEx('可平仓资产:' + str(sell_num), symbol)
|
||||
|
||||
# 做空-止损价小于开仓价,重设止损价
|
||||
if float(stop_loss_price) <= float(open_price):
|
||||
stop_loss_price = float(open_price) * float((1 + 0.003))
|
||||
|
||||
# 止盈价
|
||||
diff = float(stop_loss_price) - float(open_price)
|
||||
closing_price_c = float(open_price) - (diff * 2)
|
||||
closing_price = float(open_price) * float((1 - profit))
|
||||
# 选择盈利多的
|
||||
if closing_price_c < closing_price:
|
||||
closing_price = closing_price_c
|
||||
|
||||
closing_price = common.roundVal(closing_price, open_price)
|
||||
stop_loss_price = common.roundVal(stop_loss_price, open_price)
|
||||
common.writeLogEx('实际开仓价:' + str(open_price), symbol)
|
||||
common.writeLogEx('止盈价:' + str(closing_price), symbol)
|
||||
common.writeLogEx('止损价:' + str(stop_loss_price), symbol)
|
||||
|
||||
# 设置 - 止损价
|
||||
sl_exchange_params = {
|
||||
'ccy': "USDT",
|
||||
'reduceOnly': True,
|
||||
'tdMode': "cross",
|
||||
'slOrdPx': "-1",
|
||||
'slTriggerPx': stop_loss_price,
|
||||
}
|
||||
|
||||
sl_amount = common.multiply(stop_loss_price, sell_num)
|
||||
# 解决平仓时,未全部平仓
|
||||
sl_amount = common.addition(sl_amount, 0.1)
|
||||
common.writeLogEx('止损总价值:' + str(sl_amount), symbol)
|
||||
common.writeLogEx('止损参数:' + json.dumps([symbol, 'limit', 'buy', float(
|
||||
sl_amount), stop_loss_price, sl_exchange_params]), symbol)
|
||||
sl_cond_data = exchange.create_order(
|
||||
symbol, 'limit', 'buy', sl_amount, stop_loss_price, sl_exchange_params)
|
||||
|
||||
common.writeLogEx('止损价数据:', symbol)
|
||||
common.writeLogEx(json.dumps(sl_cond_data), symbol)
|
||||
|
||||
tp_exchange_params = {
|
||||
'ccy': "USDT",
|
||||
'reduceOnly': True,
|
||||
'tdMode': "cross",
|
||||
'tpOrdPx': "-1",
|
||||
'tpTriggerPx': closing_price,
|
||||
}
|
||||
|
||||
# 设置 -止盈价
|
||||
tp_amount = common.multiply(closing_price, sell_num)
|
||||
# 解决平仓时,未全部平仓
|
||||
tp_amount = common.addition(tp_amount, 0.1)
|
||||
common.writeLogEx('止盈总价值:' + str(tp_amount), symbol)
|
||||
common.writeLogEx('止盈参数:' + json.dumps([symbol, 'limit', 'buy', float(
|
||||
tp_amount), closing_price, tp_exchange_params]), symbol)
|
||||
tp_cond_data = exchange.create_order(
|
||||
symbol, 'limit', 'buy', tp_amount, closing_price, tp_exchange_params)
|
||||
|
||||
common.writeLogEx('止盈价数据:', symbol)
|
||||
common.writeLogEx(json.dumps(tp_cond_data), symbol)
|
||||
|
||||
common.writeLogEx('------做空 end----------------------------------', symbol)
|
||||
return True, open_price, closing_price
|
||||
|
||||
|
||||
def onSellOrder(symbol, stop_loss_price, profit=0.005, timeframe='15m'):
|
||||
# 做空开仓
|
||||
# profit 百分比
|
||||
try:
|
||||
return onSellOrderTry(symbol, stop_loss_price, profit, timeframe)
|
||||
except Exception as e:
|
||||
common.writeLogErrorEx(mw.getTracebackInfo(), symbol)
|
||||
return False, 0, 0
|
||||
|
||||
|
||||
def getOnlineData(symbol, input_tf="15m", limit=230):
|
||||
bars = exchange.fetch_ohlcv(symbol, timeframe=input_tf, limit=limit)
|
||||
df = pd.DataFrame(bars[:], columns=['timestamp',
|
||||
'open', 'high', 'low', 'close', 'volume'])
|
||||
df['dt'] = pd.to_datetime(df['timestamp'], unit='ms')
|
||||
df.set_index('dt', inplace=True)
|
||||
df.index = df.index.tz_localize('UTC').tz_convert('Asia/Shanghai')
|
||||
return df
|
||||
|
||||
|
||||
def isKdj(last, last_pre):
|
||||
# 判断是否是金叉
|
||||
if (float(last_pre['macd']) < 0) and (float(last['macd']) > 0):
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def isDeadFork(last, last_pre):
|
||||
# 判断是否是死叉
|
||||
if (float(last_pre['macd']) > 0) and (float(last['macd']) < 0):
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
def getMACD(df, lenght=-60):
|
||||
if len(df['close'].values) < 100:
|
||||
return False, None
|
||||
|
||||
close_p = df['close'].values
|
||||
df['dif'], df['dea'], df['macd'] = talib.MACD(close_p,
|
||||
fastperiod=12,
|
||||
slowperiod=26,
|
||||
signalperiod=9)
|
||||
return df[lenght:]
|
||||
|
||||
|
||||
def getEMA(df):
|
||||
close_p = df['close'].values
|
||||
df['ema'] = talib.EMA(np.array(close_p), timeperiod=200)
|
||||
return df
|
||||
|
||||
|
||||
def doneMacd(data, tag, timeframe):
|
||||
data = getEMA(data)
|
||||
|
||||
ma_data = getMACD(data)
|
||||
# alen = len(data)
|
||||
# print(ma_data)
|
||||
|
||||
t_data = ma_data.tail(3)
|
||||
print(t_data)
|
||||
|
||||
last_pre = t_data.iloc[0]
|
||||
last = t_data.iloc[1]
|
||||
|
||||
print(last)
|
||||
# print(last.index)
|
||||
|
||||
# print('close:', last['close'], 'ema:', last['ema'])
|
||||
|
||||
obj = common.MsgTpl()
|
||||
symbol = tag.upper() + '/USDT'
|
||||
obj.setName(symbol)
|
||||
obj.setStrategyName("MACD检查")
|
||||
obj.setTimeFrame(timeframe)
|
||||
obj.setOpenTime(last['timestamp'] / 1000)
|
||||
|
||||
now_data = t_data.iloc[2]
|
||||
# print('now_data:', now_data)
|
||||
|
||||
# msg = obj.toText()
|
||||
# print(msg)
|
||||
|
||||
if isKdj(last, last_pre) and last['close'] > last['ema']:
|
||||
# if isKdj(last, last_pre):
|
||||
|
||||
# closing_price = common.calc_ClosingPrice(
|
||||
# now_data['close'], last_pre['low'], 'buy')
|
||||
# # print('closing_price:', closing_price)
|
||||
|
||||
# 做多止损点
|
||||
stop_loss_price = last_pre['low']
|
||||
buy_status, open_price, closing_price = onBuyOrder(
|
||||
symbol, stop_loss_price, 0.005, timeframe)
|
||||
|
||||
if buy_status:
|
||||
obj.setStrategicDt('buy')
|
||||
# 做多止损点
|
||||
obj.setStopLossPrice(str(stop_loss_price))
|
||||
obj.setOpenPrice(str(open_price))
|
||||
obj.setClosingPrice(str(closing_price))
|
||||
obj.setContent("检查到金叉状态")
|
||||
msg = obj.toText()
|
||||
print(msg)
|
||||
common.notifyMsg(msg, timeframe, tag)
|
||||
common.writeLog(msg)
|
||||
|
||||
if isDeadFork(last, last_pre) and last['close'] < last['ema']:
|
||||
# if isDeadFork(last, last_pre):
|
||||
# closing_price = common.calc_ClosingPrice(
|
||||
# now_data['close'], last_pre['high'], 'sell')
|
||||
|
||||
# 做空止损点
|
||||
stop_loss_price = last_pre['high']
|
||||
sell_status, open_price, closing_price = onSellOrder(
|
||||
symbol, stop_loss_price, 0.005, timeframe)
|
||||
if sell_status:
|
||||
obj.setStopLossPrice(str(stop_loss_price))
|
||||
obj.setOpenPrice(str(open_price))
|
||||
obj.setClosingPrice(str(closing_price))
|
||||
obj.setStrategicDt('sell')
|
||||
obj.setContent("检查到死叉状态")
|
||||
msg = obj.toText()
|
||||
print(msg)
|
||||
common.notifyMsg(msg, timeframe, tag)
|
||||
common.writeLog(msg)
|
||||
|
||||
|
||||
def mainProcess(tag, timeframe='15m'):
|
||||
symbol = tag.upper() + '/USDT'
|
||||
data = getOnlineData(symbol, timeframe)
|
||||
|
||||
doneMacd(data, tag, timeframe)
|
||||
|
||||
|
||||
def foreachList():
|
||||
tag_list = ['btc']
|
||||
|
||||
for tag in tag_list:
|
||||
mainProcess(tag, '15m')
|
||||
time.sleep(1)
|
||||
|
||||
|
||||
def longRun():
|
||||
while True:
|
||||
foreachList()
|
||||
time.sleep(3)
|
||||
|
||||
|
||||
def debug():
|
||||
while True:
|
||||
mainProcess('xrp', '1m')
|
||||
time.sleep(3)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
func = sys.argv[1]
|
||||
if func == 'long':
|
||||
longRun()
|
||||
elif func == 'run':
|
||||
debug()
|
||||
else:
|
||||
print('error')
|
||||
@@ -1,138 +0,0 @@
|
||||
import ccxt
|
||||
import talib
|
||||
|
||||
import sys
|
||||
import os
|
||||
import time
|
||||
import pandas as pd
|
||||
import pandas_ta as ta
|
||||
from pprint import pprint
|
||||
|
||||
sys.path.append(os.getcwd() + "/plugins/cryptocurrency_trade/strategy")
|
||||
import common
|
||||
|
||||
# cd /www/server/mdserver-web && . bin/activate
|
||||
# python3 plugins/cryptocurrency_trade/ccxt/strategy/hammer_robot.py run
|
||||
|
||||
|
||||
pd.set_option('display.max_rows', None)
|
||||
|
||||
exchange = common.initEx()
|
||||
|
||||
exchange.load_markets()
|
||||
|
||||
entry_rsi = 30
|
||||
exit_rsi = 40
|
||||
|
||||
|
||||
symbol = 'XRP/USDT'
|
||||
timeframe = '15m'
|
||||
|
||||
tf_mult = exchange.parse_timeframe(timeframe) * 1000
|
||||
|
||||
|
||||
def indicators(data):
|
||||
|
||||
data['rsi'] = data.ta.rsi(length=10)
|
||||
data['ema'] = data.ta.ema(length=200)
|
||||
|
||||
# close_p = data['close'].values
|
||||
# data['rsi'] = talib.RSI(close_p, timeperiod=10)
|
||||
# data['ema'] = talib.EMA(close_p, timeperiod=200)
|
||||
return data
|
||||
|
||||
|
||||
def check_buy_sell_signals(df):
|
||||
last_row_index = len(df.index) - 1
|
||||
lastest_rsi = round(df['rsi'].iloc[-1], 2)
|
||||
lastest_price = round(df['close'].iloc[-1], 5)
|
||||
lastest_ema = round(df['ema'].iloc[-1], 5)
|
||||
lastest_ts = df['timestamp'].iloc[-1]
|
||||
|
||||
msg = "lastest_rsi:" + str(lastest_rsi) + " < entry_rsi:" + str(entry_rsi)
|
||||
msg += ",lastest_price:" + \
|
||||
str(lastest_price) + " > lastest_ema:" + str(lastest_ema)
|
||||
print(msg)
|
||||
|
||||
long_cond = (lastest_rsi < entry_rsi) and (lastest_price > lastest_ema)
|
||||
if long_cond:
|
||||
print("买入")
|
||||
order = exchange.create_market_buy_order(symbol, 1)
|
||||
|
||||
closed_orders = exchange.fetchClosedOrders(symbol, limit=2)
|
||||
if len(closed_orders) > 0:
|
||||
print("closed_orders:", closed_orders)
|
||||
most_recent_closed_order = closed_orders[-1]
|
||||
diff = lastest_ts - most_recent_closed_order['timestamp']
|
||||
last_buy_signal_cnt = int(diff / tf_mult)
|
||||
|
||||
exit_cond = (lastest_rsi > exit_rsi) and (last_buy_signal_cnt > 10)
|
||||
if exit_cond:
|
||||
print("卖出")
|
||||
order = exchange.create_market_sell_order(symbol, 1)
|
||||
return
|
||||
|
||||
|
||||
def get_hammer(df, lenght):
|
||||
# 影线要大于body的多少倍
|
||||
factor = 2
|
||||
hl_range = df['high'] - df['low']
|
||||
|
||||
body_hi = df.apply(lambda x: max(x['close'], x['open']), axis=1)
|
||||
body_lo = df.apply(lambda x: min(x['close'], x['open']), axis=1)
|
||||
body = body_hi - body_lo
|
||||
|
||||
body_avg = ta.ema(body, lenght=lenght)
|
||||
small_body = body < body_avg
|
||||
|
||||
# 上下影线站body的百分比
|
||||
shadow_percent = 10
|
||||
|
||||
# 上影线
|
||||
up_shadow = df['high'] - body_hi
|
||||
dn_shadow = body_lo - df['low']
|
||||
has_up_shadow = up_shadow > shadow_percent / 100 * body
|
||||
has_dn_shadow = dn_shadow > shadow_percent / 100 * body
|
||||
|
||||
downtrend = df['close'] < ta.ema(df['close'], 50)
|
||||
bullish_hammer = downtrend & small_body & (body > 0) & (
|
||||
dn_shadow >= factor * body) & (has_up_shadow == False)
|
||||
return bullish_hammer
|
||||
|
||||
|
||||
def runBot():
|
||||
bars = exchange.fetch_ohlcv(symbol, timeframe=timeframe, limit=200)
|
||||
df = pd.DataFrame(bars[:], columns=['timestamp',
|
||||
'open', 'high', 'low', 'close', 'volume'])
|
||||
|
||||
# format='%Y-%m-%d %H:%M:%S',
|
||||
df['dt'] = pd.to_datetime(
|
||||
df['timestamp'], unit="ms")
|
||||
|
||||
df['hammer'] = get_hammer(df, 10)
|
||||
|
||||
lastest_hammer = df.iloc[-1, -1]
|
||||
lastest_price = df.iloc[-1, 0]
|
||||
|
||||
print("lastest_price:" + str(lastest_price))
|
||||
print("lastest_hammer:" + str(lastest_hammer))
|
||||
print(df.tail())
|
||||
|
||||
if lastest_hammer:
|
||||
print("购买,做多")
|
||||
notifyMsg("购买,做多")
|
||||
|
||||
|
||||
def longRunBot():
|
||||
common.notifyMsg("任务开始")
|
||||
while True:
|
||||
runBot()
|
||||
time.sleep(10)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
func = sys.argv[1]
|
||||
if func == 'run':
|
||||
longRunBot()
|
||||
else:
|
||||
print('error')
|
||||