Compare commits

..
11 Commits
Author SHA1 Message Date
Mr Chen 894c98702e Update CNAME 2025-08-06 23:47:40 +08:00
Mr Chen b39d95d29e Update CNAME 2025-08-06 23:44:10 +08:00
Mr Chen 1c66f691a0 Update index.html 2023-12-01 11:37:14 +08:00
Mr Chen aaaea5cb44 Update CNAME 2023-12-01 11:35:26 +08:00
midoks 349a7b9496 Create ads.txt 2023-07-05 15:38:16 +08:00
midoks 2c6d70f1d9 Update index.html 2023-07-05 15:36:26 +08:00
midoks 12b8afb7e9 up 2023-03-17 15:45:23 +08:00
midoks 01664afc0c Update index.html 2023-03-17 15:41:41 +08:00
midoks 5c059ffca5 up 2023-03-17 15:38:32 +08:00
Mr Chen d122f7812a Create CNAME 2023-03-17 15:25:41 +08:00
midoks fc5174ba64 up 2023-03-17 15:23:57 +08:00
1636 changed files with 380 additions and 246672 deletions
+2 -2
View File
@@ -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 &
+2 -2
View File
@@ -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 &
+2 -2
View File
@@ -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 &
+2 -2
View File
@@ -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 &
+3 -3
View File
@@ -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 &
+2 -2
View File
@@ -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 &
-16
View File
@@ -1,16 +0,0 @@
name: sync2gitee
on: [push]
jobs:
repo-sync:
runs-on: ubuntu-latest
steps:
- name: Mirror the Github organization repos to Gitee.
uses: Yikun/hub-mirror-action@master
with:
src: 'github/midoks'
dst: 'gitee/midoks'
dst_key: ${{ secrets.GITEE_PRIVATE_KEY }}
dst_token: ${{ secrets.GITEE_TOKEN }}
static_list: "mdserver-web"
force_update: true
+29 -44
View File
@@ -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,15 +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
+1
View File
@@ -0,0 +1 @@
www.midoks.icu
-201
View File
@@ -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.
-235
View File
@@ -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管理软件。我一看到,就知道这是我一直想要的页面化管理方式。
复制了后台管理界面,按照自己想要的方式写了一版。
![EulerOS](https://img.shields.io/badge/LINUX-EulerOS-blue?style=for-the-badge&logo=EulerOS)
![Debian](https://img.shields.io/badge/LINUX-Debian-blue?style=for-the-badge&logo=Debian)
![Ubuntu](https://img.shields.io/badge/LINUX-Ubuntu-blue?style=for-the-badge&logo=Ubuntu)
![Fedora](https://img.shields.io/badge/LINUX-Fedora-blue?style=for-the-badge&logo=Fedora)
![CentOS](https://img.shields.io/badge/LINUX-CentOS-blue?style=for-the-badge&logo=CentOS)
[![Wiki](https://img.shields.io/badge/MW-Wiki-red?style=for-the-badge&logo=wiki)](https://github.com/midoks/mdserver-web/wiki)
[![jsdelivr](https://data.jsdelivr.com/v1/package/gh/midoks/mdserver-web/badge)](https://www.jsdelivr.com/package/gh/midoks/mdserver-web)
* SSH终端工具
* 面板收藏功能
* 网站备份功能
* 插件方式管理
基本上可以使用,后续会继续优化!欢迎提供意见!
- 吹水组 - https://t.me/mdserver_web
- 交流论坛 - https://bbs.midoks.icu
```
如果出现问题,最好私给我面板信息。不要让我猜。如果不提供,不要提出问题,自行解决。 — 座右铭
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 |[![digitalvirt](https://digitalvirt.com/templates/BlueWhite/img/logo-dark.svg)](https://digitalvirt.com/aff.php?aff=154) | https://digitalvirt.com/aff.php?aff=154 | mdserver-web |
# Docker
- 由[DDS-Derek](https://github.com/DDS-Derek)开发维护。
- https://github.com/DDS-Derek/mdserver-web-Docker
```
docker run -itd --name mw-server --privileged=true -p 7200:7200 -p 80:80 -p 443:443 -p 888:888 ddsderek/mw-server:latest
```
### 版本更新 0.18.4
- 国内安装优化。
### JSDelivr安装地址
- 初始安装
```
bash <(curl --insecure -fsSL https://cdn.jsdelivr.net/gh/midoks/mdserver-web@latest/scripts/install.sh)
```
- 直接更新
```
bash <(curl --insecure -fsSL https://cdn.jsdelivr.net/gh/midoks/mdserver-web@latest/scripts/update.sh)
```
- 卸载脚本
```
wget --no-check-certificate -O uninstall.sh https://cdn.jsdelivr.net/gh/midoks/mdserver-web@latest/scripts/uninstall.sh && bash uninstall.sh
```
### 备用地址
- 初始安装
```
bash <(curl --insecure -fsSL https://raw.githubusercontent.com/midoks/mdserver-web/dev/scripts/install.sh)
bash <(curl --insecure -fsSL https://code.midoks.icu/midoks/mdserver-web/raw/branch/master/scripts/install.sh)
```
- 直接更新
```
bash <(curl --insecure -fsSL https://raw.githubusercontent.com/midoks/mdserver-web/master/scripts/update.sh)
```
- 卸载脚本
```
wget --no-check-certificate -O uninstall.sh https://raw.githubusercontent.com/midoks/mdserver-web/master/scripts/uninstall.sh && bash uninstall.sh
```
### 通用软件安装[命令行安装]
- 需已经安装mdserver-web
```
bash <(curl --insecure -fsSL https://raw.githubusercontent.com/midoks/mdserver-web/dev/scripts/quick/app.sh)
```
### DEV使用
```
bash <(curl --insecure -fsSL https://raw.githubusercontent.com/midoks/mdserver-web/dev/scripts/install.sh)
bash <(curl --insecure -fsSL https://raw.githubusercontent.com/midoks/mdserver-web/dev/scripts/update.sh)
bash <(curl --insecure -fsSL https://raw.githubusercontent.com/midoks/mdserver-web/dev/scripts/install_dev.sh)
bash <(curl --insecure -fsSL https://raw.githubusercontent.com/midoks/mdserver-web/dev/scripts/update_dev.sh)
wget --no-check-certificate -O install.sh https://raw.githubusercontent.com/midoks/mdserver-web/dev/scripts/install.sh && bash install.sh
wget --no-check-certificate -O uninstall.sh https://raw.githubusercontent.com/midoks/mdserver-web/dev/scripts/uninstall.sh && bash uninstall.sh
bash <(curl --insecure -fsSL https://raw.githubusercontent.com/midoks/mdserver-web/dev/scripts/quick/debug.sh)
bash <(curl --insecure -fsSL https://code.midoks.icu/midoks/mdserver-web/raw/branch/dev/scripts/install.sh)
bash <(curl --insecure -fsSL https://code.midoks.icu/midoks/mdserver-web/raw/branch/dev/scripts/update.sh)
bash <(curl --insecure -fsSL https://code.midoks.icu/midoks/mdserver-web/raw/branch/dev/scripts/install_dev.sh)
bash <(curl --insecure -fsSL https://code.midoks.icu/midoks/mdserver-web/raw/branch/dev/scripts/update_dev.sh)
```
### 旧版安装/更新
```
curl --insecure -fsSL https://raw.githubusercontent.com/midoks/mdserver-web/dev/scripts/old/install.sh | bash
curl --insecure -fsSL https://raw.githubusercontent.com/midoks/mdserver-web/dev/scripts/old/update.sh | bash
```
### 偶然问题
- 在安装/更新时,突然出现python插件pip,psutil,可尝试如下解决方案
```
cd /www/server/mdserver-web rm -rf lib
cd /www/server/mdserver-web && rm -rf lib64
cd /www/server/mdserver-web && rm -rf bin
cd /www/server/mdserver-web && rm -rf include
mw update/mw update_dev/mw dev
```
### 捐赠地址 USDT(TRC20)
TVbNgrpeGBGZVm5gTLa21ADP7RpnPFhjya
日行一善,以后必定大富大贵
### 支付宝赞助
[![截图](https://cdn.jsdelivr.net/gh/midoks/mdserver-web@latest/route/static/img/alipay_zz.png)](https://cdn.jsdelivr.net/gh/midoks/mdserver-web@latest/route/static/img/alipay_zz.png)
### 无图不真相
[![截图](https://cdn.jsdelivr.net/gh/midoks/mdserver-web@latest/route/static/mdw.jpg)](https://cdn.jsdelivr.net/gh/midoks/mdserver-web@latest/route/static/mdw.jpg)
### Stargazers over time
[![Stargazers over time](https://starchart.cc/midoks/mdserver-web.svg)](https://starchart.cc/midoks/mdserver-web)
### 感谢开发赞助
[![digitalvirt](https://digitalvirt.com/templates/BlueWhite/img/logo-dark.svg)](https://digitalvirt.com/aff.php?aff=154)
### 授权许可
本项目采用 Apache 开源授权许可证,完整的授权说明已放置在 [LICENSE](https://github.com/midoks/mdserver-web/blob/master/LICENSE) 文件中。
+1
View File
@@ -0,0 +1 @@
google.com, pub-7736695762519033, DIRECT, f08c47fec0942fa0
-8
View File
@@ -1,8 +0,0 @@
#!/bin/bash
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin:/usr/local/lib/python2.7/bin
find . -name .DS_Store -print0 | xargs -0 git rm -f --ignore-unmatch
find . -name .DS_Store | xargs rm -rf
find . -type d -name "*.pyc" | xargs rm -rf
-126
View File
@@ -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 eventlet -w 1 app:app
# cd ${DIR}/web && gunicorn -b :${port} -k geventwebsocket.gunicorn.workers.GeventWebSocketWorker -w 1 app:app
}
mw_start_panel(){
port=7200
if [ -f ${DIR}/data/port.pl ];then
port=$(cat ${DIR}/data/port.pl)
fi
cd ${DIR}/web && gunicorn -b :${port} -k eventlet -w 1 app:app
# cd ${DIR}/web && gunicorn -b :${port} -k geventwebsocket.gunicorn.workers.GeventWebSocketWorker -w 1 app:app
}
mw_start_bgtask(){
cd ${DIR}/web && gunicorn -c setting.py app:app
cd ${DIR} && python3 panel_task.py
}
mw_stop()
{
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
-86
View File
@@ -1,86 +0,0 @@
### 常用命令说明
- 面板相关命令
```
mw start | 启动面板服务器
mw stop | 停止面板服务器
mw restart | 重启面板服务器
mw default | 显示登录信息
mw db | 快捷连接MySQL
mw redis | 快捷连接Redis
mw valkey | 快捷连接valkey
mw mongodb | 快捷连接MongoDB
mw pgdb | 快捷连接PostgreSQL
----------------------------------------
mw open | 开启面板
mw close | 关闭面板
mw debug | 开发测试
mw venv | 进入虚拟环境
mw mirror | 切换镜像
mw install_app | 快捷安装常用软件
mw update | 更新到正式
mw dev/update_dev | 更新到开发
service mw [start|stop|reload|restart|status]
```
- OpenResty
```
systemctl [start|stop|reload|restart|status] openresty
```
- MySQL
```
systemctl [start|stop|reload|restart|status] mysql
```
- MariaDB
```
systemctl [start|stop|reload|restart|status] mariadb
```
- PHP
```
systemctl [start|stop|reload|restart|status] php[54-81]
systemctl start php71
```
- Redis
```
systemctl [start|stop|reload|restart|status] redis
```
- Memcached
```
systemctl [start|stop|reload|restart|status] memcached
```
- sphinx
```
systemctl [start|stop|reload|restart|status] sphinx
```
-71
View File
@@ -1,71 +0,0 @@
### 兼容性测试报告
快速测试命令
```
cd /www/server/mdserver-web/scripts/quick && bash debug.sh
cd /www/server/mdserver-web/plugins/php/versions && bash all_test.sh
```
| 系统名称 | 面板 | OpenResty |
| ----------------- |---------------|---------------|
| CentOS 7.9 |✅ |✅ |
| CentOS 8.4 |✅ |✅ |
| CentOS 8 Stream |✅ |✅ |
| CentOS 9 Stream |✅ |✅ |
| Debian 10.3 |✅ |✅ |
| Debian 11.3 |✅ |✅ |
| Debian 12.0 |✅ |✅ |
| Ubuntu 18.04 |✅ |✅ |
| Ubuntu 20.04 |✅ |✅ |
| Ubuntu 22.04 |✅ |✅ |
| Fedora 31 |✅ |✅ |
| Fedora 32 |✅ |✅ |
| AlmaLinux 9 |✅ |✅ |
| RockyLinux 8.6 |✅ |✅ |
| Arch Linux  |✅ |✅ |
| openSUSE 15.4 |✅ |✅ |
| 系统名称 |PHP53 |PHP54 |PHP55 |PHP56 |PHP70 |PHP71 |PHP72 |PHP73 |PHP74 |PHP80 |PHP81 |PHP82 |PHP83 |PHP84 |
| ----------------- |-------|-------|-------|-------|-------|-------|-------|-------|-------|-------|-------|-------|-------|-------|
| CentOS 7.9 |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |
| CentOS 8.4 |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |
| CentOS 8 Stream |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |
| CentOS 9 Stream |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |
| Debian 10.3 |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |
| Debian 11.3 |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |
| Debian 12 |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |
| Ubuntu 18.04 |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |
| Ubuntu 20.04 |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |
| Ubuntu 22.04 |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |
| Fedora 31 |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |
| Fedora 32 |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |
| AlmaLinux 9 |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |
| RockyLinux 8.6 |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |
| Arch Linux |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |
| openSUSE 15.4 |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |
| 系统名称 | MySQL55|MySQL56|MySQL57|MySQL80|
| ----------------- |-------|--------|-------|-------|
| CentOS 7.9 |✅ |✅ |✅ |✅ |
| CentOS 8.4 |✅ |✅ |✅ |✅ |
| CentOS 8 Stream |✅ |✅ |✅ |✅ |
| CentOS 9 Stream |✅ |✅ |✅ |✅ |
| Debian 10.3 |✅ |✅ |✅ |✅ |
| Debian 11.3 |✅ |✅ |✅ |✅ |
| Debian 12 |✅ |✅ |✅ |✅ |
| Ubuntu 18.04 |✅ |✅ |✅ |✅ |
| Ubuntu 20.04 |✅ |✅ |✅ |✅ |
| Ubuntu 22.04 |✅ |✅ |✅ |✅ |
| Fedora 31 |✅ |✅ |✅ |✅ |
| Fedora 32 |✅ |✅ |✅ |✅ |
| AlmaLinux 9 |✅ |✅ |✅ |✅ |
| RockyLinux 8.6 |✅ |✅ |✅ |✅ |
| Arch Linux |✅ |✅ |✅ |✅ |
| openSUSE 15.4 |✅ |✅ |✅ |✅ |
-4
View File
@@ -1,4 +0,0 @@
# 自动生成
certificate.pem - 证书信息
privateKey.pem - 私钥
-62
View File
@@ -1,62 +0,0 @@
FROM debian:10.12-slim
STOPSIGNAL SIGRTMIN+3
RUN rm -f /lib/systemd/system/multi-user.target.wants/* \
/etc/systemd/system/*.wants/* \
/lib/systemd/system/local-fs.target.wants/* \
/lib/systemd/system/sockets.target.wants/*udev* \
/lib/systemd/system/sockets.target.wants/*initctl* \
/lib/systemd/system/sysinit.target.wants/systemd-tmpfiles-setup* \
/lib/systemd/system/systemd-update-utmp*
RUN apt update -y && \
apt install -y devscripts && \
apt install -y wget zip unzip && \
apt-get install -y locales && \
localedef -i en_US -c -f UTF-8 -A /usr/share/locale/locale.alias en_US.UTF-8 && \
locale-gen en_US.UTF-8 && \
export LC_ALL=en_US.UTF-8 && \
export LANG=en_US.UTF-8 && \
export LANGUAGE=en_US.UTF-8 && \
localedef -i en_US -f UTF-8 en_US.UTF-8 && \
mkdir -p /www/server && \
mkdir -p /www/wwwroot && \
mkdir -p /www/wwwlogs && \
mkdir -p /www/backup/database && \
mkdir -p /www/backup/site && \
wget -O /tmp/dev.zip https://github.com/midoks/mdserver-web/archive/refs/heads/dev.zip && \
cd /tmp && unzip /tmp/dev.zip && \
mv -f /tmp/mdserver-web-dev /www/server/mdserver-web && \
rm -rf /tmp/dev.zip && \
rm -rf /tmp/mdserver-web-dev && \
cd /www/server/mdserver-web && \
bash scripts/install/debian.sh && \
cd /www/server/mdserver-web/ && \
/www/server/mdserver-web/bin/python tools.py username username && \
cd /www/server/mdserver-web/ && \
/www/server/mdserver-web/bin/python tools.py panel password
RUN cd /www/server/mdserver-web/plugins/php && \
bash install.sh install 74 && \
cd /www/server/mdserver-web/plugins/openresty && \
bash install.sh install 1.21.4 && \
cd /www/server/mdserver-web/plugins/mysql && \
bash install.sh install 5.6 && \
cd /www/server/mdserver-web/plugins/phpmyadmin && \
bash install.sh install 4.4.15 && \
systemctl enable openresty && \
systemctl enable php74 && \
systemctl enable mysql
RUN rm -rf /www/server/mysql/data
ADD ./start.sh /start.sh
ADD start.service /usr/lib/systemd/system/start.service
#RUN systemctl enable start
CMD [ "/lib/systemd/systemd", "log-level=info", "unit=sysinit.target" ]
EXPOSE 7200 80 443 888
VOLUME [ "/www" ]
-9
View File
@@ -1,9 +0,0 @@
[Unit]
Description=Customize the script
After=network.target
[Service]
ExecStart=/start.sh
[Install]
WantedBy=multi-user.target
-1
View File
@@ -1 +0,0 @@
#!/bin/bash
Binary file not shown.

After

Width:  |  Height:  |  Size: 55 KiB

+96
View File
@@ -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
View File
@@ -1 +0,0 @@
logs
-361
View File
@@ -1,361 +0,0 @@
# coding: utf-8
# ---------------------------------------------------------------------------------
# MW-Linux面板
# ---------------------------------------------------------------------------------
# copyright (c) 2018-∞(https://github.com/midoks/mdserver-web) All rights reserved.
# ---------------------------------------------------------------------------------
# Author: midoks <midoks@163.com>
# ---------------------------------------------------------------------------------
# ---------------------------------------------------------------------------------
# 计划任务
# ---------------------------------------------------------------------------------
import sys
import os
import json
import time
import threading
import psutil
web_dir = os.getcwd() + "/web"
os.chdir(web_dir)
sys.path.append(web_dir)
import core.mw as mw
import thisdb
from admin import setup
setup.init()
g_log_file = mw.getPanelTaskExecLog()
if not os.path.exists(g_log_file):
os.system("touch " + g_log_file)
def execShell(cmdstring, cwd=None, timeout=None, shell=True):
cmd = cmdstring + ' > ' + g_log_file + ' 2>&1'
return mw.execShell(cmd)
def writeLogs(data):
# 写输出日志
try:
fp = open(g_log_file, 'w+')
fp.write(data)
fp.close()
except:
pass
def downloadFile(url, filename):
# 下载文件
try:
import urllib
import socket
socket.setdefaulttimeout(300)
headers = ('User-Agent', 'Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.88 Safari/537.36')
opener = urllib.request.build_opener()
opener.addheaders = [headers]
urllib.request.install_opener(opener)
urllib.request.urlretrieve(url, filename=filename, reporthook=downloadHook)
if not mw.isAppleSystem():
os.system('chown www.www ' + filename)
writeLogs(filename + ' download success!')
except Exception as e:
writeLogs(str(e))
return True
def downloadHook(count, blockSize, totalSize):
# 下载文件进度回调
used = count * blockSize
pre = int((100.0 * used / totalSize))
speed = {'total': totalSize, 'used': used, 'pre': pre}
writeLogs(json.dumps(speed))
def runPanelTask():
# 站点过期检查
siteEdateCheck()
lock_file = mw.getTriggerTaskLockFile()
try:
if os.path.exists(lock_file):
bash_list = thisdb.getTaskList(status=-1)
for task in bash_list:
thisdb.setTaskStatus(task['id'], 0)
run_list = thisdb.getTaskList(status=0)
for run_task in run_list:
start = int(time.time())
thisdb.setTaskData(run_task['id'], start=start)
thisdb.setTaskStatus(run_task['id'], -1)
if run_task['type'] == 'download':
argv = run_task['cmd'].split('|mw|')
downloadFile(argv[0], argv[1])
elif run_task['type'] == 'execshell':
execShell(run_task['cmd'])
end = int(time.time())
thisdb.setTaskData(run_task['id'], end=end)
thisdb.setTaskStatus(run_task['id'], 1)
if thisdb.getTaskUnexecutedCount() < 1:
os.remove(lock_file)
except Exception as e:
print('runPanelTask:',mw.getTracebackInfo())
# 网站到期处理
def siteEdateCheck():
try:
from utils.site import sites as MwSites
website_edate = thisdb.getOption('website_edate', default='0000-00-00')
now_time_ymd = time.strftime('%Y-%m-%d', time.localtime())
if website_edate == now_time_ymd:
return False
site_list = thisdb.getSitesEdateList(now_time_ymd)
for site in site_list:
MwSites.instance().stop(site['id'])
thisdb.setOption('website_edate', now_time_ymd)
except Exception as e:
print('siteEdateCheck:',mw.getTracebackInfo())
# 任务队列
def startPanelTask():
try:
while True:
runPanelTask()
time.sleep(5)
except Exception as e:
print('startPanelTask:',mw.getTracebackInfo())
time.sleep(30)
startPanelTask()
def systemTask():
# 系统监控任务
from utils.system import monitor
try:
while True:
monitor_status = thisdb.getOption('monitor_status',type='monitor',default='open')
if monitor_status == 'open':
monitor.instance().run()
time.sleep(5)
except Exception as ex:
print('systemTask:',mw.getTracebackInfo())
time.sleep(30)
systemTask()
def panelPluginStatusCheck():
# 系统监控任务
from utils.plugin import plugin
try:
while True:
# start_t = time.time()
plugin.instance().autoCachePluginStatus()
# end_t = time.time()
time.sleep(60)
except Exception as ex:
print('panelPluginStatusCheck:',mw.getTracebackInfo())
time.sleep(120)
panelPluginStatusCheck()
# -------------------------------------- PHP监控 start --------------------------------------------- #
# 502错误检查线程
def check502Task():
try:
check_file = mw.getPanelDir() + '/data/502Task.pl'
while True:
if os.path.exists(check_file):
check502()
time.sleep(10)
except:
time.sleep(30)
check502Task()
def check502():
try:
verlist = [
'52', '53', '54', '55', '56', '70',
'71', '72', '73', '74', '80', '81',
'82', '83', '84'
]
for ver in verlist:
server_dir = mw.getServerDir()
php_path = server_dir + '/php/' + ver + '/sbin/php-fpm'
if not os.path.exists(php_path):
continue
if checkPHPVersion(ver):
continue
if startPHPVersion(ver):
print('检测到PHP-' + ver + '处理异常,已自动修复!')
mw.writeLog('PHP守护程序', '检测到PHP-' + ver + '处理异常,已自动修复!')
except Exception as e:
mw.writeLog('PHP守护程序', '自动修复异常:'+str(e))
# 处理指定PHP版本
def startPHPVersion(version):
server_dir = mw.getServerDir()
try:
# system
phpService = mw.systemdCfgDir() + '/php' + version + '.service'
if os.path.exists(phpService):
mw.execShell("systemctl restart php" + version)
if checkPHPVersion(version):
return True
# initd
fpm = server_dir + '/php/init.d/php' + version
php_path = server_dir + '/php/' + version + '/sbin/php-fpm'
if not os.path.exists(php_path):
if os.path.exists(fpm):
os.remove(fpm)
return False
if not os.path.exists(fpm):
return False
# 尝试重载服务
os.system(fpm + ' reload')
if checkPHPVersion(version):
return True
# 尝试重启服务
cgi = '/tmp/php-cgi-' + version + '.sock'
pid = server_dir + '/php/' + version + '/var/run/php-fpm.pid'
data = mw.execShell("ps -ef | grep php/" + version +" | grep -v grep|grep -v python |awk '{print $2}'")
if data[0] != '':
os.system("ps -ef | grep php/" + version + " | grep -v grep|grep -v python |awk '{print $2}' | xargs kill ")
time.sleep(0.5)
if not os.path.exists(cgi):
os.system('rm -f ' + cgi)
if not os.path.exists(pid):
os.system('rm -f ' + pid)
os.system(fpm + ' start')
if checkPHPVersion(version):
return True
# 检查是否正确启动
if os.path.exists(cgi):
return True
except Exception as e:
print('startPHPVersion:',mw.getTracebackInfo())
mw.writeLog('PHP守护程序', '自动修复异常:'+str(e))
return True
def checkPHPVersion(version):
# 检查指定PHP版本
try:
sock = mw.getFpmAddress(version)
data = mw.requestFcgiPHP(sock, '/phpfpm_status_' + version + '?json')
result = str(data, encoding='utf-8')
except Exception as e:
result = 'Bad Gateway'
# 检查openresty
if result.find('Bad Gateway') != -1:
return False
if result.find('HTTP Error 404: Not Found') != -1:
return False
# 检查Web服务是否启动
if result.find('Connection refused') != -1:
return False
return True
# -------------------------------------- PHP监控 end --------------------------------------------- #
# --------------------------------------OpenResty Auto Restart Start --------------------------------------------- #
# 解决acme.sh续签后,未起效。
def openrestyAutoRestart():
try:
while True:
# 检查是否安装
odir = mw.getServerDir() + '/openresty'
if not os.path.exists(odir):
time.sleep(86400)
continue
mw.opWeb('reload')
time.sleep(86400)
except Exception as e:
mw.writeLog('OpenResty检测', '自动修复异常:'+str(e))
time.sleep(86400)
# --------------------------------------OpenResty Auto Restart End --------------------------------------------- #
# ------------------------------------ OpenResty Restart At Once Start ------------------------------------------ #
def openrestyRestartAtOnce():
restart_nginx_tip = mw.getPanelDir()+'/data/restart_nginx.pl'
while True:
if os.path.exists(restart_nginx_tip):
os.remove(restart_nginx_tip)
mw.opWeb('reload')
time.sleep(1)
# ----------------------------------- OpenResty Restart At Once End ------------------------------------------ #
# --------------------------------------Panel Restart Start --------------------------------------------- #
def restartPanelService():
restart_tip = mw.getPanelDir()+'/data/restart.pl'
while True:
if os.path.exists(restart_tip):
print("restart panel")
os.remove(restart_tip)
mw.panelCmd('restart_panel')
time.sleep(1)
# --------------------------------------Panel Restart End --------------------------------------------- #
def setDaemon(t):
if sys.version_info.major == 3 and sys.version_info.minor >= 10:
t.daemon = True
else:
t.setDaemon(True)
return t
def run():
# 系统监控
sysTask = threading.Thread(target=systemTask)
sysTask = setDaemon(sysTask)
sysTask.start()
# PHP 502错误检查线程
php502 = threading.Thread(target=check502Task)
php502 = setDaemon(php502)
php502.start()
# OpenResty Restart At Once Start
oraos = threading.Thread(target=openrestyRestartAtOnce)
oraos = setDaemon(oraos)
oraos.start()
# OpenResty Auto Restart Start
oar = threading.Thread(target=openrestyAutoRestart)
oar = setDaemon(oar)
oar.start()
# Panel Plugin Status Check
pps = threading.Thread(target=panelPluginStatusCheck)
pps = setDaemon(pps)
pps.start()
# Panel Restart Start
rps = threading.Thread(target=restartPanelService)
rps = setDaemon(rps)
rps.start()
# 面板后台任务
startPanelTask()
if __name__ == "__main__":
run()
-391
View File
@@ -1,391 +0,0 @@
# coding=utf-8
# ---------------------------------------------------------------------------------
# MW-Linux面板
# ---------------------------------------------------------------------------------
# copyright (c) 2018-∞(https://github.com/midoks/mdserver-web) All rights reserved.
# ---------------------------------------------------------------------------------
# Author: midoks <midoks@163.com>
# ---------------------------------------------------------------------------------
# ---------------------------------------------------------------------------------
# 工具箱
# ---------------------------------------------------------------------------------
import sys
import os
import json
import time
import re
web_dir = os.getcwd() + "/web"
os.chdir(web_dir)
sys.path.append(web_dir)
from utils.firewall import Firewall as MwFirewall
import core.mw as mw
import thisdb
INIT_DIR = "/etc/rc.d/init.d"
if mw.isAppleSystem():
INIT_DIR = mw.getPanelDir() + "/scripts/init.d"
INIT_CMD = INIT_DIR + "/mw"
def mw_input_cmd(msg):
if sys.version_info[0] == 2:
in_val = raw_input(msg)
else:
in_val = input(msg)
return in_val
def getRemainLen(cmd, max_length=100):
cmd_len = len(cmd)
cmd_u8_len = len(cmd.encode('utf-8'))
return max_length-int((cmd_u8_len - cmd_len)/2+cmd_len)
def mwcli(mw_input=0):
panel_dir = mw.getPanelDir()
raw_tip = "========================================================================"
if not mw_input:
print("========================mdserver-web cli tools==========================")
cmd_list = [
'(1) 重启面板服务',
'(2) 停止面板服务',
'(3) 启动面板服务',
'(4) 重载面板服务',
'(5) 修改面板IP',
'(6) 修改面板端口',
'(7) 关闭安全入口',
'(10) 查看面板默认信息',
'(11) 修改面板密码',
'(12) 修改面板用户名',
'(13) 显示面板错误日志',
'(14) 关闭面板访问',
'(15) 开启面板访问',
'(20) 关闭BasicAuth认证',
'(21) 解除域名绑定',
'(22) 解除面板SSL绑定',
'(23) 开启IPV6支持',
'(24) 关闭IPV6支持',
'(25) 开启防火墙SSH端口',
'(26) 关闭二次验证',
'(27) 查看防火墙信息',
'(28) 自动识别防火墙端口到面板',
'(29) 自动识别配置站点信息',
'(100) 开启PHP52显示',
'(101) 关闭PHP52显示',
'(200) 切换Linux系统软件源',
'(201) 简单速度测试',
'(0) 取消'
]
cmd_list_num = len(cmd_list)
for index in range(cmd_list_num):
cmd = cmd_list[index]
if index % 2 == 0:
if index == (cmd_list_num-1):
print(cmd)
else:
print(cmd + " " * getRemainLen(cmd, 40), end="")
if index % 2 == 1:
print(cmd)
print(raw_tip)
try:
mw_input = input("请输入命令编号:")
if sys.version_info[0] == 3:
mw_input = int(mw_input)
except:
mw_input = 0
nums = [
1, 2, 3, 4, 5, 6, 7,
10, 11, 12, 13, 14, 15,
20, 21, 22, 23, 24, 25, 26, 27, 28, 29,
100, 101,
200, 201
]
if not mw_input in nums:
print(raw_tip)
print("已取消!")
exit()
if mw_input == 1:
os.system(INIT_CMD + " restart")
elif mw_input == 2:
os.system(INIT_CMD + " stop")
elif mw_input == 3:
os.system(INIT_CMD + " start")
elif mw_input == 4:
os.system(INIT_CMD + " reload")
elif mw_input == 5:
in_ip = mw_input_cmd("请输入设置的面板IP:")
in_ip = in_ip.strip()
ip_text = panel_dir + '/data/iplist.txt'
if not mw.isVaildIp(in_ip):
mw.echoInfo("【"+in_ip+"】: IP不合法")
return
mw.writeFile(ip_text, in_ip)
thisdb.setOption('server_ip', in_ip)
mw.echoInfo("设置面板IP: " + in_ip)
elif mw_input == 6:
in_port = mw_input_cmd("请输入新的面板端口:")
in_port_int = int(in_port.strip())
if in_port_int < 65536 and in_port_int > 0:
MwFirewall.instance().addAcceptPort(in_port, 'WEB面板[TOOLS修改]', 'port')
panel_port = panel_dir + '/data/port.pl'
mw.writeFile(panel_port, in_port)
os.system(INIT_CMD + " restart_panel")
os.system(INIT_CMD + " default")
else:
mw.echoInfo("端口范围在0-65536之间")
return
elif mw_input == 7:
thisdb.setOption('admin_path', '')
mw.echoInfo("关闭安全入口成功!")
elif mw_input == 10:
os.system(INIT_CMD + " default")
elif mw_input == 11:
input_pwd = mw_input_cmd("请输入新的面板密码:")
if len(input_pwd.strip()) < 5:
mw.echoInfo("错误,密码长度不能小于5位")
return
set_panel_pwd(input_pwd.strip(), True)
elif mw_input == 12:
input_user = mw_input_cmd("请输入新的面板用户名(>=5位):")
set_panel_username(input_user.strip())
elif mw_input == 13:
os.system('tail -100 ' + panel_dir + '/logs/panel_error.log')
elif mw_input == 14:
admin_close = thisdb.getOption('admin_close')
if admin_close == 'no':
thisdb.setOption('admin_close', 'yes')
mw.echoInfo("关闭面板访问成功!")
else:
mw.echoInfo("已关闭面板访问!")
elif mw_input == 15:
admin_close = thisdb.getOption('admin_close')
if admin_close == 'yes':
thisdb.setOption('admin_close', 'no')
mw.echoInfo("开启面板访问成功!")
else:
mw.echoInfo("已开启面板访问!")
elif mw_input == 20:
basic_auth = thisdb.getOptionByJson('basic_auth', default={'open':False})
if basic_auth['open']:
basic_auth['open'] = False
thisdb.setOption('basic_auth', json.dumps(basic_auth))
os.system(INIT_CMD + " restart")
mw.echoInfo("关闭basic_auth成功")
elif mw_input == 21:
panel_domain = thisdb.getOption('panel_domain', default='')
if panel_domain != '':
thisdb.setOption('panel_domain', '')
os.system(INIT_CMD + " unbind_domain")
mw.echoInfo("解除域名绑定成功")
else:
mw.echoInfo("面板未绑定域名!")
elif mw_input == 22:
panel_ssl = thisdb.getOptionByJson('panel_ssl', default={'open':False})
if panel_ssl['open']:
panel_ssl['open'] = False
thisdb.setOption('panel_ssl', json.dumps(panel_ssl))
os.system(INIT_CMD + " unbind_ssl")
mw.echoInfo("解除面板SSL绑定成功")
elif mw_input == 23:
listen_ipv6 = panel_dir + '/data/ipv6.pl'
if not os.path.exists(listen_ipv6):
mw.writeFile(listen_ipv6, 'True')
os.system(INIT_CMD + " restart")
mw.echoInfo("开启IPv6支持了")
else:
mw.echoInfo("已开启IPv6支持!")
elif mw_input == 24:
listen_ipv6 = panel_dir + '/data/ipv6.pl'
if not os.path.exists(listen_ipv6):
mw.echoInfo("已关闭IPv6支持!")
else:
os.remove(listen_ipv6)
os.system(INIT_CMD + " restart")
mw.echoInfo("关闭IPv6支持了")
elif mw_input == 25:
open_ssh_port()
mw.echoInfo("已开启!")
elif mw_input == 26:
two_step_verification = thisdb.getOptionByJson('two_step_verification', default={'open':False})
if two_step_verification['open']:
two_step_verification['open'] = False
thisdb.setOption('two_step_verification', json.dumps(two_step_verification))
mw.echoInfo("关闭二次验证成功!")
else:
mw.echoInfo("二次验证已关闭!")
elif mw_input == 27:
cmd = 'which ufw'
run_cmd = False
find_cmd = mw.execShell(cmd)
if find_cmd[0].strip() != '':
run_cmd = True
os.system('ufw status')
cmd = 'which firewall-cmd'
find_cmd = mw.execShell(cmd)
if find_cmd[0].strip() != '':
run_cmd = True
os.system('firewall-cmd --list-all')
if not run_cmd:
mw.echoInfo("未检测到防火墙!")
elif mw_input == 28:
MwFirewall.instance().aIF()
mw.echoInfo("执行自动识别防火墙端口到面板成功!")
elif mw_input == 29:
from utils.site_reflect import parse as MwParse
MwParse()
mw.echoInfo("自动识别配置站点信息成功!")
elif mw_input == 100:
php_conf = panel_dir + '/plugins/php/info.json'
if os.path.exists(php_conf):
cont = mw.readFile(php_conf)
cont = re.sub("\"53\"", "\"52\",\"53\"", cont)
cont = re.sub("\"5.3.29\"", "\"5.2.17\",\"5.3.29\"", cont)
mw.writeFile(php_conf, cont)
mw.echoInfo("执行PHP52显示成功!")
elif mw_input == 101:
php_conf = panel_dir + '/plugins/php/info.json'
if os.path.exists(php_conf):
cont = mw.readFile(php_conf)
cont = re.sub("\"52\",", "", cont)
cont = re.sub("\"5.2.17\",", cont)
mw.writeFile(php_conf, cont)
mw.echoInfo("执行PHP52隐藏成功!")
elif mw_input == 200:
os.system("bash <(curl -sSL https://linuxmirrors.cn/main.sh)")
# os.system(INIT_CMD + " mirror")
elif mw_input == 201:
os.system('curl -Lso- bench.sh | bash')
def open_ssh_port():
find_ssh_port_cmd = "cat /etc/ssh/sshd_config | grep '^Port \\d*' | tail -1"
cmd_data = mw.execShell(find_ssh_port_cmd)
ssh_port = cmd_data[0].replace("Port ", '').strip()
if ssh_port == '':
ssh_port = '22'
mw.echoInfo("SSH端口: "+ str(ssh_port))
MwFirewall.instance().addAcceptPort(ssh_port, 'SSH远程管理服务', 'port')
return True
def set_panel_pwd(password, ncli=False):
info = thisdb.getUserByRoot()
thisdb.setUserByRoot(password=password)
if ncli:
mw.echoInfo("username: " + info['name'])
mw.echoInfo("password: " + password)
else:
print(username)
def show_panel_pwd():
# 面板密码展示
info = thisdb.getUserByRoot()
defailt_pwd_file = mw.getPanelDir()+'/data/default.pl'
pwd = ''
if os.path.exists(defailt_pwd_file):
pwd = mw.readFile(defailt_pwd_file).strip()
if mw.md5(pwd) == info['password']:
mw.echoInfo('password: ' + pwd)
return
print("*-password has been changed!")
def show_panel_adminpath():
admin_path = thisdb.getOption('admin_path')
if admin_path == '':
print('/login')
else:
print('/'+admin_path)
def set_panel_username(username=None):
# 随机面板用户名
if username:
if len(username) < 5:
mw.echoInfo("错误,用户名长度不能少于5位")
return
if username in ['admin', 'root']:
mw.echoInfo("错误,不能使用过于简单的用户名")
return
thisdb.setUserByRoot(name=username)
mw.echoInfo("username: %s" % username)
return
info = thisdb.getUserByRoot()
if info['name'] == 'admin':
username = mw.getRandomString(8).lower()
thisdb.setUserByRoot(name=username)
mw.echoInfo('username: ' + info['name'])
def getServerIp():
version = sys.argv[2]
# ip = mw.execShell(
# "curl --insecure -{} -sS --connect-timeout 5 -m 60 https://v6r.ipip.net/?format=text".format(version))
ip = mw.execShell(
"curl --insecure -{} -sS --connect-timeout 5 -m 60 https://ip.cachecha.com/?format=text".format(version))
# print(ip[0])
return ip[0]
def getPanelSslType():
scheme = 'http'
panel_ssl = thisdb.getOptionByJson('panel_ssl', default={'open':False})
if panel_ssl['open']:
scheme = 'https'
return scheme
def getPanelBindDomain():
return thisdb.getOption('panel_domain', default='')
def main():
if len(sys.argv) == 1:
print('ERROR: Parameter error!')
exit(-2)
method = sys.argv[1]
if method == 'panel':
set_panel_pwd(sys.argv[2])
elif method == 'username':
if len(sys.argv) > 2:
set_panel_username(sys.argv[2])
else:
set_panel_username()
elif method == 'password':
show_panel_pwd()
elif method == 'test':
thisdb.getOption('admin_path')
elif method == 'admin_path':
show_panel_adminpath()
elif method == 'getServerIp':
print(getServerIp())
elif method == 'panel_ssl_type':
print(getPanelSslType())
elif method == 'panel_bind_domain':
print(getPanelBindDomain())
elif method == "cli":
clinum = 0
try:
if len(sys.argv) > 2:
clinum = int(sys.argv[2]) if sys.argv[2][:6] else sys.argv[2]
except:
clinum = sys.argv[2]
mwcli(clinum)
else:
print('ERROR: Parameter error')
if __name__ == "__main__":
main()
@@ -1,34 +0,0 @@
CREATE TABLE IF NOT EXISTS `dnsapi` (
`id` INTEGER PRIMARY KEY AUTOINCREMENT,
`name` TEXT,
`type` TEXT,
`val` TEXT,
`remark` TEXT,
`addtime` TEXT
);
CREATE TABLE IF NOT EXISTS `email` (
`id` INTEGER PRIMARY KEY AUTOINCREMENT,
`addr` TEXT,
`remark` TEXT,
`addtime` TEXT
);
CREATE TABLE IF NOT EXISTS `domain` (
`id` INTEGER PRIMARY KEY AUTOINCREMENT,
`domain` TEXT,
`dnsapi_id` TEXT,
`email` TEXT,
`remark` TEXT,
`effective_date` TEXT default '',
`expiration_date` TEXT default '',
`error` TEXT,
`status` INTEGER default '0',
`addtime` TEXT
);
-- ALTER TABLE `domain` ADD COLUMN `effective_date` TEXT DEFAULT '';
-- ALTER TABLE `domain` ADD COLUMN `expiration_date` TEXT DEFAULT '';
-- ALTER TABLE `domain` ADD COLUMN `error` TEXT DEFAULT '';
-- ALTER TABLE `domain` ADD COLUMN `status` INTEGER DEFAULT '0';
@@ -1,136 +0,0 @@
# coding:utf-8
import sys
import io
import os
import time
import re
import requests
import base64
# 8001 / 7788
goedge_addr = 'http://127.0.0.2:8009'
access_keyid = "xxx"
access_key = "xxx"
# 指定用户
userId = 1
if sys.platform != "darwin":
os.chdir("/www/server/mdserver-web")
web_dir = os.getcwd() + "/web"
if os.path.exists(web_dir):
sys.path.append(web_dir)
os.chdir(web_dir)
import core.mw as mw
domain = sys.argv[1]
ssl_path = sys.argv[2]
def getToken():
api_url = goedge_addr+'/APIAccessTokenService/getAPIAccessToken'
post_data = {
"type": "admin",
"accessKeyId": access_keyid,
"accessKey": access_key
}
data = requests.post(api_url,json=post_data)
data_obj = data.json()
return data_obj['data']['token']
token = getToken()
def commonReq(url, data):
headers = {
'X-Edge-Access-Token': token
}
api_url = goedge_addr+url
resp_data = requests.post(api_url,json=data, headers=headers)
return resp_data.json()
def listSSLCerts(domain):
request_data = {
"userId":userId,
"isCA":False,
"keyword": "ACME泛域名自动上传",
"domains":[domain,"*."+domain],
"size":1
}
response_data = commonReq('/SSLCertService/listSSLCerts', request_data)
data = response_data['data']['sslCertsJSON']
data = mw.base64StrDecode(data)
data = mw.getObjectByJson(data)
# print(data)
return data
# createSSLCert(domain)
def createSSLCert(domain, did=0):
ssl_cer_file = ssl_path + '/'+domain+'.cer'
if not os.path.exists(ssl_cer_file):
print("没有有效证书!")
return ''
# print(ssl_cer_file)
ssl_info = mw.getCertName(ssl_cer_file)
cer_data = mw.readFile(ssl_cer_file)
cer_data = mw.base64StrEncode(cer_data)
# print('cer',cer_data)
ssl_key_file = ssl_path + '/'+domain+'.key'
key_data = mw.readFile(ssl_key_file)
key_data = mw.base64StrEncode(key_data)
# print('ssl_info',ssl_info)
timeBeginAt = int(time.mktime(time.strptime(ssl_info['notBefore'], "%Y-%m-%d")))
timeEndAt = int(time.mktime(time.strptime(ssl_info['notAfter'], "%Y-%m-%d")))
request_data = {
"isOn":True,
"userId":userId,
"name": "ACME泛域名自动上传",
"isCA":False,
"description":domain,
"serverName":domain,
"certData":cer_data,
"keyData":key_data,
"timeBeginAt":timeBeginAt,
"timeEndAt": timeEndAt,
"dnsNames":[domain,"*."+domain],
"commonNames":[ssl_info['issuer']]
}
if did>0:
request_data['sslCertId'] = did
# print(request_data)
response_data = commonReq('/SSLCertService/updateSSLCert', request_data)
print('更新成功',domain,response_data)
return response_data
else:
# print(request_data)
response_data = commonReq('/SSLCertService/createSSLCert', request_data)
print('创建成功',domain,response_data)
return response_data
return response_data
def autoSyncDomain(domain):
data = listSSLCerts(domain)
if len(data) > 0 :
did = data[0]['id']
createSSLCert(domain,did)
else:
createSSLCert(domain)
print(data)
autoSyncDomain(domain)
print(domain,ssl_path)
@@ -1,12 +0,0 @@
# coding:utf-8
import sys
import io
import os
import time
import re
domain = sys.argv[1]
path = sys.argv[2]
print(domain,path)
Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.0 KiB

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

Before

Width:  |  Height:  |  Size: 42 KiB

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

Before

Width:  |  Height:  |  Size: 1.5 KiB

-101
View File
@@ -1,101 +0,0 @@
<style>
.upyunCon {
height: 428px;
}
.up-place {
height: 62px;
border-bottom: 1px solid #ddd;
}
.up-place .btn {
border-radius: 0;
}
.up-place .place-input {
background-color: #f3f3f3;
border: 1px solid #ccc;
height: 30px;
line-height: 28px;
overflow: hidden;
margin: 1px 0 0 -1px;
width: 340px;
}
.place-input ul {
display: inline-block;
position: relative;
width: auto;
}
.place-input ul li {
background: url("/static/img/ico/ico-ltr.png") no-repeat right center;
float: left;
padding-left: 10px;
padding-right: 18px;
}
.place-input ul li a {
height: 28px;
cursor: pointer;
display: inline-block;
}
.upyunlist {
height: 516px;
overflow: auto;
}
.up-bottom {
background-color: #fafafa;
border-top: 1px solid #eee;
bottom: 0;
position: absolute;
width: 100%;
}
.up-use {
line-height: 50px
}
.list-list .cursor span {
line-height: 30px;
}
.btn-title {
margin-top: 1px
}
.tip {
font-size: 10px;
font-style: oblique;
color: green;
}
</style>
<div class="upyunCon">
<div class="up-place pd15">
<button id="backBtn" class="btn btn-default btn-sm glyphicon glyphicon-arrow-left pull-left" title="后退"></button>
<input id="myPath" style="display:none;" type="text" value="">
<div class="place-input pull-left">
<div style="width:1400px;height:28px"><ul></ul></div>
</div>
<button class="refreshBtn btn btn-default btn-sm glyphicon glyphicon-refresh pull-left mr20" title="刷新" style="margin-left:-1px;"></button>
<button class="btn btn-default btn-sm pull-right btn-title" onclick="upyunApi()">帐户设置</button>
<button class="btn btn-default btn-sm pull-right mr20 btn-title" onclick="createDir()">新建文件夹</button>
</div>
<div class="upyunlist pd15">
<div class="divtable" style="margin-bottom:15px">
<table class="table table-hover">
<thead><tr><th>名称</th><th>大小</th><th>更新时间</th><th class="text-right">操作</th></tr></thead>
<tbody class="list-list"></tbody>
</table>
</div>
</div>
</div>
<script type="text/javascript">
$.getScript( "/plugins/file?name=backup_ftp&f=js/backup_ftp.js", function(){
osList('/');
});
</script>
-311
View File
@@ -1,311 +0,0 @@
# coding:utf-8
import sys
import io
import os
import time
import re
import json
# print(sys.platform)
if sys.platform != "darwin":
os.chdir("/www/server/mdserver-web")
web_dir = os.getcwd() + "/web"
if os.path.exists(web_dir):
sys.path.append(web_dir)
os.chdir(web_dir)
import core.mw as mw
import core.db as db
app_debug = False
if mw.isAppleSystem():
app_debug = True
def getPluginName():
return 'backup_ftp'
def getPluginDir():
return mw.getPluginDir() + '/' + getPluginName()
sys.path.append(getPluginDir() + "/class")
from ftp_client import FtpPSClient
def getServerDir():
return mw.getServerDir() + '/' + getPluginName()
def getArgs():
args = sys.argv[2:]
tmp = {}
args_len = len(args)
if args_len == 1:
t = args[0].strip('{').strip('}')
t = t.split(':')
tmp[t[0]] = t[1]
elif args_len > 1:
for i in range(len(args)):
t = args[i].split(':', 1)
tmp[t[0]] = t[1]
return tmp
def checkArgs(data, ck=[]):
for i in range(len(ck)):
if not ck[i] in data:
return (False, mw.returnJson(False, '参数:(' + ck[i] + ')没有!'))
return (True, mw.returnJson(True, 'ok'))
def status():
return 'start'
def getConf():
cfg = getServerDir() + "/cfg.json"
if not os.path.exists(cfg):
rdata = {
'use_sftp':False,
}
return mw.returnJson(False, "未配置", rdata)
data = mw.readFile(cfg)
data = json.loads(data)
return mw.returnJson(True, "OK", data)
def setConf():
args = getArgs()
data = checkArgs(args, ['use_sftp', 'ftp_user',
'ftp_pass', 'ftp_host', 'backup_path'])
if not data[0]:
return data[1]
cfg = getServerDir() + "/cfg.json"
values = ['ftp_user',
'ftp_pass',
'ftp_host']
for v in values:
if args[v] == '':
return mw.returnJson(False, '必填资料不能为空,请核实!', [])
if args['backup_path'] == '':
args['backup_path'] = "/backup"
try:
ftp = FtpPSClient(load_config=False)
ftp.injection_config(args)
data = ftp.getList("/")
if data:
mw.writeFile(cfg, mw.getJson(args))
return mw.returnJson(True, '设置成功', [])
except Exception as e:
return mw.returnJson(False, "FTP校验失败,请核实!\n" + str(e), [])
def getList():
cfg = getServerDir() + "/cfg.json"
if not os.path.exists(cfg):
return mw.returnJson(False, "未配置FTP,请点击`账户设置`", [])
args = getArgs()
data = checkArgs(args, ['path'])
if not data[0]:
return data[1]
try:
ftp = FtpPSClient()
flist = ftp.getList(args['path'])
return mw.returnJson(True, "ok", flist)
except Exception as e:
return mw.returnJson(False, str(e), [])
def createDir():
cfg = getServerDir() + "/cfg.json"
if not os.path.exists(cfg):
return mw.returnJson(False, "未配置FTP,请点击`账户设置`", [])
args = getArgs()
data = checkArgs(args, ['path', 'name'])
if not data[0]:
return data[1]
ftp = FtpPSClient()
isok = ftp.createDir(args['path'], args['name'])
if isok:
return mw.returnJson(True, "创建成功")
return mw.returnJson(False, "创建失败")
def deleteDir():
args = getArgs()
data = checkArgs(args, ['dir_name', 'path'])
if not data[0]:
return data[1]
ftp = FtpPSClient()
isok = ftp.deleteDir(args['path'], args['dir_name'])
if isok:
return mw.returnJson(True, "删除成功")
return mw.returnJson(False, "删除失败")
def deleteFile():
args = getArgs()
data = checkArgs(args, ['path', 'filename'])
if not data[0]:
return data[1]
ftp = FtpPSClient()
isok = ftp.deleteFile(args['path'] + "/" + args['filename'])
if isok:
return mw.returnJson(True, "删除成功")
return mw.returnJson(False, "删除失败")
def findPathName(path, filename):
f = os.scandir(path)
l = []
for ff in f:
t = {}
if ff.name.find(filename) > -1:
t['filename'] = path + '/' + ff.name
l.append(t)
return l
def backupAllFunc(stype):
name = sys.argv[2]
num = sys.argv[3]
prefix_dict = {
"site": "web",
"database": "db",
"path": "path",
}
backups = []
# print("stype:", stype)
# 提前获取-清理多余备份
if stype == 'site':
pid = mw.M('sites').where('name=?', (name,)).getField('id')
backups = mw.M('backup').where('type=? and pid=?', ('0', pid)).field('id,filename').select()
if stype == 'database':
db_path = mw.getServerDir() + '/mysql'
pid = mw.M('databases').dbPos(db_path, 'mysql').where('name=?', (name,)).getField('id')
backups = mw.M('backup').where('type=? and pid=?', ('1', pid)).field('id,filename').select()
if stype == 'path':
backup_dir = mw.getBackupDir()
backup_path = backup_dir + '/path'
_name = 'path_{}'.format(os.path.basename(name))
backups = findPathName(backup_path, _name)
# 其他类型关系性数据库(mysql类的)
if stype.find('database_') > -1:
plugin_name = stype.replace('database_', '')
db_path = mw.getServerDir() + '/' + plugin_name
pid = mw.M('databases').dbPos(db_path, 'mysql').where('name=?', (name,)).getField('id')
backups = mw.M('backup').where('type=? and pid=?', ('1', pid)).field('id,filename').select()
args = stype + " " + name + " " + num
cmd = 'python3 ' + mw.getPanelDir() + '/scripts/backup.py ' + args
if stype.find('database_') > -1:
plugin_name = stype.replace('database_', '')
args = "database " + name + " " + num
cmd = 'python3 ' + mw.getPanelDir() + '/plugins/' + plugin_name + '/scripts/backup.py ' + args
os.system(cmd)
# 开始执行上传信息.
if stype.find('database_') > -1:
bk_name = 'database'
plugin_name = stype.replace('database_', '')
bk_prefix = plugin_name + '/db'
stype = 'database'
else:
bk_prefix = prefix_dict[stype]
bk_name = stype
find_path = mw.getBackupDir() + '/' + bk_name + '/' + bk_prefix + '_' + name
if stype == 'path':
_name = 'path_{}'.format(os.path.basename(name))
find_path = mw.getBackupDir() + '/path/'+_name
find_new_file = "ls " + find_path + "_* | grep '.gz' | cut -d \\ -f 1 | awk 'END {print}'"
filename = mw.execShell(find_new_file)[0].strip()
if filename == "":
mw.echoInfo("not find upload file!")
return ''
mw.echoInfo("准备上传文件 {}".format(filename))
ftp = FtpPSClient()
ftp.uploadFile(filename, stype)
# print(backups)
backups = sorted(backups, key=lambda x: x['filename'], reverse=False)
mw.echoStart('开始删除远程备份')
num = int(num)
sep = len(backups) - num
if sep > -1:
for backup in backups:
fn = os.path.basename(backup['filename'])
object_name = ftp.buildDirName(stype, fn)
ftp.deleteFile(object_name)
mw.echoInfo("已清理远程过期备份文件:" + object_name)
sep -= 1
if sep < 0:
break
mw.echoEnd('结束删除远程备份')
return ''
def in_array(name, arr=[]):
for x in arr:
if name == x:
return True
return False
def installPreInspection():
return 'ok'
if __name__ == "__main__":
func = sys.argv[1]
if func == 'status':
print(status())
elif func == 'start':
print(start())
elif func == 'stop':
print(stop())
elif func == 'restart':
print(restart())
elif func == 'reload':
print(reload())
elif func == 'install_pre_inspection':
print(installPreInspection())
elif func == 'conf':
print(getConf())
elif func == 'set_config':
print(setConf())
elif func == "get_list":
print(getList())
elif func == "create_dir":
print(createDir())
elif func == "delete_dir":
print(deleteDir())
elif func == 'delete_file':
print(deleteFile())
elif in_array(func, ['site', 'database', 'path']) or func.find('database_') > -1:
print(backupAllFunc(func))
else:
print('error')
-18
View File
@@ -1,18 +0,0 @@
{
"sort":6,
"title":"FTP存储空间",
"hook":["backup"],
"tip":"soft",
"name":"backup_ftp",
"type":"运行环境",
"ps":"将网站或数据库打包备份到FTP存储空间",
"versions":["1.0"],
"install_pre_inspection":false,
"shell":"install.sh",
"checks":"server/backup_ftp",
"path": "server/backup_ftp",
"author":"midoks",
"home":"https://github.com/midoks/mdserver-web",
"date":"2022-10-23",
"pid": "5"
}
-31
View File
@@ -1,31 +0,0 @@
#!/bin/bash
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH
curPath=`pwd`
rootPath=$(dirname "$curPath")
rootPath=$(dirname "$rootPath")
serverPath=$(dirname "$rootPath")
sys_os=`uname`
Install_App()
{
mkdir -p ${serverPath}/backup_ftp
echo "${1}" > ${serverPath}/backup_ftp/version.pl
echo '安装完成'
}
Uninstall_App()
{
rm -rf ${serverPath}/backup_ftp
}
action=$1
if [ "${1}" == 'install' ];then
Install_App $2
else
Uninstall_App $2
fi
-258
View File
@@ -1,258 +0,0 @@
function bkfPost(method,args,callback){
var _args = null;
if (typeof(args) == 'string'){
_args = JSON.stringify(toArrayObject(args));
} else {
_args = JSON.stringify(args);
}
var loadT = layer.msg('正在获取...', { icon: 16, time: 0, shade: 0.3 });
$.post('/plugins/run', {name:'backup_ftp', func:method, args:_args}, function(data) {
layer.close(loadT);
if (!data.status){
layer.msg(data.msg,{icon:0,time:2000,shade: [0.3, '#000']});
return;
}
if(typeof(callback) == 'function'){
callback(data);
}
},'json');
}
function getFtpLocalTime(data){
var str = data.slice(0,4)+"/"+data.slice(4,6)+"/"+data.slice(6,8)
+ " " + data.slice(8,10)+":"+data.slice(10,12)+":"+data.slice(12,14);
return str;
}
// 自定义部分
var i = null;
//设置API
function upyunApi(){
bkfPost('conf', {}, function(rdata){
var rdata = $.parseJSON(rdata.data);
var token = rdata.data;
var check_status = token.use_sftp;
var sftp_checked = check_status === "true" ? " checked=\"checked\"" : "";
if (typeof(token.ftp_host) == 'undefined'){
token.ftp_host = '';
}
if (typeof(token.ftp_user) == 'undefined'){
token.ftp_user = '';
}
if (typeof(token.ftp_pass) == 'undefined'){
token.ftp_pass = '';
}
if (typeof(token.backup_path) == 'undefined'){
token.backup_path = '';
}
var apicon = '<div class="bingfa mtb15" style="padding-bottom:0px;">\
<p>\
<span class="span_tit">使用SFTP:</span>\ <input style="width: 20px; vertical-align:middle;" type="checkbox" name="use_sftp"'+sftp_checked+'> 是否使用SFTP进行数据传输 \
</p>\
<p>\
<span class="span_tit">Host:</span>\
<input placeholder="请输入主机地址" style="width: 200px;" type="text" name="upyun_service" value="'+token.ftp_host+'"> *服务器地址,FTP默认端口21, SFTP默认端口22\
</p>\
<p>\
<span class="span_tit">用户名:</span>\
<input style="width: 200px;" type="text" name="ftp_username" value="'+token.ftp_user+'"> *指定用户名\
</p>\
<p>\
<span class="span_tit">密码:</span>\
<input style="width: 200px;" type="password" name="ftp_password" value="'+token.ftp_pass+'"> *登录密码\
</p>\
<p>\
<span class="span_tit">存储位置:</span>\
<input placeholder="请输入存储位置" style="width: 200px;" type="text" name="backup_path" value="'+token.backup_path+'"> *相对于根目录的路径,默认是/backup\
</p>\
</div>';
layer.open({
type: 1,
area: "600px",
title: "FTP/SFTP帐户设置",
closeBtn: 1,
shift: 5,
shadeClose: false,
btn: ['确定','取消'],
content:apicon,
yes:function(index,layero){
var data = {
use_sftp:$("input[name='use_sftp']").prop('checked'),
ftp_user:$("input[name='ftp_username']").val(),
ftp_pass:$("input[name='ftp_password']").val(),
ftp_host:$("input[name='upyun_service']").val(),
backup_path:$("input[name='backup_path']").val()
}
bkfPost('set_config', data, function(rdata){
var rdata = $.parseJSON(rdata.data);
if (rdata.status){
showMsg(rdata.msg,function(){
layer.close(index);
osList("/");
},{icon:1},2000);
} else{
layer.msg(rdata.msg,{icon:2});
}
})
},
});
});
}
function createDir(){
layer.open({
type: 1,
area: "400px",
title: "创建目录",
closeBtn: 1,
shift: 5,
shadeClose: false,
btn: ['确定','取消'],
content:'<div class="bingfa bt-form c6" style="padding-bottom: 10px;">\
<p>\
<span class="span_tit">目录名称:</span>\
<input style="width: 200px;" type="text" name="newPath" value="">\
</p>\
</div>',
success:function(){
$("input[name='newPath']").focus().keyup(function(e){
if(e.keyCode == 13) $(".layui-layer-btn0").click();
});
},
yes:function(index,layero){
var name = $("input[name='newPath']").val();
if(name == ''){
layer.msg('目录名称不能为空!',{icon:2});
return;
}
var path = $("#myPath").val();
var dirname = name;
// var loadT = layer.msg('正在创建目录['+dirname+']...',{icon:16,time:0,shade: [0.3, '#000']});
bkfPost('create_dir', {path:path,name:dirname}, function(data){
var rdata = $.parseJSON(data.data);
if(rdata.status) {
showMsg(rdata.msg, function(){
layer.close(index);
osList(path);
} ,{icon:1}, 2000);
} else{
layer.msg(rdata.msg,{icon:2});
}
});
}
});
}
//删除文件
function deleteFile(name, is_dir){
if (is_dir === false){
safeMessage('删除文件','删除后将无法恢复,真的要删除['+name+']吗?',function(){
var path = $("#myPath").val();
var filename = name;
bkfPost('delete_file', {filename:filename,path:path}, function(rdata){
var rdata = $.parseJSON(rdata.data);
showMsg(rdata.msg,function(){
osList(path);
},{icon:rdata.status?1:2},2000);
});
});
} else {
safeMessage('删除文件夹','删除后将无法恢复,真的要删除['+name+']吗?',function(){
var path = $("#myPath").val();
bkfPost('delete_dir', {dir_name:name,path:path}, function(rdata){
var rdata = $.parseJSON(rdata.data);
showMsg(rdata.msg,function(){
osList(path);
},{icon:rdata.status?1:2},2000);
});
});
}
}
function osList(path){
bkfPost('get_list', {path:path}, function(rdata){
var rdata = $.parseJSON(rdata.data);
if(rdata.status === false){
showMsg(rdata.msg,function(){
upyunApi();
},{icon:2},2000);
return;
}
var mlist = rdata.data;
// console.log(mlist);
var listBody = ''
var listFiles = ''
for(var i=0;i<mlist.list.length;i++){
if(mlist.list[i].type == null){
listBody += '<tr><td class="cursor" onclick="osList(\''+(path+'/'+mlist.list[i].name).replace('//','/')+'\')"><span class="ico ico-folder"></span>\<span>'+mlist.list[i].name+'</span></td>\
<td>-</td>\
<td>-</td>\
<td class="text-right"><a class="btlink" onclick="deleteFile(\''+mlist.list[i].name+'\', true)">删除</a></td></tr>'
}else{
listFiles += '<tr><td class="cursor"><span class="ico ico-file"></span>\<span>'+mlist.list[i].name+'</span></td>\
<td>'+toSize(mlist.list[i].size)+'</td>\
<td>'+getFtpLocalTime(mlist.list[i].time)+'</td>\
<td class="text-right"><a target="_blank" href="'+mlist.list[i].download+'" class="btlink">下载</a> | <a class="btlink" onclick="deleteFile(\''+mlist.list[i].name+'\', false)">删除</a></td></tr>'
}
}
listBody += listFiles;
var pathLi='';
var tmp = path.split('/')
var pathname = '';
var n = 0;
for(var i=0;i<tmp.length;i++){
if(n > 0 && tmp[i] == '') continue;
var dirname = tmp[i];
if(dirname == '') {
dirname = '根目录';
n++;
}
pathname += '/' + tmp[i];
pathname = pathname.replace('//','/');
pathLi += '<li><a title="'+pathname+'" onclick="osList(\''+pathname+'\')">'+dirname+'</a></li>';
}
var um = 1;
if(tmp[tmp.length-1] == '') um = 2;
var backPath = tmp.slice(0,tmp.length-um).join('/') || '/';
$('#myPath').val(path);
$(".upyunCon .place-input ul").html(pathLi);
$(".upyunlist .list-list").html(listBody);
upPathLeft();
$('#backBtn').unbind().click(function() {
osList(backPath);
});
$('.upyunCon .refreshBtn').unbind().click(function(){
osList(path);
});
});
}
//计算当前目录偏移
function upPathLeft(){
var UlWidth = $(".place-input ul").width();
var SpanPathWidth = $(".place-input").width() - 20;
var Ml = UlWidth - SpanPathWidth;
if(UlWidth > SpanPathWidth ){
$(".place-input ul").css("left",-Ml)
}
else{
$(".place-input ul").css("left",0)
}
}
// $('.layui-layer-page').css('height','670px');
Binary file not shown.

Before

Width:  |  Height:  |  Size: 254 B

-74
View File
@@ -1,74 +0,0 @@
<div class="bt-form">
<div class="bt-w-main">
<div class="bt-w-menu">
<p class="bgw" onclick="pluginService('clean');">服务</p>
<p onclick="pluginConfig('clean');">配置修改</p>
<p onclick="commonFunc();">常用功能</p>
<p onclick="pluginLogs('clean',null,'run_log');">运行日志</p>
<p onclick="pRead()">说明</p>
</div>
<div class="bt-w-con pd15">
<div class="soft-man-con"></div>
</div>
</div>
</div>
<script type="text/javascript">
pluginService('clean');
function cleanPost(method, version, args, callback){
var loadT = layer.msg('正在获取...', { icon: 16, time: 0, shade: 0.3 });
var req_data = {};
req_data['name'] = 'clean';
req_data['func'] = method;
req_data['version'] = version;
if (typeof(args) == 'string'){
req_data['args'] = JSON.stringify(toArrayObject(args));
} else {
req_data['args'] = JSON.stringify(args);
}
$.post('/plugins/run', req_data, function(data) {
layer.close(loadT);
if (!data.status){
layer.msg(data.msg,{icon:0,time:2000,shade: [10, '#000']});
return;
}
if(typeof(callback) == 'function'){
callback(data);
}
},'json');
}
function cleanRun(){
cleanPost('clean_run', '', {}, function(rdata){
// var rdata = $.parseJSON(rdata.data);
// layer.msg(rdata.msg, { icon: rdata.status ? 1 : 2 });
});
layer.msg("执行成功!", { icon: 1});
}
function commonFunc(){
con = '<hr/><p class="conf_p" style="text-align:center;">\
<button class="btn btn-default btn-sm" onclick="cleanRun()">手动执行</button> \
</p>';
$(".soft-man-con").html(con);
}
function pRead(){
var readme = '<ul class="help-info-text c7">';
readme += '<li>启动后,加入到计划任务每隔7天[0:15]执行。</li>';
readme += '<li>【*】代表要删除。</li>';
readme += '<li>添加/删除清除日志,可在`配置修改`中添加/删除。</li>';
readme += '<li>如果是目录,会寻找【.log】后缀的文件清空。</li>';
readme += '</ul>';
$('.soft-man-con').html(readme);
}
</script>
-298
View File
@@ -1,298 +0,0 @@
# coding:utf-8
import sys
import io
import os
import time
web_dir = os.getcwd() + "/web"
if os.path.exists(web_dir):
sys.path.append(web_dir)
os.chdir(web_dir)
import core.mw as mw
app_debug = False
if mw.isAppleSystem():
app_debug = True
def getPluginName():
return 'clean'
def getPluginDir():
return mw.getPluginDir() + '/' + getPluginName()
def getServerDir():
return mw.getServerDir() + '/' + getPluginName()
def getInitDFile():
if app_debug:
return '/tmp/' + getPluginName()
return '/etc/init.d/' + getPluginName()
def getConf():
path = getServerDir() + "/clean.conf"
return path
def getArgs():
args = sys.argv[2:]
tmp = {}
args_len = len(args)
if args_len == 1:
t = args[0].strip('{').strip('}')
t = t.split(':')
tmp[t[0]] = t[1]
elif args_len > 1:
for i in range(len(args)):
t = args[i].split(':')
tmp[t[0]] = t[1]
return tmp
def getLockFile():
return getServerDir() + "/installed_lock.pl"
def runLog():
return getServerDir() + "/clean.log"
def status():
initConf()
if os.path.exists(getLockFile()):
return "start"
return 'stop'
def initConf():
conf = getConf()
if not os.path.exists(conf):
content = ""
clog = [
"/var/log/cron-*",
"/var/log/maillog-*",
"/var/log/secure-*",
"/var/log/spooler-*",
"/var/log/yum.log-*",
"/var/log/messages-*",
"/var/log/btmp-*",
"/var/log/auth.*",
"/var/log/messages.*",
"/var/log/debug.*",
"/var/log/syslog.*",
"/var/log/syslog",
"/var/log/mail.*",
"/var/log/btmp.*",
"/var/log/sa/sa*",
"/var/log/sysstat/sa*",
"/var/log/atop/atop*",
"/var/log/anaconda/*.log",
"/var/log/dpkg.log.*",
"/var/log/alternatives.log.*",
"/var/log/user.log.*",
"/var/log/kern.log.*",
"/var/log/daemon.log.*",
"/var/log/*.gz",
"/var/log/*.xz",
"/var/log/*.log.*",
"/var/log/audit/audit.log.*",
"/var/log/hawkey.log-*",
"/var/log/apt/*.gz",
"/var/log/apt/*.xz",
"/var/log/rhsm/rhsm.log-*",
"/var/log/rhsm/rhsmcertd.log-*",
"/var/log/exim4/*.gz",
"/var/log/journal/*",
"/var/spool/clientmqueue/*",
"/tmp/yum_save_*",
"/tmp/tmp.*",
"/www/server/dztasks/logs/dztasks.*.log",
"/www/server/dztasks/logs/dztasks_*",
]
for i in clog:
content += i + "\n"
# 常用日志
clogcom = [
"/var/log/messages",
"/var/log/btmp",
"/var/log/wtmp",
"/var/log/secure",
"/var/log/lastlog",
"/var/log/syslog",
"/var/log/cron",
"/www/wwwlogs",
"/www/server/rsyncd",
"/www/server/acme_pandominassl_apply",
"/www/server/sphinx/index",
"/www/server/mongodb/logs",
"/www/server/php/53/var/log",
"/www/server/php/54/var/log",
"/www/server/php/55/var/log",
"/www/server/php/56/var/log",
"/www/server/php/70/var/log",
"/www/server/php/71/var/log",
"/www/server/php/72/var/log",
"/www/server/php/73/var/log",
"/www/server/php/74/var/log",
"/www/server/php/80/var/log",
"/www/server/php/81/var/log",
"/www/server/php/82/var/log",
"/www/server/php/83/var/log",
"/www/server/php/84/var/log",
"/www/server/openresty/nginx/logs",
"/www/server/phpmyadmin",
"/www/server/redis/data",
"/www/server/alist/data/log",
"/www/server/dztasks/logs",
"/www/server/rsyncd/lsyncd.log"
"/www/server/cron",
]
for i in clogcom:
if os.path.exists(i):
content += i + "\n"
# 清理日志
rootDir = "/var/log"
l = os.listdir(rootDir)
for x in range(len(l)):
abspath = rootDir + "/" + l[x]
content += abspath + "\n"
mw.writeFile(conf, content)
def start():
initConf()
lock_file = getLockFile()
if not os.path.exists(lock_file):
mw.writeFile(lock_file, "")
import tool_task
tool_task.createBgTask()
return 'ok'
return 'fail'
def stop():
initConf()
lock_file = getLockFile()
if os.path.exists(lock_file):
os.remove(lock_file)
import tool_task
tool_task.removeBgTask()
return 'ok'
return 'fail'
def reload():
return 'ok'
def get_filePath_fileName_fileExt(filename):
(filepath, tempfilename) = os.path.split(filename)
(shotname, extension) = os.path.splitext(tempfilename)
return filepath, shotname, extension
def cleanFileLog(path):
filepath, shotname, extension = get_filePath_fileName_fileExt(path)
if extension == ".log":
cmd = "echo \"\" > " + path
tmp = mw.execShell(cmd)
if tmp[1] != "":
cmd += " | error:" + tmp[1].strip()
print(cmd)
def cleanSelfFileLog(path):
filepath, shotname, extension = get_filePath_fileName_fileExt(path)
if extension == ".log":
cmd = "echo \"\" > " + path
tmp = mw.execShell(cmd)
if tmp[1] != "":
cmd += " | error:" + tmp[1].strip()
print(cmd)
def cleanDirLog(path):
l = os.listdir(path)
for x in range(len(l)):
abspath = path + "/" + l[x]
if os.path.isfile(abspath):
cleanFileLog(abspath)
if os.path.isdir(abspath):
cleanDirLog(abspath)
def cleanRun():
plugin_dir = getPluginDir()
log_file = getServerDir()+'/clean.log'
cmd = 'cd '+mw.getPanelDir()+' && python3 '+plugin_dir+'/index.py clean > '+log_file
os.system(cmd)
return mw.returnJson(True, '执行成功!')
def cleanLog():
conf = getConf()
clist = mw.readFile(conf).strip()
clist = clist.split("\n")
for x in clist:
abspath = x.strip()
if abspath.find('*') > 1:
cmd = 'rm -rf ' + abspath
print(cmd)
data = mw.execShell(cmd)
# print(data)
continue
if os.path.exists(abspath):
if os.path.isfile(abspath):
cleanSelfFileLog(abspath)
continue
if os.path.isdir(abspath):
cleanDirLog(abspath)
continue
if __name__ == "__main__":
func = sys.argv[1]
if func == 'status':
print(status())
elif func == 'start':
print(start())
elif func == 'stop':
print(stop())
elif func == 'restart':
print(restart())
elif func == 'reload':
print(reload())
elif func == 'conf':
print(getConf())
elif func == 'run_log':
print(runLog())
elif func == 'clean':
cleanLog()
elif func == 'clean_run':
print(cleanRun())
else:
print('error')
-17
View File
@@ -1,17 +0,0 @@
{
"sort":2,
"title":"日志清理",
"tip":"soft",
"name":"clean",
"type":"运行环境",
"ps":"日志清理",
"versions":"1.0",
"updates":"1.0",
"shell":"install.sh",
"checks":"server/clean",
"path": "server/clean",
"author":"clean",
"home":"https://github.com/midoks",
"date":"2021-11-23",
"pid": "5"
}
-42
View File
@@ -1,42 +0,0 @@
#!/bin/bash
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH
# cd /Users/midoks/Desktop/mwdev/server/mdserver-web/plugins/mongodb && /bin/bash install.sh install 5.0.4
# cd /www/server/mdserver-web/plugins/mongodb && /bin/bash install.sh install 5.0.4
curPath=`pwd`
rootPath=$(dirname "$curPath")
rootPath=$(dirname "$rootPath")
serverPath=$(dirname "$rootPath")
VERSION=$2
if [ -f ${rootPath}/bin/activate ];then
source ${rootPath}/bin/activate
fi
Install_app()
{
echo '正在安装脚本文件...'
mkdir -p $serverPath/clean
cd ${rootPath} && python3 ${rootPath}/plugins/clean/index.py start
echo "${VERSION}" > $serverPath/clean/version.pl
echo '安装完成'
}
Uninstall_app()
{
cd ${rootPath} && python3 ${rootPath}/plugins/clean/index.py stop
rm -rf $serverPath/clean
echo "Uninstall_clean"
}
action=$1
if [ "${1}" == 'install' ];then
Install_app
else
Uninstall_app
fi
-129
View File
@@ -1,129 +0,0 @@
# coding:utf-8
import sys
import io
import os
import time
import json
web_dir = os.getcwd() + "/web"
if os.path.exists(web_dir):
sys.path.append(web_dir)
os.chdir(web_dir)
import core.mw as mw
from utils.crontab import crontab as MwCrontab
app_debug = False
if mw.isAppleSystem():
app_debug = True
def getPluginName():
return 'clean'
def getPluginDir():
return mw.getPluginDir() + '/' + getPluginName()
def getServerDir():
return mw.getServerDir() + '/' + getPluginName()
def getTaskConf():
conf = getServerDir() + "/task_config.json"
return conf
def getConfigData():
conf = getTaskConf()
if os.path.exists(conf):
return json.loads(mw.readFile(getTaskConf()))
return {
"task_id": -1,
"period": "day-n",
"where1": "7",
"hour": "0",
"minute": "15",
}
def createBgTask():
removeBgTask()
createBgTaskByName(getPluginName())
def createBgTaskByName(name):
args = getConfigData()
_name = "[勿删]日志清理[" + name + "]"
res = mw.M("crontab").field("id, name").where("name=?", (_name,)).find()
if res:
return True
if "task_id" in args and args["task_id"] > 0:
res = mw.M("crontab").field("id, name").where(
"id=?", (args["task_id"],)).find()
if res and res["id"] == args["task_id"]:
print("计划任务已经存在!")
return True
mw_dir = mw.getPanelDir()
cmd = '''
mw_dir=%s
rname=%s
plugin_path=%s
script_path=%s
logs_file=$plugin_path/${rname}.log
''' % (mw_dir, name, getServerDir(), getPluginDir())
cmd += 'echo "★【`date +"%Y-%m-%d %H:%M:%S"`】 STSRT★" >> $logs_file' + "\n"
cmd += 'echo ">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>" >> $logs_file' + "\n"
cmd += 'echo "python3 $script_path/index.py clean >> $logs_file 2>&1"' + "\n"
cmd += 'cd $mw_dir && python3 $script_path/index.py clean >> $logs_file 2>&1' + "\n"
cmd += 'echo "【`date +"%Y-%m-%d %H:%M:%S"`】 END★" >> $logs_file' + "\n"
cmd += 'echo "<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<" >> $logs_file' + "\n"
params = {
'name': _name,
'type': args['period'],
'week': "",
'where1': args['where1'],
'hour': args['hour'],
'minute': args['minute'],
'save': "",
'backup_to': "",
'stype': "toShell",
'sname': '',
'sbody': cmd,
'url_address': '',
}
task_id = MwCrontab.instance().add(params)
if task_id > 0:
args["task_id"] = task_id
args["name"] = name
mw.writeFile(getTaskConf(), json.dumps(args))
def removeBgTask():
cfg = getConfigData()
if "task_id" in cfg and cfg["task_id"] > 0:
res = mw.M("crontab").field("id, name").where(
"id=?", (cfg["task_id"],)).find()
if res and res["id"] == cfg["task_id"]:
data = MwCrontab.instance().delete(cfg["task_id"])
if data[0]:
cfg["task_id"] = -1
mw.writeFile(getTaskConf(), json.dumps(cfg))
return True
return False
if __name__ == "__main__":
if len(sys.argv) > 1:
action = sys.argv[1]
if action == "remove":
removeBgTask()
elif action == "add":
createBgTask()
Binary file not shown.

Before

Width:  |  Height:  |  Size: 31 KiB

-31
View File
@@ -1,31 +0,0 @@
<style>
.overflow_hide {
overflow: hidden;
text-overflow: ellipsis;
white-space: nowrap;
vertical-align: middle;
}
</style>
<div class="bt-form">
<div class='plugin_version'></div>
<div class="bt-w-main">
<div class="bt-w-menu">
<p class="bgw" onclick="pluginService('cloudreve');">服务</p>
<p onclick="pluginInitD('cloudreve');">自启动</p>
<p onclick="alistCommonFunc();">常用功能</p>
<p onclick="pluginConfigTpl('cloudreve',$('.plugin_version').attr('version'));">配置修改</p>
<p onclick="pluginLogs('cloudreve','','run_log');">运行日志</p>
<p onclick="alistReadme();">相关说明</p>
</div>
<div class="bt-w-con pd15">
<div class="soft-man-con" style="height: 520px; overflow: auto;"></div>
</div>
</div>
</div>
<script type="text/javascript">
$.getScript( "/plugins/file?name=cloudreve&f=js/cloudreve.js", function(){
pluginService('cloudreve', $('.plugin_version').attr('version'));
});
</script>
-292
View File
@@ -1,292 +0,0 @@
# coding:utf-8
import sys
import io
import os
import time
import re
web_dir = os.getcwd() + "/web"
if os.path.exists(web_dir):
sys.path.append(web_dir)
os.chdir(web_dir)
import core.mw as mw
app_debug = False
if mw.isAppleSystem():
app_debug = True
def getPluginName():
return 'cloudreve'
def getPluginDir():
return mw.getPluginDir() + '/' + getPluginName()
def getServerDir():
return mw.getServerDir() + '/' + getPluginName()
def getInitDFile():
current_os = mw.getOs()
if current_os == 'darwin':
return '/tmp/' + getPluginName()
if current_os.startswith('freebsd'):
return '/etc/rc.d/' + getPluginName()
return '/etc/init.d/' + getPluginName()
def getInitDTpl():
path = getPluginDir() + "/init.d/" + getPluginName() + ".tpl"
return path
def getArgs():
args = sys.argv[3:]
tmp = {}
args_len = len(args)
if args_len == 1:
t = args[0].strip('{').strip('}')
if t.strip() == '':
tmp = []
else:
t = t.split(':')
tmp[t[0]] = t[1]
tmp[t[0]] = t[1]
elif args_len > 1:
for i in range(len(args)):
t = args[i].split(':')
tmp[t[0]] = t[1]
return tmp
def checkArgs(data, ck=[]):
for i in range(len(ck)):
if not ck[i] in data:
return (False, mw.returnJson(False, '参数:(' + ck[i] + ')没有!'))
return (True, mw.returnJson(True, 'ok'))
def getConf():
path = getServerDir() + "/conf.ini"
return path
def configTpl():
path = getPluginDir() + '/tpl'
pathFile = os.listdir(path)
tmp = []
for one in pathFile:
file = path + '/' + one
tmp.append(file)
return mw.getJson(tmp)
def readConfigTpl():
args = getArgs()
data = checkArgs(args, ['file'])
if not data[0]:
return data[1]
content = mw.readFile(args['file'])
content = contentReplace(content)
return mw.returnJson(True, 'ok', content)
def status():
data = mw.execShell(
"ps aux|grep cloudreve |grep -v grep | grep -v python | grep -v mdserver-web | awk '{print $2}'")
if data[0] == '':
return 'stop'
return 'start'
def contentReplace(content):
service_path = mw.getServerDir()
content = content.replace('{$ROOT_PATH}', mw.getFatherDir())
content = content.replace('{$SERVER_PATH}', service_path)
content = content.replace('{$SERVER_APP}', service_path + '/'+getPluginName())
return content
def initDreplace():
file_tpl = getInitDTpl()
service_path = mw.getServerDir()
initD_path = getServerDir() + '/init.d'
if not os.path.exists(initD_path):
os.mkdir(initD_path)
file_bin = initD_path + '/' + getPluginName()
# initd replace
if not os.path.exists(file_bin):
content = mw.readFile(file_tpl)
content = content.replace('{$SERVER_PATH}', service_path)
mw.writeFile(file_bin, content)
mw.execShell('chmod +x ' + file_bin)
# systemd
systemDir = mw.systemdCfgDir()
systemService = systemDir + '/' + getPluginName() + '.service'
if os.path.exists(systemDir) and not os.path.exists(systemService):
systemServiceTpl = getPluginDir() + '/init.d/' + getPluginName() + '.service.tpl'
service_path = mw.getServerDir()
se_content = mw.readFile(systemServiceTpl)
se_content = se_content.replace('{$SERVER_PATH}', service_path)
mw.writeFile(systemService, se_content)
mw.execShell('systemctl daemon-reload')
return file_bin
def alistOp(method):
file = initDreplace()
current_os = mw.getOs()
if current_os == "darwin":
data = mw.execShell(file + ' ' + method)
if data[1] == '':
return 'ok'
return data[1]
if current_os.startswith("freebsd"):
data = mw.execShell('service ' + getPluginName() + ' ' + method)
if data[1] == '':
return 'ok'
return data[1]
data = mw.execShell('systemctl ' + method + ' ' + getPluginName())
if data[1] == '':
return 'ok'
return data[1]
def start():
return alistOp('start')
def stop():
return alistOp('stop')
def restart():
status = alistOp('restart')
return status
def reload():
return alistOp('reload')
def initdStatus():
current_os = mw.getOs()
if current_os == 'darwin':
return "Apple Computer does not support"
if current_os.startswith('freebsd'):
initd_bin = getInitDFile()
if os.path.exists(initd_bin):
return 'ok'
shell_cmd = 'systemctl status ' + \
getPluginName() + ' | grep loaded | grep "enabled;"'
data = mw.execShell(shell_cmd)
if data[0] == '':
return 'fail'
return 'ok'
def initdInstall():
current_os = mw.getOs()
if current_os == 'darwin':
return "Apple Computer does not support"
# freebsd initd install
if current_os.startswith('freebsd'):
import shutil
source_bin = initDreplace()
initd_bin = getInitDFile()
shutil.copyfile(source_bin, initd_bin)
mw.execShell('chmod +x ' + initd_bin)
mw.execShell('sysrc ' + getPluginName() + '_enable="YES"')
return 'ok'
mw.execShell('systemctl enable ' + getPluginName())
return 'ok'
def initdUinstall():
current_os = mw.getOs()
if current_os == 'darwin':
return "Apple Computer does not support"
if current_os.startswith('freebsd'):
initd_bin = getInitDFile()
os.remove(initd_bin)
mw.execShell('sysrc ' + getPluginName() + '_enable="NO"')
return 'ok'
mw.execShell('systemctl disable ' + getPluginName())
return 'ok'
def runLog():
return getServerDir() + '/logs.pl'
def pSqliteDb(dbname='databases'):
pos_file = getServerDir() + '/data/'
file = pos_file + '/data.db'
name = 'data'
conn = mw.M(dbname).dbPos(pos_file, name)
return conn
def getCloudrevePort():
file = getConf()
content = mw.readFile(file)
rep = r'Listen\s*=\s*(.*)'
tmp = re.search(rep, content)
return tmp.groups()[0].strip()
def homePage():
http_port = getCloudrevePort()
ip = mw.getLocalIp()
if mw.isAppleSystem():
ip = '127.0.0.1'
url = 'http://'+ip+""+str(http_port)
# print(url)
return mw.returnJson(True, 'ok!', url)
if __name__ == "__main__":
func = sys.argv[1]
if func == 'status':
print(status())
elif func == 'start':
print(start())
elif func == 'stop':
print(stop())
elif func == 'restart':
print(restart())
elif func == 'reload':
print(reload())
elif func == 'initd_status':
print(initdStatus())
elif func == 'initd_install':
print(initdInstall())
elif func == 'initd_uninstall':
print(initdUinstall())
elif func == 'conf':
print(getConf())
elif func == 'run_log':
print(runLog())
elif func == 'config_tpl':
print(configTpl())
elif func == 'read_config_tpl':
print(readConfigTpl())
elif func == 'home_page':
print(homePage())
else:
print('error')
-17
View File
@@ -1,17 +0,0 @@
{
"sort": 7,
"ps": "一个支持多种存储的文件列表程序",
"name": "cloudreve",
"title": "cloudreve",
"shell": "install.sh",
"versions":["3.8.3"],
"tip": "soft",
"checks": "server/cloudreve",
"path": "server/cloudreve",
"display": 1,
"author": "cloudreve",
"date": "2024-10-14",
"home": "https://cloudreve.org/",
"type": 0,
"pid": "5"
}
@@ -1,13 +0,0 @@
[Unit]
Description=A file list program that supports multiple storage
After=network.target
[Service]
Type=simple
WorkingDirectory={$SERVER_PATH}/cloudreve
ExecStart={$SERVER_PATH}/cloudreve/cloudreve
ExecReload=/bin/kill -USR2 $MAINPID
Restart=on-failure
[Install]
WantedBy=multi-user.target
-46
View File
@@ -1,46 +0,0 @@
#!/bin/sh
# chkconfig: 2345 55 25
# description: cloudreve Service
### BEGIN INIT INFO
# Provides: cloudreve
# Required-Start: $all
# Required-Stop: $all
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
# Short-Description: starts cloudreve
# Description: starts the MDW-Web
### END INIT INFO
# Simple cloudreve init.d script conceived to work on Linux systems
# as it does use of the /proc filesystem.
app_start(){
echo "Starting cloudreve server..."
cd {$SERVER_PATH}/cloudreve
./cloudreve >> {$SERVER_PATH}/cloudreve/logs.pl 2>&1 &
}
app_stop(){
echo "dztasks stopped"
ps -ef| grep cloudreve | grep -v grep | grep -v python | grep -v sh | awk '{print $2}'| xargs kill
}
case "$1" in
start)
app_start
;;
stop)
app_stop
;;
restart|reload)
app_stop
sleep 0.3
app_start
;;
*)
echo "Please use start or stop as first argument"
;;
esac
-90
View File
@@ -1,90 +0,0 @@
#!/bin/bash
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin:/opt/homebrew/bin
export PATH
curPath=`pwd`
rootPath=$(dirname "$curPath")
rootPath=$(dirname "$rootPath")
serverPath=$(dirname "$rootPath")
# cd /Users/midoks/Desktop/mwdev/server/mdserver-web/plugins/cloudreve && bash install.sh install 3.8.3
# cd /www/server/mdserver-web/plugins/cloudreve && bash install.sh install 3.8.3
VERSION=$2
sysArch=`arch`
sysName=`uname`
ALIST_ARCH_NAME=amd64
if [ "$sysArch" == "arm64" ];then
ALIST_ARCH_NAME=arm64
elif [ "$sysArch" == "x86_64" ]; then
ALIST_ARCH_NAME=amd64
elif [ "$sysArch" == "aarch64" ]; then
ALIST_ARCH_NAME=aarch64
fi
ALIST_NAME=linux
if [ "$sysName" == "Darwin" ];then
ALIST_NAME=darwin
fi
Install_App()
{
echo '正在安装脚本文件...'
mkdir -p $serverPath/source
mkdir -p $serverPath/source/cloudreve
FILE_TGZ=cloudreve_${VERSION}_${ALIST_NAME}_${ALIST_ARCH_NAME}.tar.gz
CLOUDREVE_DIR=$serverPath/source/cloudreve
if [ ! -f $CLOUDREVE_DIR/${FILE_TGZ} ];then
wget -O $CLOUDREVE_DIR/${FILE_TGZ} https://github.com/cloudreve/Cloudreve/releases/download/${VERSION}/${FILE_TGZ}
fi
mkdir -p $serverPath/cloudreve
cd $CLOUDREVE_DIR && tar -zxvf ${FILE_TGZ} -C $serverPath/cloudreve
echo "${VERSION}" > $serverPath/cloudreve/version.pl
cd ${rootPath} && python3 ${rootPath}/plugins/cloudreve/index.py start
cd ${rootPath} && python3 ${rootPath}/plugins/cloudreve/index.py initd_install
# cd $serverPath/cloudreve && ./alist admin set admin
echo '安装cloudreve完成'
}
Uninstall_App()
{
if [ -f /usr/lib/systemd/system/cloudreve.service ];then
systemctl stop cloudreve
systemctl disable cloudreve
rm -rf /usr/lib/systemd/system/cloudreve.service
systemctl daemon-reload
fi
if [ -f /lib/systemd/system/cloudreve.service ];then
systemctl stop cloudreve
systemctl disable cloudreve
rm -rf /lib/systemd/system/cloudreve.service
systemctl daemon-reload
fi
if [ -f $serverPath/cloudreve/initd/cloudreve ];then
$serverPath/cloudreve/initd/cloudreve stop
fi
if [ -d $serverPath/cloudreve ];then
rm -rf $serverPath/cloudreve
fi
echo "卸载cloudreve成功"
}
action=$1
if [ "${1}" == 'install' ];then
Install_App
else
Uninstall_App
fi
-82
View File
@@ -1,82 +0,0 @@
function alistPost(method, version, args,callback){
var loadT = layer.msg('正在获取...', { icon: 16, time: 0, shade: 0.3 });
var req_data = {};
req_data['name'] = 'cloudreve';
req_data['func'] = method;
req_data['version'] = version;
if (typeof(args) == 'string'){
req_data['args'] = JSON.stringify(toArrayObject(args));
} else {
req_data['args'] = JSON.stringify(args);
}
$.post('/plugins/run', req_data, function(data) {
layer.close(loadT);
if (!data.status){
//错误展示10S
layer.msg(data.msg,{icon:0,time:2000,shade: [10, '#000']});
return;
}
if(typeof(callback) == 'function'){
callback(data);
}
},'json');
}
function alistPostCallbak(method, version, args,callback){
var loadT = layer.msg('正在获取...', { icon: 16, time: 0, shade: 0.3 });
var req_data = {};
req_data['name'] = 'cloudreve';
req_data['func'] = method;
args['version'] = version;
if (typeof(args) == 'string'){
req_data['args'] = JSON.stringify(toArrayObject(args));
} else {
req_data['args'] = JSON.stringify(args);
}
$.post('/plugins/callback', req_data, function(data) {
layer.close(loadT);
if (!data.status){
layer.msg(data.msg,{icon:0,time:2000,shade: [0.3, '#000']});
return;
}
if(typeof(callback) == 'function'){
callback(data);
}
},'json');
}
function commonHomePage(){
alistPost('home_page', '', {}, function(data){
var rdata = $.parseJSON(data.data);
window.open(rdata.data);
});
}
function alistCommonFunc(){
var con = '';
con += '<hr/><p class="conf_p" style="text-align:center;">\
<button class="btn btn-default btn-sm" onclick="commonHomePage()">主页</button>\
</p>';
$(".soft-man-con").html(con);
}
function alistReadme(){
var readme = '<ul class="help-info-text c7">';
readme += '<li>记住安装时生成的用户+密码</li>';
readme += '</ul>';
$('.soft-man-con').html(readme);
}
-78
View File
@@ -1,78 +0,0 @@
# plugins_cryptocurrency_trade
- https://docs.ccxt.com/en/latest/install.html#python-proxies
数字货币量化交易插件
```
cd /www/server/mdserver-web && python3 plugins/cryptocurrency_trade/ccxt/okex/strategy/st_demo.py
python3 plugins/cryptocurrency_trade/ccxt/okex/strategy/st_demo.py
miniconda3
```
# 免费TV策略
https://cn.tradingview.com/scripts/?script_type=strategies
### tradingview pine 指标
```
// This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © TenCloud
//@version=5
indicator("第一个指标",shorttitle = "开心吧")
ema10 = ta.ema(close, 25)
ema100 = ta.ema(close, 100)
l1=plot(ema10, color=color.red,title = "ema10")
l2=plot(ema100,color = color.green, title = "ema100")
buy = ta.crossover(ema10,ema100)
sell = ta.crossunder(ema10,ema100)
plotchar(buy, text = "buy", color=color.green)
plotchar(sell, text="sell", color=color.red)
l_color = ema10>ema100 ? color.green:color.red
fill(l1,l2,color=color.new(l_color,70))
```
### tradingview pine 策略
```
// This source code is subject to the terms of the Mozilla Public License 2.0 at https://mozilla.org/MPL/2.0/
// © TenCloud
//@version=5
strategy("第一个指标",shorttitle = "开心吧", overlay = true, initial_capital = 1000)
ema10 = ta.ema(close, 25)
ema100 = ta.ema(close, 100)
l1=plot(ema10, color=color.red,title = "ema10")
l2=plot(ema100,color = color.green, title = "ema100")
buy = ta.crossover(ema10,ema100)
sell = ta.crossunder(ema10,ema100)
if buy
strategy.entry("long1", strategy.long,1)
if sell
strategy.close("long1", qty_percent = 100,comment = "平仓多单")
// plotchar(buy, text = "buy", color=color.green)
// plotchar(sell, text="sell", color=color.red)
// l_color = ema10>ema100 ? color.green:color.red
// fill(l1,l2,color=color.new(l_color,70))
```
@@ -1,555 +0,0 @@
import ccxt
from datetime import datetime
import time
import sys
import json
import os
import glob
import threading
from pprint import pprint
# print(os.getcwd())
sys.path.append(os.getcwd() + "/class/core")
import mw
# cd /www/server/mdserver-web && source bin/activate
# python3 plugins/cryptocurrency_trade/ccxt/public_data/data.py run
# python3 plugins/cryptocurrency_trade/ccxt/public_data/data.py long
# 查看支持的交易所
# print(ccxt.exchanges)
# 代理设置
# exchange = ccxt.poloniex({
# 'proxies': {
# 'http': 'http://127.0.0.1:1088'
# },
# })
# exchange = ccxt.poloniex()
exchange = ccxt.okex()
def getPluginName():
return 'cryptocurrency_trade'
def getPluginDir():
return mw.getPluginDir() + '/' + getPluginName()
def getServerDir():
return mw.getServerDir() + '/' + getPluginName()
def getConfigData():
cfg_path = getServerDir() + "/data.cfg"
if not os.path.exists(cfg_path):
mw.writeFile(cfg_path, '{}')
t = mw.readFile(cfg_path)
return json.loads(t)
def writeLog(log_str):
if __name__ == "__main__":
print(log_str)
log_file = getServerDir() + '/logs/datasource.log'
mw.writeFileLog(log_str, log_file, 1 * 1024 * 1024)
return True
def isSetDbConf():
data = getConfigData()
if 'db' in data:
return True
return False
def beforeDate(day=360):
dd = time.time() - day * 86400
d = datetime.fromtimestamp(dd)
day = d.strftime("%Y-%m-%d")
return day
def getTextTimeShow(time):
d = datetime.fromtimestamp(time)
day = d.strftime("%Y-%m-%d %H:%M:%S")
return day
def isSqlError(mysqlMsg):
# 检测数据库执行错误
mysqlMsg = str(mysqlMsg)
if "MySQLdb" in mysqlMsg:
return mw.returnJson(False, 'MySQLdb组件缺失! <br>进入SSH命令行输入: pip install mysql-python | pip install mysqlclient==2.0.3')
if "2002," in mysqlMsg:
return mw.returnJson(False, '数据库连接失败,请检查数据库服务是否启动!')
if "2003," in mysqlMsg:
return mw.returnJson(False, "Can't connect to MySQL server on '127.0.0.1' (61)")
if "using password:" in mysqlMsg:
return mw.returnJson(False, '数据库密码错误')
if "1045" in mysqlMsg:
return mw.returnJson(False, '连接错误!')
if "SQL syntax" in mysqlMsg:
return mw.returnJson(False, 'SQL语法错误!')
if "Connection refused" in mysqlMsg:
return mw.returnJson(False, '数据库连接失败,请检查数据库服务是否启动!')
if "1133" in mysqlMsg:
return mw.returnJson(False, '数据库用户不存在!')
if "1007" in mysqlMsg:
return mw.returnJson(False, '数据库已经存在!')
return None
def pMysqlDb():
# pymysql
db = mw.getMyORM()
data = getConfigData()
db_data = data['db']
db.setHost(db_data['db_host'])
db.setPort(db_data['db_port'])
db.setUser(db_data['db_user'])
db.setPwd(db_data['db_pass'])
db.setDbName(db_data['db_name'])
return db
def makeInsertSql(table, item):
sql = "insert into " + table
keyStr = '('
valueStr = ' values('
for i in item:
# print i, item[i]
keyStr += '`' + str(i) + '`,'
valueStr += "'" + str(item[i]) + "',"
keyStrLen = len(keyStr)
keyStr = keyStr[0:keyStrLen - 1]
keyStr += ') '
valueStrLen = len(valueStr)
valueStr = valueStr[0:valueStrLen - 1]
valueStr += ') '
sql += keyStr
sql += valueStr
return sql
def makeReplaceSql(table, item):
sql = "replace into " + table
keyStr = '('
valueStr = ' values('
for i in item:
# print i, item[i]
keyStr += '`' + str(i) + '`,'
valueStr += "'" + str(item[i]) + "',"
keyStrLen = len(keyStr)
keyStr = keyStr[0:keyStrLen - 1]
keyStr += ') '
valueStrLen = len(valueStr)
valueStr = valueStr[0:valueStrLen - 1]
valueStr += ') '
sql += keyStr
sql += valueStr
return sql
def makeUpdateSql(table, item, mid):
sql = "update " + table + " set "
keyStr = ''
for i in item:
keyStr += '`' + str(i) + '`=' + "'" + item[i] + "',"
keyStrLen = len(keyStr)
keyStr = keyStr[0:keyStrLen - 1]
sql += keyStr
sql += " where id = '" + str(mid) + "'"
return sql
def dataToDb(table_name, data):
pdb = pMysqlDb()
for i in data:
rdata = {
'addtime': int(i[0] / 1000),
'open': i[1],
'high': i[2],
'low': i[3],
'close': i[4],
'vol': i[5],
}
# sql = """SELECT id FROM %s where addtime='%s' LIMIT 1""" % (
# table_name, rdata['addtime'])
# fdata = pdb.query(sql)
# if fdata:
# print(table_name + ":" + str(rdata['addtime']) + ", old to db ok")
# else:
# isql = makeReplaceSql(table_name, rdata)
# r = pdb.execute(isql)
# print(table_name + ":" + str(rdata['addtime']) + ", go to db ok")
isql = makeReplaceSql(table_name, rdata)
r = pdb.execute(isql)
# print(table_name + ":" + str(rdata['addtime']) + ", go to db ok")
return True
def makeTableName(input_type="btc", input_tf="1m"):
table_name = "ct_%s_%s" % (input_type, input_tf)
return table_name
def isHasTable(input_type="btc", input_tf="1m"):
pdb = pMysqlDb()
# input_type = 'btc'
# input_tf = '1m'
table_name = makeTableName(input_type, input_tf)
mtable = pdb.query("show tables like '%s'" % (table_name,))
if len(mtable) != 0:
return True
return False
def createSql(input_type="btc", input_tf="1m"):
pdb = pMysqlDb()
# input_type = 'btc'
# input_tf = '1m'
table_name = makeTableName(input_type, input_tf)
mtable = pdb.query("show tables like '%s'" % (table_name,))
if len(mtable) != 0:
return True
sql_tpl = getPluginDir() + "/conf/create.sql"
content = mw.readFile(sql_tpl)
content = content.replace("xx1", input_type)
content = content.replace("xx2", input_tf)
res = pdb.execute(content)
# pprint(res)
return True
def getDataFetch(symbol, start_time="2023-3-1", input_tf="1m", limit=500):
start_time = datetime.strptime(start_time, '%Y-%m-%d')
end_time = datetime.strptime(end_time, '%Y-%m-%d')
start_time_stamp = int(time.mktime(start_time.timetuple())) * 1000
data = exchange.fetch_ohlcv(symbol, timeframe=input_tf,
since=start_time_stamp, limit=limit)
return data
def getPointData(symbol, start_time=1677713220000, input_tf="1m", limit=500):
data = exchange.fetch_ohlcv(
symbol, timeframe=input_tf, since=start_time, limit=limit)
return data
def toUnixTimeSecond(tf="1m"):
if tf.find("m") > -1:
v = int(tf.replace("m", ''))
return v * 60
if tf.find("h") > -1:
v = int(tf.replace("h", ''))
return v * 3600
if tf.find("d") > -1:
v = int(tf.replace("d", ''))
return v * 86400
return 0
def findAndUpdateData(tag, input_tf="1m", start_time="2023-1-1", limit=300):
pdb = pMysqlDb()
table_name = makeTableName(tag, input_tf)
sql = 'SELECT addtime FROM ' + table_name + ' order by addtime desc LIMIT 1'
qdata = pdb.query(sql)
start_time = datetime.strptime(start_time, '%Y-%m-%d')
start_time = int(time.mktime(start_time.timetuple())) * 1000
if len(qdata) != 0:
start_time = int(qdata[0]['addtime']) * 1000
pre_time = toUnixTimeSecond(input_tf) * 5 * 1000
start_time = start_time - pre_time
# pprint(start_time)
symbol = tag.upper() + "/USDT"
data = getPointData(symbol, start_time, input_tf, limit)
# pprint(data)
# print("------------lini===========")
# pprint(data[1:])
if len(qdata) == 1:
data = data[1:]
dataToDb(table_name, data)
now = time.strftime("%m/%d %H:%M:%S")
data_len = len(data)
msg_head = now + "|虚拟币:" + tag + ",tf:" + \
input_tf + "!,总数:" + str(data_len)
writeLog(msg_head)
if data_len > 0:
# print("new data time", data[data_len - 1][0])
dt = getTextTimeShow(data[data_len - 1][0] / 1000)
msg = now + "|最新日期:" + dt
writeLog(msg)
table_name = makeTableName(tag, input_tf)
sql = "SELECT addtime FROM " + table_name + \
" order by addtime desc limit 10000,1"
qdata = pdb.query(sql)
if len(qdata) > 0:
print(qdata)
# input_tf_time = toUnixTimeSecond(input_tf)
# now_t = int(time.time())
# del_before_t = now_t - input_tf_time * 1000
sql = "delete from %s where addtime<'%d' " % (
table_name, qdata[0]['addtime'],)
writeLog("删除冗余数据:" + str(sql))
pdb.execute(sql)
return True
def dataToDbTpl(tag='btc', input_tf="1m", start_time="2023-1-1", limit=300):
# tag = "btc"
# tf = "1m"
# start_time = "2023-1-1"
# end_time = "2023-3-2"
symbol = tag.lower() + '/USDT'
if not isHasTable(tag, input_tf):
createSql(tag, input_tf)
data = getDataFetch(symbol, start_time, input_tf, limit)
table_name = makeTableName(tag, input_tf)
dataToDb(table_name, data)
now = time.strftime("%m/%d %H:%M:%S")
data_len = len(data)
writeLog(now + "|数据获取成功!,总数:" + str(data_len))
else:
findAndUpdateData(tag, input_tf, start_time, limit)
return True
def dataToDbList(input_tf="1m", start_time="2023-1-1"):
data = getConfigData()
if not 'token' in data:
writeLog("未设置同步配置,需要添加币种!")
return
for t in data['token']:
dataToDbTpl(t, input_tf, start_time)
time.sleep(4)
def dataRunToDb():
data = getConfigData()
if not 'db' in data:
writeLog("数据库未设置!")
return
tag = "btc"
symbol = tag.upper() + '/USDT'
# pprint(exchange.fetch_ticker('XRP/USDT'))
limit_count = 1
start_time = "2023-1-1"
end_time = "2023-3-2"
tf = "1m"
if not isHasTable(tag, tf):
createSql(tag, tf)
data = getDataFetch(symbol, start_time, end_time, tf, limit_count)
table_name = makeTableName(tag, tf)
dataToDb(table_name, data)
china_datetime = str(datetime.now())
data_len = len(data)
writeLog(china_datetime + ":数据获取成功!,总数:" + str(data_len))
else:
findAndUpdateData(tag, tf, start_time, 30)
return data
def startTask():
# 任务队列
try:
while True:
time.sleep(5)
except Exception as e:
time.sleep(60)
startTask()
def setDaemon(t):
if sys.version_info.major == 3 and sys.version_info.minor >= 10:
t.daemon = True
else:
t.setDaemon(True)
return t
def dataDay():
try:
while True:
if not isSetDbConf():
print("数据库未设置!")
else:
start_time = beforeDate(2 * 365)
dataToDbList("1d", start_time=start_time)
time.sleep(3600)
# time.sleep(10)
except Exception as e:
time.sleep(60)
dataDay()
def data4h():
try:
while True:
if not isSetDbConf():
print("数据库未设置!")
else:
start_time = beforeDate(365)
dataToDbList("4h", start_time=start_time)
time.sleep(10)
except Exception as e:
time.sleep(60)
data4h()
def data1h():
try:
while True:
if not isSetDbConf():
print("数据库未设置!")
else:
start_time = beforeDate(365)
dataToDbList("1h", start_time=start_time)
time.sleep(10)
except Exception as e:
print(e)
time.sleep(60)
data1h()
def data15m():
try:
while True:
if not isSetDbConf():
print("数据库未设置!")
else:
start_time = beforeDate(365)
dataToDbList("15m", start_time=start_time)
time.sleep(10)
except Exception as e:
time.sleep(60)
data15m()
def data5m():
try:
while True:
if not isSetDbConf():
print("数据库未设置!")
else:
start_time = beforeDate(180)
dataToDbList("5m", start_time=start_time)
time.sleep(10)
except Exception as e:
time.sleep(60)
data5m()
def data1m():
try:
while True:
if not isSetDbConf():
print("数据库未设置!")
else:
start_time = beforeDate(30)
dataToDbList("1m", start_time=start_time)
time.sleep(10)
except Exception as e:
time.sleep(60)
data1m()
def longRun():
# 日线
d1_tt = threading.Thread(target=dataDay)
d1_tt = setDaemon(d1_tt)
d1_tt.start()
# 4h
h4_tt = threading.Thread(target=data4h)
h4_tt = setDaemon(h4_tt)
h4_tt.start()
# 1h
h1_tt = threading.Thread(target=data1h)
h1_tt = setDaemon(h1_tt)
h1_tt.start()
# 15m
m15_tt = threading.Thread(target=data15m)
m15_tt = setDaemon(m15_tt)
m15_tt.start()
# 5m
m5_tt = threading.Thread(target=data5m)
m5_tt = setDaemon(m5_tt)
m5_tt.start()
# 1m
# h1m_tt = threading.Thread(target=data1m)
# h1m_tt = setDaemon(h1m_tt)
# h1m_tt.start()
startTask()
def dataRun():
do_num = 0
while True:
if do_num > 1:
break
do_num = do_num + 1
dataRunToDb()
time.sleep(6)
if __name__ == "__main__":
func = sys.argv[1]
if func == 'run':
data5m()()
elif func == 'long':
longRun()
elif func == 'demo':
writeLog('111')
else:
print('error')
@@ -1,12 +0,0 @@
#!/bin/bash
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH
curPath=`pwd`
# bash plugins/cryptocurrency_trade/ccxt/public_data/data.sh
if [ -f ${curPath}/bin/activate ];then
source ${curPath}/bin/activate
fi
python3 plugins/cryptocurrency_trade/ccxt/public_data/data.py long
@@ -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 && source 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 && source 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 && source 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')
@@ -1,33 +0,0 @@
[
{
"id":"01",
"name": "MACD-KDJ策略检测|1h",
"file": "macd_kdj.py"
},
{
"id":"02",
"name": "通知测试|60s",
"file": "notify_demo.py"
},
{
"id":"03",
"name": "okex|macd交易策略|15m",
"file": "online_macd_trade.py"
},
{
"id":"04",
"name": "okex|动量交易策略|5m",
"file": "momentun_trade.py"
},
{
"id":"05",
"name": "gate.io|动量交易策略|5m",
"file": "gate_100.py"
},
{
"id":"06",
"name": "仅提醒|Vega交易策略|5m",
"file": "vega_trade.py"
}
]
@@ -1,121 +0,0 @@
# cd /www/server/mdserver-web && source bin/activate
# python3 plugins/cryptocurrency_trade/ccxt/strategy/macd_kdj.py run
# python3 plugins/cryptocurrency_trade/ccxt/strategy/macd_kdj.py long
import ccxt
import talib
import pandas as pd
import time
import sys
import os
from pprint import pprint
sys.path.append(os.getcwd() + "/plugins/cryptocurrency_trade/strategy")
import common
sys.path.append(os.getcwd() + "/class/core")
import mw
pd.set_option('display.max_rows', None)
# import warnings
# warnings.filterwarnings('error')
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 True, df[lenght:]
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 checkData():
tag = 'btc'
tf_frame = '1h'
data = common.getDataFromDb_DF(tf_frame, tag, 300)
# print(data)
b, r = getMACD(data)
if b:
# rlen = len(r)
# r = r.copy()
# r['dt'] = pd.to_datetime(
# r['addtime'], unit='s')
# r['dt'] = r['dt'].dt.tz_convert('Asia/Shanghai')
# print(r)
rlen = len(r)
t_data = r.tail(2)
# print(r.tail(2))
last_pre = t_data.iloc[0]
last = t_data.iloc[1]
# print(last_pre)
# print(last)
# print(last_pre['addtime'])
# print(last_pre['open'])
# print(last_pre['high'])
# print(last_pre['low'])
# print(last_pre['close'])
# print(last['addtime'])
# print(last['open'])
# print(last['high'])
# print(last['low'])
# print(last['close'])
now = mw.getDateFromNow()
if isKdj(last, last_pre):
msg = now + "|{}|{}|检查到金叉状态!".format(tag, tf_frame)
common.notifyMsg(msg, tf_frame, tag)
common.writeLog(msg)
if isDeadFork(last, last_pre):
msg = now + "|{}|{}|检查到死叉状态!".format(tag, tf_frame)
common.notifyMsg(msg, tf_frame, tag)
common.writeLog(msg)
def run():
checkData()
def longRun():
while True:
checkData()
time.sleep(3)
if __name__ == "__main__":
func = sys.argv[1]
if func == 'run':
run()
elif func == 'long':
longRun()
else:
print('error')
@@ -1,505 +0,0 @@
# cd /www/server/mdserver-web && source bin/activate
# python3 plugins/cryptocurrency_trade/ccxt/strategy/momentun_trade.py run
# python3 plugins/cryptocurrency_trade/ccxt/strategy/momentun_trade.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 = common.initEx()
exchange.load_markets()
# 默认开仓数据
# default_open_num = 70
# default_sell_num = 0.003
default_open = {
'BTC/USDT': 30,
'XRP/USDT': 30,
}
default_sell = {
'BTC/USDT': 0.001,
'XRP/USDT': 100,
}
# 做多开仓
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']
# 修正小数点位数
open_price = common.roundVal(open_price, stop_loss_price)
common.writeLogEx('实际开仓价:' + str(open_price), symbol)
# 做多-止损价大于开仓价,重设止损价
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 = float(open_price) + (diff * 1.5)
# 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(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_num
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']
# 修正
open_price = common.roundVal(open_price, stop_loss_price)
common.writeLogEx('实际开仓价:' + str(open_price), symbol)
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 = float(open_price) - (diff * 1.5)
# 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(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', float(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 = closing_price * sell_num
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', float(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 getTarget(df):
close = df['close'].values
df['ema'] = talib.EMA(np.array(close), timeperiod=10)
df['ema_200'] = talib.EMA(np.array(close), timeperiod=200)
df['ma'] = talib.MA(close, timeperiod=10)
df['rsi'] = talib.RSI(close, timeperiod=14)
return df
# 多头信号
def isBuyCrondSignal(last):
if last['ema'] > last['ma'] and (last['rsi'] > 50 and last['rsi'] < 70) and last['low'] >= last['ma'] and last['close'] > last['open']:
return True
return False
def isBuyFristSignal(data):
data = data.sort_values(by=['timestamp'], ascending=False)
# print(data)
data_len = len(data)
signal_num = 0
first_data = data.iloc[1]
# print(1, first_data['close'], first_data['ema'])
is_buy_signal = isBuyCrondSignal(first_data)
for x in range(2, data_len):
tmp = data.iloc[x]
# print(data.iloc[x])
# print(x, tmp['close'], tmp['ema'])
if isBuyCrondSignal(tmp):
signal_num = + 1
# print('signal_num:', signal_num)
if (tmp['ema'] < tmp['ma']):
break
if str(tmp['ema']) == 'nan':
break
print("is_buy_signal:", is_buy_signal, 'signal_num:', signal_num)
if is_buy_signal and signal_num == 0:
return True
return False
# 空头信号
def isSellCrondSignal(last):
if last['ema'] < last['ma'] and (last['rsi'] > 30 and last['rsi'] < 50) and last['high'] <= last['ema'] and last['close'] < last['open']:
return True
return False
def isSellFristSignal(data):
data = data.sort_values(by=['timestamp'], ascending=False)
# print(data)
data_len = len(data)
signal_num = 0
first_data = data.iloc[1]
# print(1, first_data['close'], first_data['ema'])
is_sell_signal = isSellCrondSignal(first_data)
for x in range(2, data_len):
tmp = data.iloc[x]
# print(data.iloc[x])
# print(x, tmp['close'], tmp['ema'])
if isSellCrondSignal(tmp):
signal_num = + 1
# print('signal_num:', signal_num)
if (tmp['ema'] < tmp['ma']):
break
if str(tmp['ema']) == 'nan':
break
print("is_sell_signal:", is_sell_signal, 'signal_num:', signal_num)
if is_sell_signal and signal_num == 0:
return True
return False
def monentunTrade(data, tag, timeframe):
data = getTarget(data)
# print(data)
key_data = data.tail(3)
print(key_data)
last_pre = key_data.iloc[0]
last = key_data.iloc[1]
obj = common.MsgTpl()
symbol = tag.upper() + '/USDT'
obj.setName(symbol)
obj.setStrategyName("动量交易策略")
obj.setTimeFrame(timeframe)
obj.setOpenTime(last['timestamp'] / 1000)
# 买入信号,并且收盘价要大于200 ema
if isBuyFristSignal(data) and last['close'] > last['ema_200']:
obj.setStrategicDt('buy')
# 做多止损点
stop_loss_price = last['ma']
stop_loss_price = common.roundVal(stop_loss_price, last['open'])
buy_status, open_price, closing_price = onBuyOrder(
symbol, stop_loss_price, 0.005, timeframe)
if buy_status:
# 做多止损点
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)
# 卖出信号,并且收盘价要小于200 ema
# if isSellFristSignal(data) and last['close'] < last['ema_200']:
# obj.setStrategicDt('sell')
# stop_loss_price = last['ma']
# stop_loss_price = common.roundVal(stop_loss_price, last['open'])
# 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)
monentunTrade(data, tag, timeframe)
def foreachList():
tag_list = ['btc', 'xrp']
for tag in tag_list:
mainProcess(tag, '15m')
time.sleep(1)
def longRun():
while True:
foreachList()
time.sleep(1)
def debug():
while True:
mainProcess('btc', '5m')
time.sleep(1)
if __name__ == "__main__":
func = sys.argv[1]
if func == 'long':
longRun()
elif func == 'run':
debug()
else:
print('error')
@@ -1,48 +0,0 @@
# cd /www/server/mdserver-web && source bin/activate
# python3 plugins/cryptocurrency_trade/ccxt/strategy/notify_demo.py run
# python3 plugins/cryptocurrency_trade/ccxt/strategy/notify_demo.py long
import ccxt
import talib
import pandas as pd
import time
import sys
import os
from pprint import pprint
sys.path.append(os.getcwd() + "/plugins/cryptocurrency_trade/strategy")
import common
sys.path.append(os.getcwd() + "/class/core")
import mw
def run():
print('debug')
def longRun():
while True:
obj = common.MsgTpl()
obj.setName("通知测试")
obj.setStrategyName("无")
obj.setTimeFrame("1m")
obj.setContent("60s通知测试")
msg = obj.toText()
print(msg)
common.notifyMsg(msg, '1m', 'debug')
common.writeLog(msg)
time.sleep(3)
if __name__ == "__main__":
func = sys.argv[1]
if func == 'run':
run()
if func == 'long':
longRun()
else:
print('error')
@@ -1,294 +0,0 @@
# cd /www/server/mdserver-web && source bin/activate
# python3 plugins/cryptocurrency_trade/ccxt/strategy/on_g_test.py t_buy_open
# python3 plugins/cryptocurrency_trade/ccxt/strategy/on_g_test.py t_buy_close
# python3 plugins/cryptocurrency_trade/ccxt/strategy/on_g_test.py t_sell_open
# python3 plugins/cryptocurrency_trade/ccxt/strategy/on_g_test.py t_sell_cloe
# 获取仓位数据
# python3 plugins/cryptocurrency_trade/ccxt/strategy/on_g_test.py t_get_trade
# API地址
# https://www.gate.io/docs/developers/apiv4/zh_CN/#api
import ccxt
import talib
import sys
import os
import time
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()
def t_get_trade():
data = exchange.fetchPositions()
print(data)
def btc_test():
closing_price = 24599.9
stop_loss_price = 24740.4
# ---------------------------
stop_loss_args = {
'ccy': "USDT",
'reduceOnly': True,
'tdMode': "cross",
'slOrdPx': "-1",
'slTriggerPx': stop_loss_price,
}
print('---------止损价 执行 START ----------------------------------')
sl_amount = stop_loss_price * 0.001
print(amount)
data = exchange.create_order(
'BTC/USDT', 'limit', 'buy', sl_amount, stop_loss_price, stop_loss_args)
print(data)
print('---------止损价 执行 END ----------------------------------')
print('---------止盈价 执行 START ----------------------------------')
closing_price_args = {
'ccy': "USDT",
'reduceOnly': True,
'tdMode': "cross",
'tpOrdPx': "-1",
'tpTriggerPx': closing_price,
}
cp_amount = closing_price * 0.001
print(amount)
data = exchange.create_order(
'BTC/USDT', 'limit', 'buy', cp_amount, closing_price, closing_price_args)
print(data)
print('---------止盈价 执行 END ----------------------------------')
def testK_buy_open_sz():
# 做多开仓
data = exchange.fetchTicker('BTC/USDT')
print(data)
# data = exchange.createMarketBuyOrder('DOT/USDT', 1, {"tdMode": "cross"})
# print(data)
# print(type(data))
def testK_buy_open():
# 做多开仓
# print(dir(exchange))
# exchange['options']['createMarketBuyOrderRequiresPrice'] = False
data = exchange.fetch_ticker('BTC/USDT')
print("now price:", data['ask'])
amount = 0.001
price = data['ask']
cost = amount * float(price)
print('total price:', cost)
print('amount price:', amount)
# a_amount = round(amount / data['ask'], 5)
# print('amount :', amount)
# print('amount :', str(amount))
#
data = exchange.createOrder(
"BTC/USDT", type="limit", side="buy", amount=amount, price=price, params={'account': "cross_margin"})
# data = exchange.createMarketBuyOrder('BTC/USDT', amount)
print(data)
print(type(data))
# 数据
# {'info': {'clOrdId': 'e847386590ce4dBC58e8b0afb0fe70cc', 'ordId': '554437054223302656', 'sCode': '0', 'sMsg': 'Order placed', 'tag': 'e847386590ce4dBC'}, 'id': '554437054223302656', 'clientOrderId': 'e847386590ce4dBC58e8b0afb0fe70cc', 'timestamp': None, 'datetime': None, 'lastTradeTimestamp': None, 'symbol': 'DOT/USDT', 'type': 'market', 'timeInForce': None, 'postOnly': None, 'side': 'buy', 'price': None, 'stopPrice': None, 'triggerPrice': None, 'average': None, 'cost': None, 'amount': None, 'filled': None, 'remaining': None, 'status': None, 'fee': None, 'trades': [], 'reduceOnly': None, 'fees': []}
# 查询委托单
# order_id = data['info']['ordId']
# order_id = '554437054223302656'
# data = exchange.fetchOrder(order_id, 'BTC/USDT')
# print(data['info'])
# {'info': {'accFillSz': '0.190548', 'algoClOrdId': '', 'algoId': '', 'avgPx': '5.248', 'cTime': '1678441709955', 'cancelSource': '', 'cancelSourceReason': '', 'category': 'normal', 'ccy': 'USDT', 'clOrdId': 'e847386590ce4dBCeaddfd1494dc7080', 'fee': '-0.000190548', 'feeCcy': 'DOT', 'fillPx': '5.248', 'fillSz': '0.190548', 'fillTime': '1678441709957', 'instId': 'DOT-USDT', 'instType': 'MARGIN', 'lever': '10', 'ordId': '554359943143833600', 'ordType': 'market', 'pnl': '0', 'posSide': 'net', 'px': '', 'quickMgnType': '', 'rebate': '0', 'rebateCcy': 'USDT', 'reduceOnly': 'false', 'side': 'buy', 'slOrdPx': '', 'slTriggerPx': '', 'slTriggerPxType': '', 'source': '', 'state': 'filled', 'sz': '1', 'tag': 'e847386590ce4dBC', 'tdMode': 'cross', 'tgtCcy': '', 'tpOrdPx': '', 'tpTriggerPx': '', 'tpTriggerPxType': '', 'tradeId': '81184616', 'uTime': '1678441709960'}, 'id': '554359943143833600', 'clientOrderId': 'e847386590ce4dBCeaddfd1494dc7080', 'timestamp': 1678441709955, 'datetime': '2023-03-10T09:48:29.955Z', 'lastTradeTimestamp': 1678441709957, 'symbol': 'DOT/USDT', 'type': 'market', 'timeInForce': 'IOC', 'postOnly': None, 'side': 'buy', 'price': 5.248, 'stopPrice': None, 'triggerPrice': None, 'average': 5.248, 'cost': 0.999995904, 'amount': 1.0, 'filled': 0.190548, 'remaining': 0.809452, 'status': 'closed', 'fee': {'cost': 0.000190548, 'currency': 'DOT'}, 'trades': [], 'reduceOnly': False, 'fees': [{'cost': 0.000190548, 'currency': 'DOT'}]}
# open_price = data['info']['avgPx']
# print("开仓平均价", open_price)
# # 止盈价
# closing_price = float(open_price) * float((1 + 0.005))
# closing_price = common.roundVal(closing_price, open_price)
# print("止盈价", closing_price)
# # 止损价
# stop_loss_price = float(open_price) * float((1 - 0.01))
# stop_loss_price = common.roundVal(stop_loss_price, open_price)
# print("止损价", stop_loss_price)
# property_val = float(data['info']['accFillSz']) + \
# float(data['info']['fee'])
# # 相同位数
# property_val = common.roundValCeil(property_val, data['info']['accFillSz'])
# print("可平仓资产", property_val)
# closed_orders = exchange.fetchClosedOrders('DOT/USDT', limit=2)
# print('closed_orders', closed_orders)
# print(exchange.fetchBalance())
# 可以用,限价单
# time.sleep(1)
# data = exchange.createLimitSellOrder(
# 'DOT/USDT', property_val, closing_price, {"tdMode": "cross", 'ccy': 'USDT', "reduceOnly": True})
# print(data)
# print(type(data))
# 止损价
# 止盈价
# exchange_params = {
# 'ccy': "USDT",
# 'reduceOnly': True,
# 'tdMode': "cross",
# 'tpOrdPx': "-1",
# 'tpTriggerPx': closing_price,
# 'slOrdPx': "-1",
# 'slTriggerPx': stop_loss_price,
# }
# print('---------止损价 执行----------------------------------')
# data = exchange.create_order(
# 'BTC/USDT', 'limit', 'sell', property_val, closing_price, exchange_params)
# print(data)
# print(type(data))
# print('---------止盈价 执行----------------------------------')
# exchange_params = {
# 'stopPrice': closing_price,
# 'type': 'stopLimit',
# }
# data = exchange.create_order(
# 'DOT/USDT', 'limit', 'sell', property_val, closing_price, exchange_params)
# print(data)
# print(type(data))
def testK_buy_close():
# 平多
data = exchange.create_limit_buy_order(
'DOT/USDT', 1, 5, {"tdMode": "cross"})
print(data)
print(type(data))
# closed_orders = exchange.fetchClosedOrders('BTC/USDT', limit=2)
# print(closed_orders)
# closed_orders = exchange.fetchMyTrades('ETH/USDT', limit=2)
# print(closed_orders)
def testK_sell_open():
# 做空开仓
# data = exchange.createMarketSellOrder(
# 'DOT/USDT', 1, {"tdMode": "cross", 'ccy': "USDT", })
# print(data)
# print(type(data))
# 数据
# {'info': {'clOrdId': 'e847386590ce4dBC58e8b0afb0fe70cc', 'ordId': '554437054223302656', 'sCode': '0', 'sMsg': 'Order placed', 'tag': 'e847386590ce4dBC'}, 'id': '554437054223302656', 'clientOrderId': 'e847386590ce4dBC58e8b0afb0fe70cc', 'timestamp': None, 'datetime': None, 'lastTradeTimestamp': None, 'symbol': 'DOT/USDT', 'type': 'market', 'timeInForce': None, 'postOnly': None, 'side': 'buy', 'price': None, 'stopPrice': None, 'triggerPrice': None, 'average': None, 'cost': None, 'amount': None, 'filled': None, 'remaining': None, 'status': None, 'fee': None, 'trades': [], 'reduceOnly': None, 'fees': []}
# # 查询委托单
# order_id = data['info']['ordId']
order_id = '554516809819832320'
data = exchange.fetchOrder(order_id, 'DOT/USDT')
print(data['info'])
# {'info': {'accFillSz': '0.190548', 'algoClOrdId': '', 'algoId': '', 'avgPx': '5.248', 'cTime': '1678441709955', 'cancelSource': '', 'cancelSourceReason': '', 'category': 'normal', 'ccy': 'USDT', 'clOrdId': 'e847386590ce4dBCeaddfd1494dc7080', 'fee': '-0.000190548', 'feeCcy': 'DOT', 'fillPx': '5.248', 'fillSz': '0.190548', 'fillTime': '1678441709957', 'instId': 'DOT-USDT', 'instType': 'MARGIN', 'lever': '10', 'ordId': '554359943143833600', 'ordType': 'market', 'pnl': '0', 'posSide': 'net', 'px': '', 'quickMgnType': '', 'rebate': '0', 'rebateCcy': 'USDT', 'reduceOnly': 'false', 'side': 'buy', 'slOrdPx': '', 'slTriggerPx': '', 'slTriggerPxType': '', 'source': '', 'state': 'filled', 'sz': '1', 'tag': 'e847386590ce4dBC', 'tdMode': 'cross', 'tgtCcy': '', 'tpOrdPx': '', 'tpTriggerPx': '', 'tpTriggerPxType': '', 'tradeId': '81184616', 'uTime': '1678441709960'}, 'id': '554359943143833600', 'clientOrderId': 'e847386590ce4dBCeaddfd1494dc7080', 'timestamp': 1678441709955, 'datetime': '2023-03-10T09:48:29.955Z', 'lastTradeTimestamp': 1678441709957, 'symbol': 'DOT/USDT', 'type': 'market', 'timeInForce': 'IOC', 'postOnly': None, 'side': 'buy', 'price': 5.248, 'stopPrice': None, 'triggerPrice': None, 'average': 5.248, 'cost': 0.999995904, 'amount': 1.0, 'filled': 0.190548, 'remaining': 0.809452, 'status': 'closed', 'fee': {'cost': 0.000190548, 'currency': 'DOT'}, 'trades': [], 'reduceOnly': False, 'fees': [{'cost': 0.000190548, 'currency': 'DOT'}]}
open_price = data['info']['avgPx']
print("开仓平均价", open_price)
# 止盈价
closing_price = float(open_price) * float((1 - 0.005))
closing_price = common.roundVal(closing_price, open_price)
print("止盈价", closing_price)
# 止损价
stop_loss_price = float(open_price) * float((1 + 0.01))
stop_loss_price = common.roundVal(stop_loss_price, open_price)
print("止损价", stop_loss_price)
property_val = float(data['info']['accFillSz'])
# 相同位数
print("可平仓资产", property_val)
# 止损价
# 止盈价
exchange_params = {
'ccy': "USDT",
'reduceOnly': True,
'tdMode': "cross",
'tpOrdPx': "-1",
'tpTriggerPx': closing_price,
'slOrdPx': "-1",
'slTriggerPx': stop_loss_price,
}
print('---------止损价 执行----------------------------------')
data = exchange.create_order(
'DOT/USDT', 'limit', 'buy', property_val, closing_price, exchange_params)
print(data)
print(type(data))
def testK_sell_close():
# 平多
data = exchange.create_limit_buy_order(
'DOT/USDT', 1, 5, {"tdMode": "cross"})
print(data)
print(type(data))
# closed_orders = exchange.fetchClosedOrders('BTC/USDT', limit=2)
# print(closed_orders)
# closed_orders = exchange.fetchMyTrades('ETH/USDT', limit=2)
# print(closed_orders)
if __name__ == "__main__":
func = sys.argv[1]
if func == 'long':
longRun()
elif func == 'run':
debug()
elif func == 'test':
testKdan()
elif func == 't_get_trade':
t_get_trade()
elif func == 't_buy_open':
testK_buy_open()
elif func == 't_buy_close':
testK_buy_close()
elif func == 't_sell_open':
testK_sell_open()
elif func == 't_sell_cloe':
testK_sell_close()
else:
print('error')
@@ -1,437 +0,0 @@
# cd /www/server/mdserver-web && source bin/activate
# python3 plugins/cryptocurrency_trade/ccxt/strategy/online_macd_trade.py run
# python3 plugins/cryptocurrency_trade/ccxt/strategy/online_macd_trade.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 = common.initEx()
exchange.load_markets()
# 默认开仓数据
# default_open_num = 10
default_open = {
'ETH/USDT': 30,
'DOT/USDT': 30,
'CEL/USDT': 30,
}
default_sell = {
'ETH/USDT': 0.02,
'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 = [
'eth', 'dot'
]
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,273 +0,0 @@
# cd /www/server/mdserver-web && source bin/activate
# python3 plugins/cryptocurrency_trade/ccxt/strategy/online_test.py t_buy_open
# python3 plugins/cryptocurrency_trade/ccxt/strategy/online_test.py t_buy_close
# python3 plugins/cryptocurrency_trade/ccxt/strategy/online_test.py t_sell_open
# python3 plugins/cryptocurrency_trade/ccxt/strategy/online_test.py t_sell_cloe
# 获取仓位数据
# python3 plugins/cryptocurrency_trade/ccxt/strategy/online_test.py t_get_trade
import ccxt
import talib
import sys
import os
import time
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 = common.initEx()
exchange.load_markets()
def t_get_trade():
data = exchange.fetchPositions()
print(data)
def btc_test():
closing_price = 24599.9
stop_loss_price = 24740.4
# ---------------------------
stop_loss_args = {
'ccy': "USDT",
'reduceOnly': True,
'tdMode': "cross",
'slOrdPx': "-1",
'slTriggerPx': stop_loss_price,
}
print('---------止损价 执行 START ----------------------------------')
sl_amount = stop_loss_price * 0.003
print(amount)
data = exchange.create_order(
'BTC/USDT', 'limit', 'buy', sl_amount, stop_loss_price, stop_loss_args)
print(data)
print('---------止损价 执行 END ----------------------------------')
print('---------止盈价 执行 START ----------------------------------')
closing_price_args = {
'ccy': "USDT",
'reduceOnly': True,
'tdMode': "cross",
'tpOrdPx': "-1",
'tpTriggerPx': closing_price,
}
cp_amount = closing_price * 0.003
print(amount)
data = exchange.create_order(
'BTC/USDT', 'limit', 'buy', cp_amount, closing_price, closing_price_args)
print(data)
print('---------止盈价 执行 END ----------------------------------')
def testK_buy_open_sz():
# 做多开仓
data = exchange.fetchTicker('DOT/USDT')
print(data)
# data = exchange.createMarketBuyOrder('DOT/USDT', 1, {"tdMode": "cross"})
# print(data)
# print(type(data))
def testK_buy_open():
# 做多开仓
data = exchange.createMarketBuyOrder(
'DOT/USDT', 1, {"tdMode": "cross"})
print(data)
print(type(data))
# 数据
# {'info': {'clOrdId': 'e847386590ce4dBC58e8b0afb0fe70cc', 'ordId': '554437054223302656', 'sCode': '0', 'sMsg': 'Order placed', 'tag': 'e847386590ce4dBC'}, 'id': '554437054223302656', 'clientOrderId': 'e847386590ce4dBC58e8b0afb0fe70cc', 'timestamp': None, 'datetime': None, 'lastTradeTimestamp': None, 'symbol': 'DOT/USDT', 'type': 'market', 'timeInForce': None, 'postOnly': None, 'side': 'buy', 'price': None, 'stopPrice': None, 'triggerPrice': None, 'average': None, 'cost': None, 'amount': None, 'filled': None, 'remaining': None, 'status': None, 'fee': None, 'trades': [], 'reduceOnly': None, 'fees': []}
# 查询委托单
order_id = data['info']['ordId']
# order_id = '554437054223302656'
data = exchange.fetchOrder(order_id, 'DOT/USDT')
print(data['info'])
# {'info': {'accFillSz': '0.190548', 'algoClOrdId': '', 'algoId': '', 'avgPx': '5.248', 'cTime': '1678441709955', 'cancelSource': '', 'cancelSourceReason': '', 'category': 'normal', 'ccy': 'USDT', 'clOrdId': 'e847386590ce4dBCeaddfd1494dc7080', 'fee': '-0.000190548', 'feeCcy': 'DOT', 'fillPx': '5.248', 'fillSz': '0.190548', 'fillTime': '1678441709957', 'instId': 'DOT-USDT', 'instType': 'MARGIN', 'lever': '10', 'ordId': '554359943143833600', 'ordType': 'market', 'pnl': '0', 'posSide': 'net', 'px': '', 'quickMgnType': '', 'rebate': '0', 'rebateCcy': 'USDT', 'reduceOnly': 'false', 'side': 'buy', 'slOrdPx': '', 'slTriggerPx': '', 'slTriggerPxType': '', 'source': '', 'state': 'filled', 'sz': '1', 'tag': 'e847386590ce4dBC', 'tdMode': 'cross', 'tgtCcy': '', 'tpOrdPx': '', 'tpTriggerPx': '', 'tpTriggerPxType': '', 'tradeId': '81184616', 'uTime': '1678441709960'}, 'id': '554359943143833600', 'clientOrderId': 'e847386590ce4dBCeaddfd1494dc7080', 'timestamp': 1678441709955, 'datetime': '2023-03-10T09:48:29.955Z', 'lastTradeTimestamp': 1678441709957, 'symbol': 'DOT/USDT', 'type': 'market', 'timeInForce': 'IOC', 'postOnly': None, 'side': 'buy', 'price': 5.248, 'stopPrice': None, 'triggerPrice': None, 'average': 5.248, 'cost': 0.999995904, 'amount': 1.0, 'filled': 0.190548, 'remaining': 0.809452, 'status': 'closed', 'fee': {'cost': 0.000190548, 'currency': 'DOT'}, 'trades': [], 'reduceOnly': False, 'fees': [{'cost': 0.000190548, 'currency': 'DOT'}]}
open_price = data['info']['avgPx']
print("开仓平均价", open_price)
# 止盈价
closing_price = float(open_price) * float((1 + 0.005))
closing_price = common.roundVal(closing_price, open_price)
print("止盈价", closing_price)
# 止损价
stop_loss_price = float(open_price) * float((1 - 0.01))
stop_loss_price = common.roundVal(stop_loss_price, open_price)
print("止损价", stop_loss_price)
property_val = float(data['info']['accFillSz']) + \
float(data['info']['fee'])
# 相同位数
property_val = common.roundValCeil(property_val, data['info']['accFillSz'])
print("可平仓资产", property_val)
# closed_orders = exchange.fetchClosedOrders('DOT/USDT', limit=2)
# print('closed_orders', closed_orders)
# print(exchange.fetchBalance())
# 可以用,限价单
# time.sleep(1)
# data = exchange.createLimitSellOrder(
# 'DOT/USDT', property_val, closing_price, {"tdMode": "cross", 'ccy': 'USDT', "reduceOnly": True})
# print(data)
# print(type(data))
# 止损价
# 止盈价
exchange_params = {
'ccy': "USDT",
'reduceOnly': True,
'tdMode': "cross",
'tpOrdPx': "-1",
'tpTriggerPx': closing_price,
'slOrdPx': "-1",
'slTriggerPx': stop_loss_price,
}
print('---------止损价 执行----------------------------------')
data = exchange.create_order(
'DOT/USDT', 'limit', 'sell', property_val, closing_price, exchange_params)
print(data)
print(type(data))
# print('---------止盈价 执行----------------------------------')
# exchange_params = {
# 'stopPrice': closing_price,
# 'type': 'stopLimit',
# }
# data = exchange.create_order(
# 'DOT/USDT', 'limit', 'sell', property_val, closing_price, exchange_params)
# print(data)
# print(type(data))
def testK_buy_close():
# 平多
data = exchange.create_limit_buy_order(
'DOT/USDT', 1, 5, {"tdMode": "cross"})
print(data)
print(type(data))
# closed_orders = exchange.fetchClosedOrders('BTC/USDT', limit=2)
# print(closed_orders)
# closed_orders = exchange.fetchMyTrades('ETH/USDT', limit=2)
# print(closed_orders)
def testK_sell_open():
# 做空开仓
# data = exchange.createMarketSellOrder(
# 'DOT/USDT', 1, {"tdMode": "cross", 'ccy': "USDT", })
# print(data)
# print(type(data))
# 数据
# {'info': {'clOrdId': 'e847386590ce4dBC58e8b0afb0fe70cc', 'ordId': '554437054223302656', 'sCode': '0', 'sMsg': 'Order placed', 'tag': 'e847386590ce4dBC'}, 'id': '554437054223302656', 'clientOrderId': 'e847386590ce4dBC58e8b0afb0fe70cc', 'timestamp': None, 'datetime': None, 'lastTradeTimestamp': None, 'symbol': 'DOT/USDT', 'type': 'market', 'timeInForce': None, 'postOnly': None, 'side': 'buy', 'price': None, 'stopPrice': None, 'triggerPrice': None, 'average': None, 'cost': None, 'amount': None, 'filled': None, 'remaining': None, 'status': None, 'fee': None, 'trades': [], 'reduceOnly': None, 'fees': []}
# # 查询委托单
# order_id = data['info']['ordId']
order_id = '554516809819832320'
data = exchange.fetchOrder(order_id, 'DOT/USDT')
print(data['info'])
# {'info': {'accFillSz': '0.190548', 'algoClOrdId': '', 'algoId': '', 'avgPx': '5.248', 'cTime': '1678441709955', 'cancelSource': '', 'cancelSourceReason': '', 'category': 'normal', 'ccy': 'USDT', 'clOrdId': 'e847386590ce4dBCeaddfd1494dc7080', 'fee': '-0.000190548', 'feeCcy': 'DOT', 'fillPx': '5.248', 'fillSz': '0.190548', 'fillTime': '1678441709957', 'instId': 'DOT-USDT', 'instType': 'MARGIN', 'lever': '10', 'ordId': '554359943143833600', 'ordType': 'market', 'pnl': '0', 'posSide': 'net', 'px': '', 'quickMgnType': '', 'rebate': '0', 'rebateCcy': 'USDT', 'reduceOnly': 'false', 'side': 'buy', 'slOrdPx': '', 'slTriggerPx': '', 'slTriggerPxType': '', 'source': '', 'state': 'filled', 'sz': '1', 'tag': 'e847386590ce4dBC', 'tdMode': 'cross', 'tgtCcy': '', 'tpOrdPx': '', 'tpTriggerPx': '', 'tpTriggerPxType': '', 'tradeId': '81184616', 'uTime': '1678441709960'}, 'id': '554359943143833600', 'clientOrderId': 'e847386590ce4dBCeaddfd1494dc7080', 'timestamp': 1678441709955, 'datetime': '2023-03-10T09:48:29.955Z', 'lastTradeTimestamp': 1678441709957, 'symbol': 'DOT/USDT', 'type': 'market', 'timeInForce': 'IOC', 'postOnly': None, 'side': 'buy', 'price': 5.248, 'stopPrice': None, 'triggerPrice': None, 'average': 5.248, 'cost': 0.999995904, 'amount': 1.0, 'filled': 0.190548, 'remaining': 0.809452, 'status': 'closed', 'fee': {'cost': 0.000190548, 'currency': 'DOT'}, 'trades': [], 'reduceOnly': False, 'fees': [{'cost': 0.000190548, 'currency': 'DOT'}]}
open_price = data['info']['avgPx']
print("开仓平均价", open_price)
# 止盈价
closing_price = float(open_price) * float((1 - 0.005))
closing_price = common.roundVal(closing_price, open_price)
print("止盈价", closing_price)
# 止损价
stop_loss_price = float(open_price) * float((1 + 0.01))
stop_loss_price = common.roundVal(stop_loss_price, open_price)
print("止损价", stop_loss_price)
property_val = float(data['info']['accFillSz'])
# 相同位数
print("可平仓资产", property_val)
# 止损价
# 止盈价
exchange_params = {
'ccy': "USDT",
'reduceOnly': True,
'tdMode': "cross",
'tpOrdPx': "-1",
'tpTriggerPx': closing_price,
'slOrdPx': "-1",
'slTriggerPx': stop_loss_price,
}
print('---------止损价 执行----------------------------------')
data = exchange.create_order(
'DOT/USDT', 'limit', 'buy', property_val, closing_price, exchange_params)
print(data)
print(type(data))
def testK_sell_close():
# 平多
data = exchange.create_limit_buy_order(
'DOT/USDT', 1, 5, {"tdMode": "cross"})
print(data)
print(type(data))
# closed_orders = exchange.fetchClosedOrders('BTC/USDT', limit=2)
# print(closed_orders)
# closed_orders = exchange.fetchMyTrades('ETH/USDT', limit=2)
# print(closed_orders)
if __name__ == "__main__":
func = sys.argv[1]
if func == 'long':
longRun()
elif func == 'run':
debug()
elif func == 'test':
testKdan()
elif func == 't_get_trade':
t_get_trade()
elif func == 't_buy_open':
btc_test()
elif func == 't_buy_close':
testK_buy_close()
elif func == 't_sell_open':
testK_sell_open()
elif func == 't_sell_cloe':
testK_sell_close()
else:
print('error')
@@ -1,102 +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 && source bin/activate
# python3 plugins/cryptocurrency_trade/ccxt/strategy/rsi_robot.py run
pd.set_option('display.max_rows', None)
exchange = common.initEx()
# 查看隐形方法
# print(dir(exchange))
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 runBot():
bars = exchange.fetch_ohlcv(symbol, timeframe=timeframe, limit=200)
df = pd.DataFrame(bars[:], columns=['timestamp',
'open', 'high', 'low', 'close', 'volume'])
df['dt'] = pd.to_datetime(df['timestamp'], unit='ms')
df = indicators(df).tail(30)
check_buy_sell_signals(df)
def longRunBot():
while True:
runBot()
time.sleep(10)
if __name__ == "__main__":
func = sys.argv[1]
if func == 'run':
longRunBot()
else:
print('error')
@@ -1,536 +0,0 @@
# cd /www/server/mdserver-web && source bin/activate
# python3 plugins/cryptocurrency_trade/ccxt/strategy/vega_trade.py run
# python3 plugins/cryptocurrency_trade/ccxt/strategy/vega_trade.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 = common.initEx()
exchange.load_markets()
# 默认开仓数据
# default_open_num = 70
# default_sell_num = 0.003
default_open = {
'BTC/USDT': 70,
'XRP/USDT': 70,
}
default_sell = {
'BTC/USDT': 0.003,
'XRP/USDT': 185,
}
# 做多开仓
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']
# 修正小数点位数
open_price = common.roundVal(open_price, stop_loss_price)
common.writeLogEx('实际开仓价:' + str(open_price), symbol)
# 做多-止损价大于开仓价,重设止损价
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 = float(open_price) + (diff * 1.5)
# 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(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_num
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']
# 修正
open_price = common.roundVal(open_price, stop_loss_price)
common.writeLogEx('实际开仓价:' + str(open_price), symbol)
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 = float(open_price) - (diff * 1.5)
# 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(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', float(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 = closing_price * sell_num
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', float(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=500):
bars = exchange.fetch_ohlcv(symbol, timeframe=input_tf, limit=500)
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 getTarget(df):
close = df['close'].values
# vega
df['ema144'] = talib.EMA(np.array(close), timeperiod=144)
df['ema169'] = talib.EMA(np.array(close), timeperiod=169)
df['ema288'] = talib.EMA(np.array(close), timeperiod=288)
df['ema388'] = talib.EMA(np.array(close), timeperiod=388)
df['rsi'] = talib.RSI(close, timeperiod=14)
return df
# 多头信号
def isBuyCrondSignal(last):
if last['ema'] > last['ma'] and (last['rsi'] > 50 and last['rsi'] < 70) and last['low'] >= last['ma'] and last['close'] > last['open']:
return True
return False
def isBuyFristSignal(data):
data = data.sort_values(by=['timestamp'], ascending=False)
# print(data)
data_len = len(data)
signal_num = 0
first_data = data.iloc[1]
# print(1, first_data['close'], first_data['ema'])
is_buy_signal = isBuyCrondSignal(first_data)
for x in range(2, data_len):
tmp = data.iloc[x]
# print(data.iloc[x])
# print(x, tmp['close'], tmp['ema'])
if isBuyCrondSignal(tmp):
signal_num = + 1
# print('signal_num:', signal_num)
if (tmp['ema'] < tmp['ma']):
break
if str(tmp['ema']) == 'nan':
break
print("is_buy_signal:", is_buy_signal, 'signal_num:', signal_num)
if is_buy_signal and signal_num == 0:
return True
return False
# 空头信号
def isSellCrondSignal(last):
if last['ema'] < last['ma'] and (last['rsi'] > 30 and last['rsi'] < 50) and last['high'] <= last['ema'] and last['close'] < last['open']:
return True
return False
def isSellFristSignal(data):
data = data.sort_values(by=['timestamp'], ascending=False)
# print(data)
data_len = len(data)
signal_num = 0
first_data = data.iloc[1]
# print(1, first_data['close'], first_data['ema'])
is_sell_signal = isSellCrondSignal(first_data)
for x in range(2, data_len):
tmp = data.iloc[x]
# print(data.iloc[x])
# print(x, tmp['close'], tmp['ema'])
if isSellCrondSignal(tmp):
signal_num = + 1
# print('signal_num:', signal_num)
if (tmp['ema'] < tmp['ma']):
break
if str(tmp['ema']) == 'nan':
break
print("is_sell_signal:", is_sell_signal, 'signal_num:', signal_num)
if is_sell_signal and signal_num == 0:
return True
return False
def isDownTrendCover(data, tag, timeframe):
# print(tag, timeframe, "开始检测是否下跌趋势")
# 下跌趋势回调
key_data = data.tail(2)
last_pre = key_data.iloc[0]
last = key_data.iloc[1]
pdata = data.tail(11)
for x in range(10):
t = pdata.iloc[x]
if t['low'] > t['ema388']:
print(tag, timeframe, "上升震荡行情过滤")
# print(t)
return False
if last['ema144'] > last['ema388']:
return False
print(tag, timeframe, "检查是下跌趋势!")
# 检查是否是下跌趋势
if last['high'] > last['ema388']:
return True
return False
def isUpTrendAndCover(data, tag, timeframe):
# print(tag, timeframe, "开始检测是否上升趋势")
# 上升趋势回调
key_data = data.tail(2)
# print(key_data)
last_pre = key_data.iloc[0]
last = key_data.iloc[1]
pdata = data.tail(11)
for x in range(10):
t = pdata.iloc[x]
if t['low'] < t['ema144']:
print(tag, timeframe, "上升震荡行情过滤")
return False
if last['ema144'] < last['ema388']:
return False
print(tag, timeframe, "检查是上升趋势")
# 检查是否是上升趋势
if last['low'] < last['ema144']:
return True
return False
def vegaTrade(data, tag, timeframe):
data = getTarget(data)
# print(data)
key_data = data.tail(2)
# print(key_data)
# last_pre = key_data.iloc[0]
last = key_data.iloc[1]
# print(last)
obj = common.MsgTpl()
symbol = tag.upper() + '/USDT'
obj.setName(symbol)
obj.setStrategyName("VEGA交易策略|仅具有指导作用")
obj.setTimeFrame(timeframe)
obj.setOpenTime(last['addtime'])
if isUpTrendAndCover(data, tag, timeframe):
obj.setStrategicDt('buy')
obj.setContent("VEGA上升趋势!回调做多~建议!")
msg = obj.toText()
common.notifyMsg(msg, timeframe, tag)
common.writeLog(msg)
if isDownTrendCover(data, tag, timeframe):
obj.setStrategicDt('sell')
obj.setContent("Vega下跌趋势!回调做空~建议!")
msg = obj.toText()
common.notifyMsg(msg, timeframe, tag)
common.writeLog(msg)
def mainProcess(tag, timeframe='5m'):
symbol = tag.upper() + '/USDT'
data = common.getDataFromDb_DF(timeframe, tag.lower())
vegaTrade(data, tag, timeframe)
return True
def foreachList():
tag_list = ['btc', 'xrp', 'eth', 'ltc', 'okb']
for tag in tag_list:
trame_list = ['5m', '15m', '1h', '4h']
for tf in trame_list:
mainProcess(tag, tf)
# time.sleep(1)
def longRun():
while True:
try:
foreachList()
time.sleep(1)
except Exception as e:
print(mw.getTracebackInfo())
time.sleep(3)
def debug():
while True:
mainProcess('btc', '5m')
time.sleep(1)
if __name__ == "__main__":
func = sys.argv[1]
if func == 'long':
longRun()
elif func == 'run':
debug()
else:
print('error')
@@ -1,128 +0,0 @@
'''
pip install git+https://github.com/catalyst-team/catalyst@master --upgrade
cd /www/server/mdserver-web && python3 plugins/cryptocurrency_trade/ccxt/test/p1.py
cd /Users/midoks/Desktop/mwdev/server/mdserver-web && source bin/activate
python3 plugins/cryptocurrency_trade/ccxt/test/p1.py
'''
from datetime import datetime
import akshare as ak
import pandas as pd
import backtrader as bt
class BollStrategy(bt.Strategy): # BOLL策略程序
params = (("nk", 13), # 求均值的天数
('printlog', False),) # 打印log
def __init__(self): # 初始化
self.data_close = self.datas[0].close # 指定价格序列
# 初始化交易指令、买卖价格和手续费
self.order = None
self.buy_price = None
self.buy_comm = None
# Boll指标计算
self.top = bt.indicators.BollingerBands(
self.datas[0], period=self.params.nk).top
self.bot = bt.indicators.BollingerBands(
self.datas[0], period=self.params.nk).bot
# 添加移动均线指标
self.sma = bt.indicators.SimpleMovingAverage(
self.datas[0], period=self.params.nk)
def next(self): # 买卖策略
if self.order: # 检查是否有指令等待执行
return
# 检查是否持仓
"""
if not self.position: # 没有持仓
if self.data_close[0] > self.sma[0]: # 执行买入条件判断:收盘价格上涨突破20日均线
self.order = self.buy(size=100) # 执行买入
else:
if self.data_close[0] < self.sma[0]: # 执行卖出条件判断:收盘价格跌破20日均线
self.order = self.sell(size=100) # 执行卖出
"""
if not self.position: # 没有持仓
if self.data_close[0] < self.bot[0]: # 收盘价格跌破下轨
self.log("BUY CREATE, %.2f" % self.data_close[0])
self.order = self.buy() # 执行买入
else:
if self.data_close[0] > self.top[0]: # 收盘价格上涨突破上轨
self.log("SELL CREATE, %.2f" % self.data_close[0])
self.order = self.sell() # 执行卖出
def log(self, txt, dt=None, do_print=False): # 日志函数
if self.params.printlog or do_print:
dt = dt or self.datas[0].datetime.date(0)
print('%s, %s' % (dt.isoformat(), txt))
def notify_order(self, order): # 记录交易执行情况
# 如果order为submitted/accepted,返回空
if order.status in [order.Submitted, order.Accepted]:
return
# 指令为buy/sell,报告价格结果
if order.status in [order.Completed]:
if order.isbuy():
self.log(
f"买入:\n价格:{order.executed.price},\
成本:{order.executed.value},\
手续费:{order.executed.comm}"
)
self.buyprice = order.executed.price
self.buycomm = order.executed.comm
else:
self.log(
f"卖出:\n价格:{order.executed.price},\
成本: {order.executed.value},\
手续费{order.executed.comm}"
)
self.bar_executed = len(self)
elif order.status in [order.Canceled, order.Margin, order.Rejected]:
self.log("交易失败") # 指令取消/交易失败, 报告结果
self.order = None
def notify_trade(self, trade): # 记录交易收益情况
if not trade.isclosed:
return
self.log(f"策略收益:\n毛收益 {trade.pnl:.2f}, 净收益 {trade.pnlcomm:.2f}")
def stop(self): # 回测结束后输出结果
self.log("(BOLL线: %2d日) 期末总资金 %.2f" %
(self.params.nk, self.broker.getvalue()), do_print=True)
code = "600036" # 股票代码
start_cash = 1000000 # 初始自己为1000000
stake = 100 # 单次交易数量为1手
commfee = 0.0005 # 佣金为万5
sdate = '20210101' # 回测时间段
edate = '20220930'
cerebro = bt.Cerebro() # 创建回测系统实例
# 利用AKShare获取股票的前复权数据的前6列
df_qfq = ak.stock_zh_a_hist(
symbol=code, adjust="qfq", start_date=sdate, end_date=edate).iloc[:, :6]
# 处理字段命名,以符合Backtrader的要求
df_qfq.columns = ['date', 'open', 'close', 'high', 'low', 'volume', ]
# 把date作为日期索引,以符合Backtrader的要求
df_qfq.index = pd.to_datetime(df_qfq['date'])
start_date = datetime.strptime(sdate, "%Y%m%d") # 转换日期格式
end_date = datetime.strptime(edate, "%Y%m%d")
# start_date=datetime(2022,1,4)
# end_date=datetime(2022,9,16)
data = bt.feeds.PandasData(
dataname=df_qfq, fromdate=start_date, todate=end_date) # 规范化数据格式
cerebro.adddata(data) # 加载数据
cerebro.addstrategy(BollStrategy, nk=13, printlog=True) # 加载交易策略
cerebro.broker.setcash(start_cash) # broker设置资金
cerebro.broker.setcommission(commission=commfee) # broker手续费
cerebro.addsizer(bt.sizers.FixedSize, stake=stake) # 设置买入数量
print("期初总资金: %.2f" % start_cash)
cerebro.run() # 运行回测
end_value = cerebro.broker.getvalue() # 获取回测结束后的总资金
print("期末总资金: %.2f" % end_value)
cerebro.plot()
@@ -1,154 +0,0 @@
'''
pip install git+https://github.com/catalyst-team/catalyst@master --upgrade
cd /www/server/mdserver-web && python3 plugins/cryptocurrency_trade/ccxt/test/p1.py
cd /Users/midoks/Desktop/mwdev/server/mdserver-web && source bin/activate
python3 plugins/cryptocurrency_trade/ccxt/test/p1.py
'''
from datetime import datetime
import akshare as ak
import pandas as pd
import backtrader as bt
import sys
import os
sys.path.append(os.getcwd() + "/plugins/cryptocurrency_trade/ccxt/strategy")
import common
class BollStrategy(bt.Strategy): # BOLL策略程序
params = (("nk", 13), # 求均值的天数
('printlog', False),) # 打印log
def __init__(self): # 初始化
self.data_close = self.datas[0].close # 指定价格序列
# 初始化交易指令、买卖价格和手续费
self.order = None
self.buy_price = None
self.buy_comm = None
# Boll指标计算
self.top = bt.indicators.BollingerBands(
self.datas[0], period=self.params.nk).top
self.bot = bt.indicators.BollingerBands(
self.datas[0], period=self.params.nk).bot
# 添加移动均线指标
self.sma = bt.indicators.SimpleMovingAverage(
self.datas[0], period=self.params.nk)
def next(self): # 买卖策略
if self.order: # 检查是否有指令等待执行
return
# 检查是否持仓
"""
if not self.position: # 没有持仓
if self.data_close[0] > self.sma[0]: # 执行买入条件判断:收盘价格上涨突破20日均线
self.order = self.buy(size=100) # 执行买入
else:
if self.data_close[0] < self.sma[0]: # 执行卖出条件判断:收盘价格跌破20日均线
self.order = self.sell(size=100) # 执行卖出
"""
if not self.position: # 没有持仓
if self.data_close[0] < self.bot[0]: # 收盘价格跌破下轨
self.log("BUY CREATE, %.2f" % self.data_close[0])
self.order = self.buy() # 执行买入
else:
if self.data_close[0] > self.top[0]: # 收盘价格上涨突破上轨
self.log("SELL CREATE, %.2f" % self.data_close[0])
self.order = self.sell() # 执行卖出
def log(self, txt, dt=None, do_print=False): # 日志函数
if self.params.printlog or do_print:
dt = dt or self.datas[0].datetime.date(0)
print('%s, %s' % (dt.isoformat(), txt))
def notify_order(self, order): # 记录交易执行情况
# 如果order为submitted/accepted,返回空
if order.status in [order.Submitted, order.Accepted]:
return
# 指令为buy/sell,报告价格结果
if order.status in [order.Completed]:
if order.isbuy():
self.log(
f"买入:\n价格:{order.executed.price},\
成本:{order.executed.value},\
手续费:{order.executed.comm}"
)
self.buyprice = order.executed.price
self.buycomm = order.executed.comm
else:
self.log(
f"卖出:\n价格:{order.executed.price},\
成本: {order.executed.value},\
手续费{order.executed.comm}"
)
self.bar_executed = len(self)
elif order.status in [order.Canceled, order.Margin, order.Rejected]:
self.log("交易失败") # 指令取消/交易失败, 报告结果
self.order = None
def notify_trade(self, trade): # 记录交易收益情况
if not trade.isclosed:
return
self.log(f"策略收益:\n毛收益 {trade.pnl:.2f}, 净收益 {trade.pnlcomm:.2f}")
def stop(self): # 回测结束后输出结果
self.log("(BOLL线: %2d日) 期末总资金 %.2f" %
(self.params.nk, self.broker.getvalue()), do_print=True)
code = "600036" # 股票代码
start_cash = 1000000 # 初始自己为1000000
stake = 100 # 单次交易数量为1手
commfee = 0.0005 # 佣金为万5
sdate = '20210101' # 回测时间段
edate = '20220930'
cerebro = bt.Cerebro() # 创建回测系统实例
# 利用AKShare获取股票的前复权数据的前6列
# df_qfq = ak.stock_zh_a_hist(
# symbol=code, adjust="qfq", start_date=sdate, end_date=edate).iloc[:, :6]
# 日期 开盘 收盘 最高 最低 成交量
# 0 2021-01-04 40.46 40.40 41.01 39.25 1549523
# 1 2021-01-05 39.99 39.41 40.03 38.67 1387177
# 2 2021-01-06 39.33 41.38 41.43 39.23 1200646
# 3 2021-01-07 41.52 43.13 43.20 41.49 1078322
# 4 2021-01-08 43.52 43.83 44.25 43.04 1299595
# .. ... ... ... ... ... ...
# 420 2022-09-26 34.02 33.95 34.52 33.84 525491
# 421 2022-09-27 33.89 33.90 34.05 33.44 475414
# 422 2022-09-28 33.79 33.67 34.00 33.42 487299
# 423 2022-09-29 33.99 33.15 34.32 33.00 617859
# 424 2022-09-30 33.26 33.65 33.95 33.12 539717
tag = 'btc'
symbol = tag.upper() + '/USDT'
df_qfq = common.getDataFromDb_DF('15m', tag.lower())
print(df_qfq)
# # 处理字段命名,以符合Backtrader的要求
# df_qfq.columns = ['date', 'open', 'close', 'high', 'low', 'volume', ]
# # 把date作为日期索引,以符合Backtrader的要求
# df_qfq.index = pd.to_datetime(df_qfq['date'])
# start_date = datetime.strptime(sdate, "%Y%m%d") # 转换日期格式
# end_date = datetime.strptime(edate, "%Y%m%d")
start_date = datetime(2023, 4, 6)
end_date = datetime(2023, 4, 9)
data = bt.feeds.PandasData(dataname=df_qfq, fromdate=start_date,
todate=end_date) # 规范化数据格式
cerebro.adddata(data) # 加载数据
cerebro.addstrategy(BollStrategy, nk=13, printlog=True) # 加载交易策略
cerebro.broker.setcash(start_cash) # broker设置资金
cerebro.broker.setcommission(commission=commfee) # broker手续费
cerebro.addsizer(bt.sizers.FixedSize, stake=stake) # 设置买入数量
print("期初总资金: %.2f" % start_cash)
cerebro.run() # 运行回测
end_value = cerebro.broker.getvalue() # 获取回测结束后的总资金
print("期末总资金: %.2f" % end_value)
# cerebro.plot(style='candle')
@@ -1,61 +0,0 @@
# coding:utf-8
import pandas as pd
import matplotlib.pyplot as plt
from catalyst import run_algorithm
from catalyst.api import order, record, symbol
'''
cd /Users/midoks/Desktop/mwdev/server/mdserver-web && source bin/activate
cd /www/server/mdserver-web && source bin/activate && source activate catalys
cd /Users/midoks/Desktop/mwdev/server/mdserver-web && python3 plugins/cryptocurrency_trade/ccxt/test/p_test.py
catalyst ingest-exchange -x binance -i btc_usdt -f minute
cd /www/server/mdserver-web && python3 plugins/cryptocurrency_trade/ccxt/test/p_test.py
'''
def initialize(context):
# 初始化
context.asset = symbol('btc_usdt')
def handle_data(context, data):
# 循环策略
order(context.asset, 1)
record(btc=data.current(context.asset, 'price'))
def analyze(context, perf):
print(perf.portfolio_value)
ax1 = plt.subplot(211)
perf.portfolio_value.plot(ax=ax1)
ax1.set_ylabel('portfolio value')
ax2 = plt.subplot(212, sharex=ax1)
perf.btc.plot(ax=ax2)
ax2.set_ylabel('bitcoin value')
plt.show()
if __name__ == "__main__":
run_algorithm(
capital_base=10000,
data_frequency='daily',
initialize=initialize,
handle_data=handle_data,
analyze=analyze,
exchange_name='binance',
quote_currenty='usdt',
start=pd.to_datetime("2018-01-01", utc=True),
end=pd.to_datetime("2018-10-01", utc=True),
)
@@ -1,9 +0,0 @@
CREATE TABLE IF NOT EXISTS `ct_xx1_xx2` (
`addtime` BIGINT(20) not NULL,
`open` float NOT NULL,
`high` float NOT NULL,
`low` float NOT NULL,
`close` float NOT NULL,
`vol` float NOT NULL,
UNIQUE KEY `addtime` (`addtime`)
);
@@ -1,11 +0,0 @@
CREATE TABLE IF NOT EXISTS `ct_order_list` (
`id` BIGINT(20) not NULL,
'strategy_name' varchar(50) NULL,
`symbol` varchar(50) NOT NULL,
`fee` float NOT NULL,
`price` float NOT NULL,
`closing_price` float NOT NULL comment '',
`profit` float NOT NULL,
`source` TEXT,
`addtime` BIGINT(20) not NULL
);
@@ -1,14 +0,0 @@
[program:{$NAME}]
command=bash -c "cd {$RUN_ROOT} && source bin/activate && python3 {$ABS_FILE} long"
directory={$RUN_ROOT}
autorestart=true
startsecs=3
startretries=3
stdout_logfile={$SUP_ROOT}/log/{$NAME}.out.log
stderr_logfile={$SUP_ROOT}/log/{$NAME}.err.log
stdout_logfile_maxbytes=2MB
stderr_logfile_maxbytes=2MB
user=root
priority=999
numprocs=1
process_name=%(program_name)s
@@ -1,14 +0,0 @@
[program:{$NAME}]
command=bash -c "cd {$RUN_ROOT} && source bin/activate && python3 plugins/cryptocurrency_trade/ccxt/public_data/data.py long"
directory={$RUN_ROOT}
autorestart=true
startsecs=3
startretries=3
stdout_logfile={$SUP_ROOT}/log/{$NAME}.out.log
stderr_logfile={$SUP_ROOT}/log/{$NAME}.err.log
stdout_logfile_maxbytes=2MB
stderr_logfile_maxbytes=2MB
user=root
priority=999
numprocs=1
process_name=%(program_name)s
@@ -1,350 +0,0 @@
# coding:utf-8
import sys
import io
import os
import time
import re
import string
import subprocess
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 'cryptocurrency_trade'
def getPluginDir():
return mw.getPluginDir() + '/' + getPluginName()
def getServerDir():
return mw.getServerDir() + '/' + getPluginName()
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 getArgs():
args = sys.argv[2:]
tmp = {}
args_len = len(args)
if args_len == 1:
t = args[0].strip('{').strip('}')
if t.strip() == '':
tmp = []
else:
t = t.split(':', 1)
tmp[t[0]] = t[1]
tmp[t[0]] = t[1]
elif args_len > 1:
for i in range(len(args)):
t = args[i].split(':', 1)
tmp[t[0]] = t[1]
return tmp
def checkArgs(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 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 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 writeConf(data):
cfg_path = getServerDir() + "/data.cfg"
mw.writeFile(cfg_path, json.dumps(data))
return True
def getDbConf():
data = getConfigData()
if 'db' in data:
return mw.returnJson(True, 'ok', data['db'])
return mw.returnJson(False, 'ok', {})
def getUserConf():
data = getConfigData()
if 'user' in data:
return mw.returnJson(True, 'ok', data['user'])
return mw.returnJson(False, 'ok', {})
def restartSup():
cmd = 'python3 plugins/supervisor/index.py restart'
mw.execShell(cmd)
def restartSupDst(name):
cmd = 'python3 plugins/supervisor/index.py restart_job {"name":"' + \
name + '","status":"stop"}'
mw.execShell(cmd)
def syncDataAddTaskUninstall():
sup_path = mw.getServerDir() + '/supervisor'
if not os.path.exists(sup_path):
return mw.returnJson(False, '需要安装并启动supervisor插件')
name = "ct_task"
sup_task_dst = sup_path + '/conf.d/' + name + '.ini'
if os.path.exists(sup_task_dst):
mw.execShell('rm -rf ' + sup_task_dst)
restartSup()
return mw.returnJson(True, '删除同步数据任务成功!')
def syncDataAddTaskInstall():
sup_path = mw.getServerDir() + '/supervisor'
if not os.path.exists(sup_path):
return mw.returnJson(False, '需要安装并启动supervisor插件')
name = "ct_task"
sup_task_tpl = getPluginDir() + '/conf/sup_task.tpl'
sup_task_dst = sup_path + '/conf.d/' + name + '.ini'
content = mw.readFile(sup_task_tpl)
content = content.replace(
'{$RUN_ROOT}', mw.getServerDir() + '/mdserver-web')
content = content.replace(
'{$SUP_ROOT}', sup_path)
content = content.replace(
'{$NAME}', name)
mw.writeFile(sup_task_dst, content)
restartSup()
return mw.returnJson(True, '添加同步数据任务成功!')
def syncDataAddTask():
args = getArgs()
data_args = checkArgs(args, ['check'])
if not data_args[0]:
return data_args[1]
if args['check'] == "0":
return syncDataAddTaskUninstall()
return syncDataAddTaskInstall()
def syncDataDelete():
args = getArgs()
data_args = checkArgs(args, ['token'])
if not data_args[0]:
return data_args[1]
del_token = args['token']
data = getConfigData()
if 'token' in data:
data['token'].remove(del_token)
writeConf(data)
return mw.returnJson(True, '删除成功!')
# callback ---------------------------------- start
def get_datasource_logs(args):
log_file = getServerDir() + '/logs/datasource.log'
if not os.path.exists(log_file):
return '暂无日志'
data = mw.getLastLine(log_file, 10)
return data
def get_strategy_logs(args):
log_file = getServerDir() + '/logs/strategy.log'
if not os.path.exists(log_file):
return '暂无日志'
data = mw.getLastLine(log_file, 10)
return data
def save_body(args):
path = args['path']
encoding = args['encoding']
data = args['data']
tag = args['tag']
if not os.path.exists(path):
return mw.returnData(False, '文件不存在')
try:
if encoding == 'ascii':
encoding = 'utf-8'
data = data.encode(
encoding, errors='ignore').decode(encoding)
fp = open(path, 'w+', encoding=encoding)
fp.write(data)
fp.close()
set_strategy_restart({'id': tag})
return mw.returnData(True, '文件保存成功')
except Exception as ex:
return mw.returnData(False, '文件保存错误:' + str(ex))
def get_strategy_path(args):
abs_id = args['id']
name = "ct_strategy_" + abs_id
abs_file = get_strategy_absfile(abs_id)
return mw.returnData(True, abs_file)
def set_strategy_restart(args):
sup_path = mw.getServerDir() + '/supervisor'
if not os.path.exists(sup_path):
return mw.returnData(False, '需要安装并启动supervisor插件')
abs_id = args['id']
name = "ct_strategy_" + abs_id
sup_strategy_dst = sup_path + '/conf.d/' + name + '.ini'
if not os.path.exists(sup_strategy_dst):
return mw.returnData(False, '策略任务' + abs_id + '未添加!')
restartSupDst(name)
return mw.returnData(True, '重启策略任务' + abs_id + '成功!')
def set_strategy_status(args):
sup_path = mw.getServerDir() + '/supervisor'
if not os.path.exists(sup_path):
return mw.returnData(False, '需要安装并启动supervisor插件')
abs_id = args['id']
name = "ct_strategy_" + abs_id
sup_strategy_dst = sup_path + '/conf.d/' + name + '.ini'
if args['status'] == 'stop':
if os.path.exists(sup_strategy_dst):
os.remove(sup_strategy_dst)
restartSup()
return mw.returnData(True, '删除策略任务' + abs_id + '成功!')
abs_file = get_strategy_absfile(abs_id)
sup_strategy_tpl = getPluginDir() + '/conf/sup_strategy.tpl'
content = mw.readFile(sup_strategy_tpl)
content = content.replace(
'{$RUN_ROOT}', mw.getServerDir() + '/mdserver-web')
content = content.replace(
'{$SUP_ROOT}', sup_path)
content = content.replace(
'{$NAME}', name)
content = content.replace(
'{$ABS_FILE}', abs_file)
mw.writeFile(sup_strategy_dst, content)
restartSup()
return mw.returnData(True, '添加策略任务' + abs_id + '成功!')
def get_strategy_absfile(abs_id):
info = getPluginDir() + '/ccxt/strategy/info.json'
info = json.loads(mw.readFile(info))
path = getPluginDir() + '/ccxt/strategy'
for x in range(len(info)):
if info[x]['id'] == abs_id:
return path + '/' + info[x]['file']
return path + '/abs.py'
def get_strategy_list(args):
info = getPluginDir() + '/ccxt/strategy/info.json'
info = json.loads(mw.readFile(info))
st_path = mw.getServerDir() + '/supervisor/conf.d'
page = 1
page_size = 5
search = ''
data = {}
if 'page' in args:
page = int(args['page'])
if 'page_size' in args:
page_size = int(args['page_size'])
dlist_sum = len(info)
page_start = int((page - 1) * page_size)
page_end = page_start + page_size
if page_end >= dlist_sum:
ret_data = info[page_start:]
else:
ret_data = info[page_start:page_end]
for x in range(len(ret_data)):
strategy_dst = st_path + '/ct_strategy_' + ret_data[x]['id'] + '.ini'
if os.path.exists(strategy_dst):
ret_data[x]['status'] = 'start'
else:
ret_data[x]['status'] = 'stop'
data['data'] = ret_data
data['args'] = args
data['list'] = mw.getPage(
{'count': dlist_sum, 'p': page, 'row': page_size, 'tojs': 'getStrategyList'})
return data
# callback ---------------------------------- end
Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.1 KiB

-26
View File
@@ -1,26 +0,0 @@
<style type="text/css">
.conf_p span{width: 100px;}
.code{padding: 20px 5px;border: 1px solid #e1e1e1;background: #fcfcfc;border-radius: 4px;line-height: 24px;}
.code span {display: block;margin-left: 15px;margin-bottom: 0;}
</style>
<div class="bt-form">
<div class="bt-w-main">
<div class="bt-w-menu">
<p class="bgw" onclick="pluginService('cryptocurrency_trade');">服务</p>
<p onclick="dbConf();">数据库配置</p>
<p onclick="syncDataList();">同步数据配置</p>
<p onclick="userConf();">账户配置</p>
</div>
<div class="bt-w-con pd15">
<div class="soft-man-con"></div>
</div>
</div>
</div>
<script type="text/javascript">
// resetPluginWinWidth(480);
resetPluginWinHeight(400);
$.getScript( "/plugins/file?name=cryptocurrency_trade&f=static/js/cryptocurrency_trade.js",function(){
pluginService('cryptocurrency_trade');
});
</script>
-347
View File
@@ -1,347 +0,0 @@
# coding:utf-8
import sys
import io
import os
import time
import re
import string
import subprocess
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 'cryptocurrency_trade'
def getPluginDir():
return mw.getPluginDir() + '/' + getPluginName()
def getServerDir():
return mw.getServerDir() + '/' + getPluginName()
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 getArgs():
args = sys.argv[2:]
tmp = {}
args_len = len(args)
if args_len == 1:
t = args[0].strip('{').strip('}')
if t.strip() == '':
tmp = []
else:
t = t.split(':', 1)
tmp[t[0]] = t[1]
tmp[t[0]] = t[1]
elif args_len > 1:
for i in range(len(args)):
t = args[i].split(':', 1)
tmp[t[0]] = t[1]
return tmp
def checkArgs(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 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 initDreplace():
log_dir = getServerDir() + '/logs'
if not os.path.exists(log_dir):
d = mw.execShell('mkdir -p ' + log_dir)
data = getConfigData()
data['token'] = ['btc']
writeConf(data)
return True
def status():
initDreplace()
return 'start'
def start():
syncDataAddTaskInstall()
return 'ok'
def stop():
syncDataAddTaskUninstall()
return 'ok'
def op():
return 'ok'
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 writeConf(data):
cfg_path = getServerDir() + "/data.cfg"
mw.writeFile(cfg_path, json.dumps(data))
return True
def getDbConf():
data = getConfigData()
if 'db' in data:
return mw.returnJson(True, 'ok', data['db'])
return mw.returnJson(False, 'ok', {})
def setDbConf():
args = getArgs()
data_args = checkArgs(args, ['db_host', 'db_port',
'db_name', 'db_user', 'db_pass'])
if not data_args[0]:
return data_args[1]
data = getConfigData()
data['db'] = args
writeConf(data)
db = mw.getMyORM()
db.setHost(args['db_host'])
db.setPort(args['db_port'])
db.setUser(args['db_user'])
db.setPwd(args['db_pass'])
testdata = db.query('select version()')
isError = isSqlError(testdata)
if isError != None:
return isError
return mw.returnJson(True, '保存成功,并连通成功!', [])
def getUserConf():
data = getConfigData()
if 'user' in data:
return mw.returnJson(True, 'ok', data['user'])
return mw.returnJson(False, 'ok', {})
def getUserConf():
data = getConfigData()
if 'user' in data:
try:
udata = mw.deDoubleCrypt('mw', data['user'])
udata = json.loads(udata)
return mw.returnJson(True, 'ok', udata)
except Exception as e:
pass
return mw.returnJson(False, 'ok', {})
def setUserConf():
args = getArgs()
data_args = checkArgs(args, ['app_key', 'secret',
'password', 'uid', 'exchange'])
if not data_args[0]:
return data_args[1]
data = getConfigData()
data['user'] = mw.enDoubleCrypt('mw', json.dumps(args))
writeConf(data)
return mw.returnJson(True, '保存成功!', [])
def syncDataList():
data = getConfigData()
name = "ct_task"
if 'token' in data:
rdata = {}
rdata['task_status'] = False
sup_path = mw.getServerDir() + '/supervisor'
sup_task_dst = sup_path + '/conf.d/' + name + '.ini'
if os.path.exists(sup_task_dst):
rdata['task_status'] = True
rdata['list'] = data['token']
return mw.returnJson(True, 'ok', rdata)
return mw.returnJson(False, 'ok')
def syncDataAdd():
args = getArgs()
data_args = checkArgs(args, ['token'])
if not data_args[0]:
return data_args[1]
add_token = args['token']
data = getConfigData()
if 'token' in data and data['token']:
if not add_token in data['token']:
data['token'].append(add_token)
else:
data['token'] = [add_token]
writeConf(data)
return mw.returnJson(True, '保存成功!')
def restartSup():
cmd = 'python3 plugins/supervisor/index.py restart'
mw.execShell(cmd)
def restartSupDst(name):
cmd = 'python3 plugins/supervisor/index.py restart_job {"name":"' + \
name + '","status":"stop"}'
mw.execShell(cmd)
def syncDataAddTaskUninstall():
sup_path = mw.getServerDir() + '/supervisor'
if not os.path.exists(sup_path):
return mw.returnJson(False, '需要安装并启动supervisor插件')
name = "ct_task"
sup_task_dst = sup_path + '/conf.d/' + name + '.ini'
if os.path.exists(sup_task_dst):
mw.execShell('rm -rf ' + sup_task_dst)
restartSup()
return mw.returnJson(True, '删除同步数据任务成功!')
def syncDataAddTaskInstall():
sup_path = mw.getServerDir() + '/supervisor'
if not os.path.exists(sup_path):
return mw.returnJson(False, '需要安装并启动supervisor插件')
name = "ct_task"
sup_task_tpl = getPluginDir() + '/conf/sup_task.tpl'
sup_task_dst = sup_path + '/conf.d/' + name + '.ini'
content = mw.readFile(sup_task_tpl)
content = content.replace(
'{$RUN_ROOT}', mw.getServerDir() + '/mdserver-web')
content = content.replace(
'{$SUP_ROOT}', sup_path)
content = content.replace(
'{$NAME}', name)
mw.writeFile(sup_task_dst, content)
restartSup()
return mw.returnJson(True, '添加同步数据任务成功!')
def syncDataAddTask():
args = getArgs()
data_args = checkArgs(args, ['check'])
if not data_args[0]:
return data_args[1]
if args['check'] == "0":
return syncDataAddTaskUninstall()
return syncDataAddTaskInstall()
def syncDataDelete():
args = getArgs()
data_args = checkArgs(args, ['token'])
if not data_args[0]:
return data_args[1]
del_token = args['token']
data = getConfigData()
if 'token' in data:
data['token'].remove(del_token)
writeConf(data)
return mw.returnJson(True, '删除成功!')
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(op())
elif func == 'reload':
print(op())
elif func == 'get_db_conf':
print(getDbConf())
elif func == 'set_db_conf':
print(setDbConf())
elif func == 'get_user_conf':
print(getUserConf())
elif func == 'set_user_conf':
print(setUserConf())
elif func == 'sync_data_list':
print(syncDataList())
elif func == 'sync_data_add':
print(syncDataAdd())
elif func == 'sync_data_delete':
print(syncDataDelete())
elif func == 'sync_data_add_task':
print(syncDataAddTask())
else:
print('error')
-36
View File
@@ -1,36 +0,0 @@
{
"hook":[
{
"tag":"menu",
"menu": {
"title":"量化交易",
"name":"cryptocurrency_trade",
"path":"static/html/index.html",
"css_path":"static/css/cryptocurrency_trade.css",
"js_path":"static/js/cryptocurrency_trade.js"
}
},
{
"tag":"global_static",
"global_static": {
"title":"量化交易",
"name":"cryptocurrency_trade",
"css_path":"static/css/ico.css"
}
}
],
"ps": "基于CCXT的数字货币量化交易插件",
"name": "cryptocurrency_trade",
"title": "量化交易",
"versions": ["1.0"],
"tip": "soft",
"checks": "server/cryptocurrency_trade",
"path":"server/cryptocurrency_trade",
"author": "midoks",
"date": "2022-02-25",
"home": "https://github.com/ccxt/ccxt",
"type": "量化交易",
"shell": "install.sh",
"pid": "5",
"sort": 7
}
-63
View File
@@ -1,63 +0,0 @@
#!/bin/bash
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH
# cd /www/server/mdserver-web/plugins/cryptocurrency_trade && bash install.sh install
curPath=`pwd`
rootPath=$(dirname "$curPath")
rootPath=$(dirname "$rootPath")
serverPath=$(dirname "$rootPath")
VERSION=$2
# pip3 install ccxt
if [ -f ${rootPath}/bin/activate ];then
source ${rootPath}/bin/activate
fi
pip3 install ccxt
pip3 install pandas
pip3 install pandas_ta
pip3 install pyTelegramBotAPI
pip3 install catalyst
pip3 install matplotlib==3.2.2
Install_App()
{
echo '正在安装脚本文件...'
mkdir -p $serverPath/source/cryptocurrency_trade
mkdir -p $serverPath/cryptocurrency_trade
echo "${VERSION}" > $serverPath/cryptocurrency_trade/version.pl
if [ ! -f $serverPath/source/cryptocurrency_trade/ta-lib-0.4.0-src.tar.gz ];then
wget -O $serverPath/source/cryptocurrency_trade/ta-lib-0.4.0-src.tar.gz https://sourceforge.net/projects/ta-lib/files/ta-lib/0.4.0/ta-lib-0.4.0-src.tar.gz
fi
if [ ! -d $serverPath/source/cryptocurrency_trade/ta-lib ];then
cd $serverPath/source/cryptocurrency_trade/ta-lib && tar -xzf ta-lib-0.4.0-src.tar.gz
cd ta-lib && ./configure --prefix=/usr && make && make install
rm -rf $serverPath/source/cryptocurrency_trade/ta-lib
pip3 install ta-lib
fi
cd ${rootPath} && python3 ${rootPath}/plugins/cryptocurrency_trade/index.py start
echo '安装完成'
}
Uninstall_App()
{
rm -rf $serverPath/cryptocurrency_trade
cd ${rootPath} && python3 ${rootPath}/plugins/cryptocurrency_trade/index.py stop
echo "卸载完成"
}
action=$1
if [ "${1}" == 'install' ];then
Install_App
else
Uninstall_App
fi
@@ -1,11 +0,0 @@
.screen-all {
padding: 5px;
}
.s-right .panel-default{
margin-bottom: 10px;
}
.s-left .panel-default{
margin-bottom: 10px;
}
@@ -1,11 +0,0 @@
/* menu start */
.menu .current .menu_plugin_cryptocurrency_trade:hover {
background-image: url("/plugins/file?name=cryptocurrency_trade&f=ico.png");
}
.menu .menu_plugin_cryptocurrency_trade {
background-image: url("/plugins/file?name=cryptocurrency_trade&f=ico.png");
}
/* menu end */
@@ -1,65 +0,0 @@
<div class="main-content">
<div class="screen-all">
<div class="content-screen safe bgw mtb15 ptb10 radius4 row" style="height: 300px;">
<!-- start -->
<div class="s-left col-md-8" style="padding-left: 2.5px;padding-right: 5px; overflow-y: scroll;height: 400px;">
<div class="panel panel-default">
<div class="panel-heading">订单数据</div>
<div class="panel-body">Panel content</div>
</div>
<div class="panel panel-default">
<div class="panel-heading">交易图表</div>
<div class="panel-body" style="padding: 0px;">
<div></div>
<div id="k_echarts" style="width: auto;height:400px;"></div>
</div>
</div>
<div class="panel panel-default">
<div class="panel-heading">策略交易日志</div>
<div class="panel-body" style="padding: 0px;">
<pre id="strategy_log" style="border:none;font-size:12px;white-space: pre;margin: 0px;width: auto;height: 200px;background-color: #333;color:#fff; padding:0 5px">当前没有日志.</pre>
</div>
</div>
</div>
<div class="s-right col-md-4" style="padding-left: 2.5px;padding-right: 5px; overflow-y: scroll;height: 400px;">
<div class="panel panel-default">
<div class="panel-heading">策略配置</div>
<div class="panel-body divtable pd15 relative" style="padding: 2px 2px 5px;">
<table class="table table-hover" width="100%" cellspacing="0" cellpadding="0" border="0">
<thead>
<tr>
<th width="20">序列</th>
<th width="120">说明</th>
<th width="10">状态</th>
<th style="text-align: right;" width="50">操作</th>
</tr>
</thead>
<tbody id="strategy_list"></tbody>
</table>
<div class="dataTables_paginate paging_bootstrap pagination">
<ul id="strategy_list_page" class="page"></ul>
</div>
</div>
</div>
<div class="panel panel-default">
<div class="panel-heading">数据源日志</div>
<div class="panel-body" style="padding: 0px;">
<pre id="datasource_log" style="border:none;font-size:12px;white-space: pre;margin: 0px;width: auto;height: 200px;background-color: #333;color:#fff; padding:0 5px">当前没有日志.</pre>
</div>
</div>
</div>
<!-- end -->
</div>
</div>
</div>
@@ -1,964 +0,0 @@
function appPost(method,args,callback, title){
var _args = null;
if (typeof(args) == 'string'){
_args = JSON.stringify(toArrayObject(args));
} else {
_args = JSON.stringify(args);
}
var _title = '正在获取...';
if (typeof(title) != 'undefined'){
_title = title;
}
var loadT = layer.msg(_title, { icon: 16, time: 0, shade: 0.3 });
$.post('/plugins/run', {name:'cryptocurrency_trade', 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 appPostN(method,args,callback, title){
var _args = null;
if (typeof(args) == 'string'){
_args = JSON.stringify(toArrayObject(args));
} else {
_args = JSON.stringify(args);
}
var _title = '正在获取...';
if (typeof(title) != 'undefined'){
_title = title;
}
$.post('/plugins/run', {name:'cryptocurrency_trade', func:method, args:_args}, function(data) {
if(typeof(callback) == 'function'){
callback(data);
}
},'json');
}
function appAsyncPost(method,args){
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 });
return syncPost('/plugins/run', {name:'cryptocurrency_trade', func:method, args:_args});
}
function appPostCallbak(method, args,callback, script){
var loadT = layer.msg('正在获取...', { icon: 16, time: 0, shade: 0.3 });
var req_data = {};
req_data['name'] = 'cryptocurrency_trade';
req_data['func'] = method;
if (typeof(script) != 'undefined'){
req_data['script'] = script;
}
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 appPostCallbakNoMsg(method, args,callback, script){
var req_data = {};
req_data['name'] = 'cryptocurrency_trade';
req_data['func'] = method;
if (typeof(script) != 'undefined'){
req_data['script'] = script;
} else {
req_data['script'] = req_data['name'];
}
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) {
if (!data.status){
layer.msg(data.msg,{icon:0,time:2000,shade: [0.3, '#000']});
return;
}
if(typeof(callback) == 'function'){
callback(data);
}
},'json');
}
function dbConf(){
appPost('get_db_conf','',function(data){
var rdata = $.parseJSON(data.data);
// console.log(rdata);
var db_host = '127.0.0.1';
var db_port = '3306';
var db_name = 'cryptocurrency_trade';
var db_user = 'cryptocurrency_trade';
var db_pass = 'cryptocurrency_trade';
if(rdata['status']){
db_data = rdata['data'];
db_host = db_data['db_host'];
db_port = db_data['db_port'];
db_name = db_data['db_name'];
db_user = db_data['db_user'];
db_pass = db_data['db_pass'];
}
var mlist = '';
mlist += '<p><span>数据库地址</span><input style="width: 250px;" class="bt-input-text mr5" name="db_host" value="'+db_host+'" type="text"></p>'
mlist += '<p><span>数据库端口</span><input style="width: 250px;" class="bt-input-text mr5" name="db_port" value="'+db_port+'" type="text"></p>'
mlist += '<p><span>数据库名称</span><input style="width: 250px;" class="bt-input-text mr5" name="db_name" value="'+db_name+'" type="text"></p>'
mlist += '<p><span>用户名</span><input style="width: 250px;" class="bt-input-text mr5" name="db_user" value="'+db_user+'" type="text"></p>'
mlist += '<p><span>密码</span><input style="width: 250px;" class="bt-input-text mr5" name="db_pass" value="'+db_pass+'" type="text"></p>'
var option = '<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" onclick="submitDbConf()">保存</button>\
</div>\
</div>';
$(".soft-man-con").html(option);
});
}
function submitDbConf(){
var pull_data = {};
pull_data['db_host'] = $('input[name="db_host"]').val();
pull_data['db_port'] = $('input[name="db_port"]').val();
pull_data['db_name'] = $('input[name="db_name"]').val();
pull_data['db_user'] = $('input[name="db_user"]').val();
pull_data['db_pass'] = $('input[name="db_pass"]').val();
appPost('set_db_conf',pull_data,function(data){
var rdata = $.parseJSON(data.data);
layer.msg(rdata['msg'],{icon:rdata['status']?1:2,time:2000,shade: [0.3, '#000']});
});
}
function userConf(){
appPost('get_user_conf','',function(data){
var rdata = $.parseJSON(data.data);
var app_key = 'app_key';
var secret = 'secret';
var password = 'password';
var uid = 'uid';
var exchange = 'okex';
if(rdata['status']){
db_data = rdata['data'];
app_key = db_data['app_key'];
secret = db_data['secret'];
password = db_data['password'];
uid = db_data['uid'];
exchange = db_data['exchange'];;
}
var mlist = '';
mlist += '<p><span>交易所</span><input style="width: 250px;" class="bt-input-text mr5" name="exchange" value="'+exchange+'" type="text"><font>必填写[okex, binance]</font></p>'
mlist += '<p><span>apiKey</span><input style="width: 250px;" class="bt-input-text mr5" name="app_key" value="'+app_key+'" type="text"><font>必填写</font></p>'
mlist += '<p><span>secret</span><input style="width: 250px;" class="bt-input-text mr5" name="secret" value="'+secret+'" type="text"><font>必填写</font></p>'
mlist += '<p><span>password</span><input style="width: 250px;" class="bt-input-text mr5" name="password" value="'+password+'" type="text"><font>根据情况填写</font></p>'
mlist += '<p><span>uid</span><input style="width: 250px;" class="bt-input-text mr5" name="uid" value="'+uid+'" type="text"><font>根据情况填写</font></p>'
var option = '<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" onclick="submitUserConf()">保存</button>\
</div>\
</div>';
$(".soft-man-con").html(option);
});
}
function submitUserConf(){
var pull_data = {};
pull_data['app_key'] = $('input[name="app_key"]').val();
pull_data['secret'] = $('input[name="secret"]').val();
pull_data['password'] = $('input[name="password"]').val();
pull_data['uid'] = $('input[name="uid"]').val();
pull_data['exchange'] = $('input[name="exchange"]').val();
appPost('set_user_conf',pull_data,function(data){
var rdata = $.parseJSON(data.data);
layer.msg(rdata['msg'],{icon:rdata['status']?1:2,time:2000,shade: [0.3, '#000']});
});
}
function syncDataList(){
appPost('sync_data_list', {}, function(data){
var rdata = $.parseJSON(data.data);
var list = '';
if (rdata['status']){
var dlist = rdata['data']['list'];
for(i in dlist){
list += '<tr>';
list += '<td>' + dlist[i] +'</td>';
list += '<td style="text-align:right">' +
'<a href="javascript:;" class="btlink" onclick="syncDataDelete(\''+dlist[i]+'\')" title="删除">删除</a>' +
'</td>';
list += '</tr>';
}
}
if( list == '' ){
list = "<tr><td colspan='2'>当前没有数据</td></tr>";
}
var task_status = rdata['data']['task_status'];
var task_status_check = '';
if (task_status){
task_status_check = 'checked';
}
var con = '<div class="safe bgw">\
<div>\
<button onclick="syncDataAdd()" title="添加币种" class="btn btn-success btn-sm" type="button" style="margin-right: 5px;">添加币种</button>\
<div class="ss-text pull-left mr50">\
<em>是否启动</em>\
<div class="ssh-item">\
<input class="btswitch btswitch-ios" id="add_task" type="checkbox" '+task_status_check+'>\
<label class="btswitch-btn" for="add_task" onclick="syncDataAddTask()"></label>\
</div>\
</div>\
</div>\
<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>\
<th>名称</th>\
<th style="text-align:right;">操作</th></tr>\
</thead>\
<tbody>'+ list +'</tbody>\
</table>\
</div>\
</div>\
</div>';
con += '<div class="code">\
<span>详细如下:</span>\
<span>*:添加同步的币种,都小写,以USDT为本币同步数据。</span>\
<span>*:需要提前安装supervisor插件。</span>\
</div>'
$(".soft-man-con").html(con);
$('#databasePage').html(rdata.page);
});
}
function syncDataAddTask(){
var at_check = $('#add_task').prop('checked');
appPost("sync_data_add_task", {'check':at_check?'0':'1'}, function(data){
rdata = $.parseJSON(data.data);
showMsg(rdata.msg,function(){
if (rdata.status){
syncDataList();
}
},{icon:rdata.status?1:2});
});
}
function syncDataDelete(name){
appPost("sync_data_delete", {"token":name}, function(data){
rdata = $.parseJSON(data.data);
showMsg(rdata.msg,function(){
if (rdata.status){
syncDataList();
}
},{icon:rdata.status?1:2});
});
}
function syncDataAdd() {
layer.open({
type: 1,
area: '500px',
title: '添加同步数据',
closeBtn: 2,
shift: 0,
shadeClose: false,
btn: ['确定', '取消'],
content: "<div class='bt_conter bt-form pd15' style='height:auto;width:100%;'>\
<div class='line'>\
<span class='tname'>名称</span>\
<div class='info-r c4'>\
<input id='name' class='bt-input-text' type='text' name='name' placeholder='请输入名称' style='width:270px' />\
</div>\
</div>\
<ul class='help-info-text c7' style='padding-left: 29px;margin-top:5px;'>\
<li style='color:#F00'>注意:币种名称用小写!</li>\
</ul>\
</div>",
yes: function(index, layero){
var token = $('input[name="name"]').val();
appPost("sync_data_add", {"token":token}, function(data){
rdata = $.parseJSON(data.data);
showMsg(rdata.msg,function(){
if (rdata.status){
layer.close(index);
syncDataList();
}
},{icon:rdata.status?1:2});
})
return;
}
});
}
function onlineEditStrategyFile(k, f, tag) {
if(k != 0) {
var l = $("#PathPlace input").val();
var h = $("#textBody").val();
var a = $("select[name=encoding]").val();
var loadT = layer.msg("正在保存中...", {icon: 16,time: 0});
appPostCallbakNoMsg('save_body',{'data':h,'path':f,'encoding':a,"tag":tag}, function(data){
var rdata = data.data;
showMsg(rdata.msg, function(){
if (rdata.status){
layer.close(loadT);
}
},{icon: rdata.status ? 1 : 2});
});
return
}
var e = layer.msg("正在读取文件,请稍候...", {icon: 16,time: 0});
var g = f.split(".");
var b = g[g.length - 1];
var d;
switch(b) {
case "html":
var j = {
name: "htmlmixed",
scriptTypes: [{
matches: /\/x-handlebars-template|\/x-mustache/i,
mode: null
}, {
matches: /(text|application)\/(x-)?vb(a|script)/i,
mode: "vbscript"
}]
};
d = j;
break;
case "htm":
var j = {
name: "htmlmixed",
scriptTypes: [{
matches: /\/x-handlebars-template|\/x-mustache/i,
mode: null
}, {
matches: /(text|application)\/(x-)?vb(a|script)/i,
mode: "vbscript"
}]
};
d = j;
break;
case "js":
d = "text/javascript";
break;
case "json":
d = "application/ld+json";
break;
case "css":
d = "text/css";
break;
case "php":
d = "application/x-httpd-php";
break;
case "tpl":
d = "application/x-httpd-php";
break;
case "xml":
d = "application/xml";
break;
case "sql":
d = "text/x-sql";
break;
case "conf":
d = "text/x-nginx-conf";
break;
default:
var j = {
name: "htmlmixed",
scriptTypes: [{
matches: /\/x-handlebars-template|\/x-mustache/i,
mode: null
}, {
matches: /(text|application)\/(x-)?vb(a|script)/i,
mode: "vbscript"
}]
};
d = j
}
$.post("/files/get_body", "path=" + encodeURIComponent(f), function(s) {
if(s.status === false){
layer.msg(s.msg,{icon:5});
return;
}
layer.close(e);
var u = ["utf-8", "GBK", "GB2312", "BIG5"];
var n = "";
var m = "";
var o = "";
for(var p = 0; p < u.length; p++) {
m = s.data.encoding == u[p] ? "selected" : "";
n += '<option value="' + u[p] + '" ' + m + ">" + u[p] + "</option>";
}
var code_mirror = null;
var r = layer.open({
type: 1,
shift: 5,
closeBtn: 1,
area: ["90%", "90%"],
title: "在线编辑[" + f + "]",
btn:['保存','关闭'],
content: '<form class="bt-form pd20">\
<div class="line">\
<p style="color:red;margin-bottom:10px">提示:Ctrl+F 搜索关键字,Ctrl+G 查找下一个,Ctrl+S 保存,Ctrl+Shift+R 查找替换!\
<select class="bt-input-text" name="encoding" style="width: 74px;position: absolute;top: 31px;right: 19px;height: 22px;z-index: 9999;border-radius: 0;">' + n + '</select>\
</p>\
<textarea class="mCustomScrollbar bt-input-text" id="textBody" style="width:100%;margin:0 auto;line-height: 1.8;position: relative;top: 10px;" value="" />\
</div>\
</form>',
success:function(){
$("#textBody").text(s.data.data);
var q = $(window).height() * 0.9;
$("#textBody").height(q - 160);
code_mirror = CodeMirror.fromTextArea(document.getElementById("textBody"), {
extraKeys: {
"Ctrl-F": "findPersistent",
"Ctrl-H": "replaceAll",
"Ctrl-S": function() {
$("#textBody").text(code_mirror.getValue());
onlineEditStrategyFile(2, f,tag);
},
"Cmd-S":function() {
$("#textBody").text(code_mirror.getValue());
onlineEditStrategyFile(2, f,tag);
},
},
mode: d,
lineNumbers: true,
matchBrackets: true,
matchtags: true,
autoMatchParens: true
});
code_mirror.focus();
code_mirror.setSize("auto", q - 150);
$(window).resize(function(){
var q = $(window).height() * 0.9;
code_mirror.setSize("auto", q - 150);
});
},
yes:function(){
$("#textBody").text(code_mirror.getValue());
onlineEditStrategyFile(1, f, tag);
}
});
//////////////////
},'json');
}
// ------------------------------------------------------------------------------
// ------------------------------------------------------------------------------
// 大屏页功能 --------------------------------------------------------------------
// ------------------------------------------------------------------------------
// ------------------------------------------------------------------------------
function changeDivH(){
var l = $(window).height();
$('.content-screen').css('height',l-80);
$('.s-right').css('height',l-80-10);
$('.s-left').css('height',l-80-10);
}
function dataSourceLog(){
appPostCallbakNoMsg('get_datasource_logs',{}, function(rdata){
$('#datasource_log').html(rdata.data);
});
}
function dataStrategyLog(){
appPostCallbakNoMsg('get_strategy_logs',{}, function(rdata){
$('#strategy_log').html(rdata.data);
});
}
// $('#strategy_list').find('span[data-id="01"]')
function setStrategyStatus(id,status){
appPostCallbakNoMsg('set_strategy_status',{"id":id,"status":status}, function(data){
var rdata = data.data;
showMsg(rdata.msg,function(){
if (rdata.status){
if (status == 'start'){
$('#strategy_list').find('tr[data-id="'+id+'"] td span').removeClass('glyphicon-pause').addClass('glyphicon-play').css('color','#20a53a');
} else{
$('#strategy_list').find('tr[data-id="'+id+'"] td span').removeClass('glyphicon-play').addClass('glyphicon-pause').css('color','red');
}
}
},{icon:rdata.status?1:2},2000);
});
}
function setStrategyRestart(id){
appPostCallbakNoMsg('set_strategy_restart',{"id":id}, function(data){
console.log(data);
var rdata = data.data;
showMsg(rdata.msg,function(){
if (rdata.status){
}
},{icon:rdata.status?1:2},2000);
});
}
function setStrategyEdit(id){
appPostCallbakNoMsg('get_strategy_path',{"id":id}, function(data){
onlineEditStrategyFile(0,data.data.msg,id);
});
}
function getStrategyList(p=1){
appPostCallbakNoMsg('get_strategy_list',{'page':p}, function(rdata){
// console.log(rdata);
ldata = rdata.data.data;
var tBody = '';
for (var i = 0; i < ldata.length; i++) {
tBody += '<tr data-id="'+ldata[i]['id']+'">'
tBody += '<td>'+ldata[i]['id']+'</td>';
tBody += '<td>'+ldata[i]['name']+'</td>';
if (ldata[i]['status'] == 'start'){
tBody += '<td><span style="color:#20a53a;cursor: pointer;" class="strategy_status glyphicon glyphicon-play"></span></td>';
} else{
tBody += '<td><span style="color:red;cursor: pointer;" class="strategy_status glyphicon glyphicon-pause"></span></td>';
}
tBody += "<td style='text-align: right;'><a class='btlink restart'>重启</a> | <a class='btlink edit'>编辑</a></td>";
tBody +='<tr>';
}
// console.log(tBody);
$('#strategy_list').html(tBody);
$('#strategy_list_page').html(rdata.data.list);
$('#strategy_list .strategy_status').click(function(){
var id = $(this).parent().parent().data('id');
var status = 'stop';
if ($(this).hasClass('glyphicon-pause')){
status = 'start';
}
setStrategyStatus(id,status);
});
$('#strategy_list .restart').click(function(){
var id = $(this).parent().parent().data('id');
setStrategyRestart(id);
});
$('#strategy_list .edit').click(function(){
var id = $(this).parent().parent().data('id');
setStrategyEdit(id);
});
});
}
function calcKLineChats(){
var chartDom = document.getElementById('k_echarts');
var myChart = echarts.init(chartDom);
var option;
const upColor = '#ec0000';
const upBorderColor = '#8A0000';
const downColor = '#00da3c';
const downBorderColor = '#008F28';
// Each item: open,close,lowest,highest
const data0 = splitData([
['2013/1/24', 2320.26, 2320.26, 2287.3, 2362.94],
['2013/1/25', 2300, 2291.3, 2288.26, 2308.38],
['2013/1/28', 2295.35, 2346.5, 2295.35, 2346.92],
['2013/1/29', 2347.22, 2358.98, 2337.35, 2363.8],
['2013/1/30', 2360.75, 2382.48, 2347.89, 2383.76],
['2013/1/31', 2383.43, 2385.42, 2371.23, 2391.82],
['2013/2/1', 2377.41, 2419.02, 2369.57, 2421.15],
['2013/2/4', 2425.92, 2428.15, 2417.58, 2440.38],
['2013/2/5', 2411, 2433.13, 2403.3, 2437.42],
['2013/2/6', 2432.68, 2434.48, 2427.7, 2441.73],
['2013/2/7', 2430.69, 2418.53, 2394.22, 2433.89],
['2013/2/8', 2416.62, 2432.4, 2414.4, 2443.03],
['2013/2/18', 2441.91, 2421.56, 2415.43, 2444.8],
['2013/2/19', 2420.26, 2382.91, 2373.53, 2427.07],
['2013/2/20', 2383.49, 2397.18, 2370.61, 2397.94],
['2013/2/21', 2378.82, 2325.95, 2309.17, 2378.82],
['2013/2/22', 2322.94, 2314.16, 2308.76, 2330.88],
['2013/2/25', 2320.62, 2325.82, 2315.01, 2338.78],
['2013/2/26', 2313.74, 2293.34, 2289.89, 2340.71],
['2013/2/27', 2297.77, 2313.22, 2292.03, 2324.63],
['2013/2/28', 2322.32, 2365.59, 2308.92, 2366.16],
['2013/3/1', 2364.54, 2359.51, 2330.86, 2369.65],
['2013/3/4', 2332.08, 2273.4, 2259.25, 2333.54],
['2013/3/5', 2274.81, 2326.31, 2270.1, 2328.14],
['2013/3/6', 2333.61, 2347.18, 2321.6, 2351.44],
['2013/3/7', 2340.44, 2324.29, 2304.27, 2352.02],
['2013/3/8', 2326.42, 2318.61, 2314.59, 2333.67],
['2013/3/11', 2314.68, 2310.59, 2296.58, 2320.96],
['2013/3/12', 2309.16, 2286.6, 2264.83, 2333.29],
['2013/3/13', 2282.17, 2263.97, 2253.25, 2286.33],
['2013/3/14', 2255.77, 2270.28, 2253.31, 2276.22],
['2013/3/15', 2269.31, 2278.4, 2250, 2312.08],
['2013/3/18', 2267.29, 2240.02, 2239.21, 2276.05],
['2013/3/19', 2244.26, 2257.43, 2232.02, 2261.31],
['2013/3/20', 2257.74, 2317.37, 2257.42, 2317.86],
['2013/3/21', 2318.21, 2324.24, 2311.6, 2330.81],
['2013/3/22', 2321.4, 2328.28, 2314.97, 2332],
['2013/3/25', 2334.74, 2326.72, 2319.91, 2344.89],
['2013/3/26', 2318.58, 2297.67, 2281.12, 2319.99],
['2013/3/27', 2299.38, 2301.26, 2289, 2323.48],
['2013/3/28', 2273.55, 2236.3, 2232.91, 2273.55],
['2013/3/29', 2238.49, 2236.62, 2228.81, 2246.87],
['2013/4/1', 2229.46, 2234.4, 2227.31, 2243.95],
['2013/4/2', 2234.9, 2227.74, 2220.44, 2253.42],
['2013/4/3', 2232.69, 2225.29, 2217.25, 2241.34],
['2013/4/8', 2196.24, 2211.59, 2180.67, 2212.59],
['2013/4/9', 2215.47, 2225.77, 2215.47, 2234.73],
['2013/4/10', 2224.93, 2226.13, 2212.56, 2233.04],
['2013/4/11', 2236.98, 2219.55, 2217.26, 2242.48],
['2013/4/12', 2218.09, 2206.78, 2204.44, 2226.26],
['2013/4/15', 2199.91, 2181.94, 2177.39, 2204.99],
['2013/4/16', 2169.63, 2194.85, 2165.78, 2196.43],
['2013/4/17', 2195.03, 2193.8, 2178.47, 2197.51],
['2013/4/18', 2181.82, 2197.6, 2175.44, 2206.03],
['2013/4/19', 2201.12, 2244.64, 2200.58, 2250.11],
['2013/4/22', 2236.4, 2242.17, 2232.26, 2245.12],
['2013/4/23', 2242.62, 2184.54, 2182.81, 2242.62],
['2013/4/24', 2187.35, 2218.32, 2184.11, 2226.12],
['2013/4/25', 2213.19, 2199.31, 2191.85, 2224.63],
['2013/4/26', 2203.89, 2177.91, 2173.86, 2210.58],
['2013/5/2', 2170.78, 2174.12, 2161.14, 2179.65],
['2013/5/3', 2179.05, 2205.5, 2179.05, 2222.81],
['2013/5/6', 2212.5, 2231.17, 2212.5, 2236.07],
['2013/5/7', 2227.86, 2235.57, 2219.44, 2240.26],
['2013/5/8', 2242.39, 2246.3, 2235.42, 2255.21],
['2013/5/9', 2246.96, 2232.97, 2221.38, 2247.86],
['2013/5/10', 2228.82, 2246.83, 2225.81, 2247.67],
['2013/5/13', 2247.68, 2241.92, 2231.36, 2250.85],
['2013/5/14', 2238.9, 2217.01, 2205.87, 2239.93],
['2013/5/15', 2217.09, 2224.8, 2213.58, 2225.19],
['2013/5/16', 2221.34, 2251.81, 2210.77, 2252.87],
['2013/5/17', 2249.81, 2282.87, 2248.41, 2288.09],
['2013/5/20', 2286.33, 2299.99, 2281.9, 2309.39],
['2013/5/21', 2297.11, 2305.11, 2290.12, 2305.3],
['2013/5/22', 2303.75, 2302.4, 2292.43, 2314.18],
['2013/5/23', 2293.81, 2275.67, 2274.1, 2304.95],
['2013/5/24', 2281.45, 2288.53, 2270.25, 2292.59],
['2013/5/27', 2286.66, 2293.08, 2283.94, 2301.7],
['2013/5/28', 2293.4, 2321.32, 2281.47, 2322.1],
['2013/5/29', 2323.54, 2324.02, 2321.17, 2334.33],
['2013/5/30', 2316.25, 2317.75, 2310.49, 2325.72],
['2013/5/31', 2320.74, 2300.59, 2299.37, 2325.53],
['2013/6/3', 2300.21, 2299.25, 2294.11, 2313.43],
['2013/6/4', 2297.1, 2272.42, 2264.76, 2297.1],
['2013/6/5', 2270.71, 2270.93, 2260.87, 2276.86],
['2013/6/6', 2264.43, 2242.11, 2240.07, 2266.69],
['2013/6/7', 2242.26, 2210.9, 2205.07, 2250.63],
['2013/6/13', 2190.1, 2148.35, 2126.22, 2190.1]
]);
function splitData(rawData) {
const categoryData = [];
const values = [];
for (var i = 0; i < rawData.length; i++) {
categoryData.push(rawData[i].splice(0, 1)[0]);
values.push(rawData[i]);
}
return {
categoryData: categoryData,
values: values
};
}
function calculateMA(dayCount) {
var result = [];
for (var i = 0, len = data0.values.length; i < len; i++) {
if (i < dayCount) {
result.push('-');
continue;
}
var sum = 0;
for (var j = 0; j < dayCount; j++) {
sum += +data0.values[i - j][1];
}
result.push(sum / dayCount);
}
return result;
}
option = {
title: {
text: '上证指数',
left: 0
},
tooltip: {
trigger: 'axis',
axisPointer: {
type: 'cross'
}
},
legend: {
data: ['日K', 'MA5', 'MA10', 'MA20', 'MA30']
},
grid: {
left: '10%',
right: '10%',
bottom: '15%'
},
xAxis: {
type: 'category',
data: data0.categoryData,
boundaryGap: false,
axisLine: { onZero: false },
splitLine: { show: false },
min: 'dataMin',
max: 'dataMax'
},
yAxis: {
scale: true,
splitArea: {
show: true
}
},
dataZoom: [
{
type: 'inside',
start: 50,
end: 100
},
{
show: true,
type: 'slider',
top: '90%',
start: 50,
end: 100
}
],
series: [
{
name: '日K',
type: 'candlestick',
data: data0.values,
itemStyle: {
color: upColor,
color0: downColor,
borderColor: upBorderColor,
borderColor0: downBorderColor
},
markPoint: {
label: {
formatter: function (param) {
return param != null ? Math.round(param.value) + '' : '';
}
},
data: [
{
name: 'Mark',
coord: ['2013/5/31', 2300],
value: 2300,
itemStyle: {
color: 'rgb(41,60,85)'
}
},
{
name: 'highest value',
type: 'max',
valueDim: 'highest'
},
{
name: 'lowest value',
type: 'min',
valueDim: 'lowest'
},
{
name: 'average value on close',
type: 'average',
valueDim: 'close'
}
],
tooltip: {
formatter: function (param) {
return param.name + '<br>' + (param.data.coord || '');
}
}
},
markLine: {
symbol: ['none', 'none'],
data: [
[
{
name: 'from lowest to highest',
type: 'min',
valueDim: 'lowest',
symbol: 'circle',
symbolSize: 10,
label: {
show: false
},
emphasis: {
label: {
show: false
}
}
},
{
type: 'max',
valueDim: 'highest',
symbol: 'circle',
symbolSize: 10,
label: {
show: false
},
emphasis: {
label: {
show: false
}
}
}
],
{
name: 'min line on close',
type: 'min',
valueDim: 'close'
},
{
name: 'max line on close',
type: 'max',
valueDim: 'close'
}
]
}
},
{
name: 'MA5',
type: 'line',
data: calculateMA(5),
smooth: true,
lineStyle: {
opacity: 0.5
}
},
{
name: 'MA10',
type: 'line',
data: calculateMA(10),
smooth: true,
lineStyle: {
opacity: 0.5
}
},
{
name: 'MA20',
type: 'line',
data: calculateMA(20),
smooth: true,
lineStyle: {
opacity: 0.5
}
},
{
name: 'MA30',
type: 'line',
data: calculateMA(30),
smooth: true,
lineStyle: {
opacity: 0.5
}
}
]
};
option && myChart.setOption(option);
}
$(document).ready(function(){
var tag = $.getUrlParam('tag');
if(tag == 'cryptocurrency_trade'){
changeDivH();
// 获取数据源更新日志
dataSourceLog();
setInterval(function(){
dataSourceLog();
},3000);
// 获取策略更新日志
dataStrategyLog();
setInterval(function(){
dataStrategyLog();
},5000);
getStrategyList(1);
calcKLineChats();
}
});
$(window).resize(function(){
changeDivH();
});
Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.3 KiB

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