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
994 changed files with 338 additions and 150873 deletions
+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.
-202
View File
@@ -1,202 +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管理软件。我一看到,就知道这是我一直想要的页面化管理方式。
复制了后台管理界面,按照自己想要的方式写了一版。
![Debian](https://img.shields.io/badge/LINUX-Debian-blue?style=for-the-badge&logo=Debian)
![Ubuntu](https://img.shields.io/badge/LINUX-Ubuntu-blue?style=for-the-badge&logo=Ubuntu)
![Fedora](https://img.shields.io/badge/LINUX-Fedora-blue?style=for-the-badge&logo=Fedora)
![CentOS](https://img.shields.io/badge/LINUX-CentOS-blue?style=for-the-badge&logo=CentOS)
[![Wiki](https://img.shields.io/badge/MW-Wiki-red?style=for-the-badge&logo=wiki)](https://github.com/midoks/mdserver-web/wiki)
[![](https://data.jsdelivr.com/v1/package/gh/midoks/mdserver-web/badge?style=for-the-badge)](https://www.jsdelivr.com/package/gh/midoks/mdserver-web)
* SSH终端工具
* 面板收藏功能
* 网站子目录绑定
* 网站备份功能
* 插件方式管理
基本上可以使用,后续会继续优化!欢迎提供意见!
- 吹水组 - https://t.me/mdserver_web
- 交流论坛 - https://bbs.midoks.me
```
如果出现问题,最好私给我面板信息。不要让我猜。如果不提供,不要提出问题,自行解决。 — 座右铭
Talk is cheap, show me the code. -- linus
```
- [兼容性测试报告](/compatibility.md)
- [常用命令说明](/cmd.md)
### 主要插件介绍
* OpenResty - 轻量级,占有内存少,并发能力强。
* PHP[53-82] - PHP是世界上最好的编程语言。
* MySQL - 一种关系数据库管理系统。
* MariaDB - 是MySQL的一个重要分支。
* MySQL[APT/YUM] - 一种关系数据库管理系统。
* MongoDB - 一种非关系NOSQL数据库管理系统。
* phpMyAdmin - 著名Web端MySQL管理工具。
* Memcached - 一个高性能的分布式内存对象缓存系统。
* Redis - 一个高性能的KV数据库。
* PureFtpd - 一款专注于程序健壮和软件安全的免费FTP服务器软件。
* Gogs - 一款极易搭建的自助Git服务。
* Rsyncd - 通用同步服务。
### 插件开发相关
```
插件文档还不完善,如果有不明白的地方提Issue! 我会尽力完善。
如果你自己写了插件,想分享出来,也可以提Issue。
```
- 简单例子 - https://github.com/mw-plugin/simple-plugin
- 插件地址 - https://github.com/mw-plugin
- 开发文档 - https://github.com/midoks/mdserver-web/wiki/插件开发
# Note
```
phpMyAdmin[4.4.15]支持MySQL[5.5-5.7]
phpMyAdmin[5.2.0]支持MySQL[8.0]
PHP[53-72]支持phpMyAdmin[4.4.15]
PHP[72-82]支持phpMyAdmin[5.2.0]
```
# 特别赞助
- [找资源 - 阿里云盘资源搜索引擎](https://zhaoziyuan.la/)
# AD - VPS推荐 - 🙏
- [ZZZ评测](https://www.zzzvps.com/)
| 服务商 | LOGO | 推广地址 | 优惠码 |
| ------------- |----------|-----------|-------|
| digitalvirt |[![digitalvirt](https://digitalvirt.com/templates/BlueWhite/img/logo-dark.svg)](https://digitalvirt.com/aff.php?aff=154) | https://digitalvirt.com/aff.php?aff=154 | mdserver-web |
# Docker
- 由[DDSRem](https://github.com/DDSRem)开发维护。
- https://hub.docker.com/r/ddsderek/mw
```
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.13.3
* tgbot插件[增加扩展功能]。
* 面板设置ssl功能优化。
* 网站统计-计划任务归档优化。
### JSDelivr安装地址
- 初始安装
```
curl -fsSL https://cdn.jsdelivr.net/gh/midoks/mdserver-web@latest/scripts/install.sh | bash
```
- 直接更新
```
curl -fsSL https://cdn.jsdelivr.net/gh/midoks/mdserver-web@latest/scripts/update.sh | bash
```
- 卸载脚本
```
wget -O uninstall.sh https://cdn.jsdelivr.net/gh/midoks/mdserver-web@latest/scripts/uninstall.sh && bash uninstall.sh
```
### 备用地址
- 初始安装
```
curl -fsSL https://raw.githubusercontent.com/midoks/mdserver-web/master/scripts/install.sh | bash
curl --insecure -fsSL https://gitee.com/midoks/mdserver-web/raw/master/scripts/install.sh | bash
```
- 直接更新
```
curl -fsSL https://raw.githubusercontent.com/midoks/mdserver-web/master/scripts/update.sh | bash
```
- 卸载脚本
```
wget -O uninstall.sh https://raw.githubusercontent.com/midoks/mdserver-web/master/scripts/uninstall.sh && bash uninstall.sh
```
### 通用软件安装[命令行安装]
- 需已经安装mdserver-web
```
curl -fsSL https://raw.githubusercontent.com/midoks/mdserver-web/dev/scripts/quick/app.sh | bash
```
### DEV使用
```
curl -fsSL https://raw.githubusercontent.com/midoks/mdserver-web/dev/scripts/install_dev.sh | bash
curl -fsSL https://raw.githubusercontent.com/midoks/mdserver-web/dev/scripts/update_dev.sh | bash
wget -O uninstall.sh https://raw.githubusercontent.com/midoks/mdserver-web/dev/scripts/uninstall.sh && bash uninstall.sh
curl -fsSL https://raw.githubusercontent.com/midoks/mdserver-web/dev/scripts/quick/debug.sh | bash
curl -fsSL https://gitee.com/midoks/mdserver-web/raw/dev/scripts/install_dev.sh | bash
curl -fsSL https://gitee.com/midoks/mdserver-web/raw/dev/scripts/update_dev.sh | bash
```
### 捐赠地址 USDT(TRC20)
TVbNgrpeGBGZVm5gTLa21ADP7RpnPFhjya
日行一善,以后必定大富大贵
### 微信赞助
[![截图](https://cdn.jsdelivr.net/gh/midoks/mdserver-web@latest/route/static/img/weixin_zz.jpg)](https://cdn.jsdelivr.net/gh/midoks/mdserver-web@latest/route/static/img/weixin_zz.jpg)
### 无图不真相
[![截图](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
-51
View File
@@ -1,51 +0,0 @@
# coding:utf-8
# ---------------------------------------------------------------------------------
# MW-Linux面板
# ---------------------------------------------------------------------------------
# copyright (c) 2018-∞(https://github.com/midoks/mdserver-web) All rights reserved.
# ---------------------------------------------------------------------------------
# Author: midoks <midoks@163.com>
# ---------------------------------------------------------------------------------
# ---------------------------------------------------------------------------------
# 入口文件
# ---------------------------------------------------------------------------------
# pip install profiler_online
# 性能测试
# from profiler_online import run_profiler
# run_profiler()
import sys
import io
import os
sys.dont_write_bytecode = True
from route import app, socketio
from gevent.pywsgi import WSGIServer
from geventwebsocket.handler import WebSocketHandler
from gevent import monkey
monkey.patch_all()
try:
if __name__ == "__main__":
PORT = 7200
if os.path.exists('data/port.pl'):
f = open('data/port.pl')
PORT = int(f.read())
f.close()
# HOST = '0.0.0.0'
# app.run(host=HOST, port=PORT)
http_server = WSGIServer(
(HOST, PORT), app, handler_class=WebSocketHandler)
http_server.serve_forever()
socketio.run(app, host=HOST, port=PORT)
except Exception as ex:
print(ex)
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
-144
View File
@@ -1,144 +0,0 @@
# coding: utf-8
# ---------------------------------------------------------------------------------
# MW-Linux面板
# ---------------------------------------------------------------------------------
# copyright (c) 2018-∞(https://github.com/midoks/mdserver-web) All rights reserved.
# ---------------------------------------------------------------------------------
# Author: midoks <midoks@163.com>
# ---------------------------------------------------------------------------------
# ---------------------------------------------------------------------------------
# 公共操作
# ---------------------------------------------------------------------------------
import os
import sys
import time
import string
import json
import hashlib
import shlex
import datetime
import subprocess
import re
import hashlib
from random import Random
import mw
import db
from flask import redirect
def init():
initDB()
initUserInfo()
initInitD()
initInitTask()
def local():
result = checkClose()
if result:
return result
# 检查面板是否关闭
def checkClose():
if os.path.exists('data/close.pl'):
return redirect('/close')
def initDB():
try:
sql = db.Sql().dbfile('default')
csql = mw.readFile('data/sql/default.sql')
csql_list = csql.split(';')
for index in range(len(csql_list)):
sql.execute(csql_list[index], ())
except Exception as ex:
print(str(ex))
def doContentReplace(src, dst):
content = mw.readFile(src)
content = content.replace("{$SERVER_PATH}", mw.getRunDir())
mw.writeFile(dst, content)
def initInitD():
# systemctl
# sysCfgDir = mw.systemdCfgDir()
# if os.path.exists(sysCfgDir) and mw.getOsName() == 'centos' and mw.getOsID() == '9':
# systemd_mw = sysCfgDir + '/mw.service'
# systemd_mw_task = sysCfgDir + '/mw-task.service'
# systemd_mw_tpl = mw.getRunDir() + '/scripts/init.d/mw.service.tpl'
# systemd_mw_task_tpl = mw.getRunDir() + '/scripts/init.d/mw-task.service.tpl'
# if os.path.exists(systemd_mw):
# os.remove(systemd_mw)
# if os.path.exists(systemd_mw_task):
# os.remove(systemd_mw_task)
# doContentReplace(systemd_mw_tpl, systemd_mw)
# doContentReplace(systemd_mw_task_tpl, systemd_mw_task)
# mw.execShell('systemctl enable mw')
# mw.execShell('systemctl enable mw-task')
# mw.execShell('systemctl daemon-reload')
script = mw.getRunDir() + '/scripts/init.d/mw.tpl'
script_bin = mw.getRunDir() + '/scripts/init.d/mw'
doContentReplace(script, script_bin)
mw.execShell('chmod +x ' + script_bin)
# 在linux系统中,确保/etc/init.d存在
if not mw.isAppleSystem() and not os.path.exists("/etc/rc.d/init.d"):
mw.execShell('mkdir -p /etc/rc.d/init.d')
if not mw.isAppleSystem() and not os.path.exists("/etc/init.d"):
mw.execShell('mkdir -p /etc/init.d')
# initd
if os.path.exists('/etc/rc.d/init.d'):
initd_bin = '/etc/rc.d/init.d/mw'
if not os.path.exists(initd_bin):
import shutil
shutil.copyfile(script_bin, initd_bin)
mw.execShell('chmod +x ' + initd_bin)
# 加入自启动
mw.execShell('which chkconfig && chkconfig --add mw')
if os.path.exists('/etc/init.d'):
initd_bin = '/etc/init.d/mw'
if not os.path.exists(initd_bin):
import shutil
shutil.copyfile(script_bin, initd_bin)
mw.execShell('chmod +x ' + initd_bin)
# 加入自启动
mw.execShell('which update-rc.d && update-rc.d -f mw defaults')
# 获取系统IPV4
mw.setHostAddr(mw.getLocalIp())
def initInitTask():
# 创建证书同步命令
import cert_api
api = cert_api.cert_api()
api.createCertCron()
def initUserInfo():
data = mw.M('users').where('id=?', (1,)).getField('password')
if data == '21232f297a57a5a743894a0e4a801fc3':
pwd = mw.getRandomString(8).lower()
file_pw = mw.getRunDir() + '/data/default.pl'
mw.writeFile(file_pw, pwd)
mw.M('users').where('id=?', (1,)).setField(
'password', mw.md5(pwd))
File diff suppressed because it is too large Load Diff
-664
View File
@@ -1,664 +0,0 @@
# coding: utf-8
# ---------------------------------------------------------------------------------
# MW-Linux面板
# ---------------------------------------------------------------------------------
# copyright (c) 2018-∞(https://github.com/midoks/mdserver-web) All rights reserved.
# ---------------------------------------------------------------------------------
# Author: midoks <midoks@163.com>
# ---------------------------------------------------------------------------------
# ---------------------------------------------------------------------------------
# 计划任务操作
# ---------------------------------------------------------------------------------
import psutil
import time
import os
import sys
import mw
import re
import json
import pwd
from flask import request
class crontab_api:
field = 'id,name,type,where1,where_hour,where_minute,echo,addtime,status,save,backup_to,stype,sname,sbody,urladdress'
def __init__(self):
pass
##### ----- start ----- ###
def listApi(self):
p = request.args.get('p', 1)
psize = 10
startPage = (int(p) - 1) * psize
pageInfo = str(startPage) + ',' + str(psize)
_list = mw.M('crontab').where('', ()).field(
self.field).limit(pageInfo).order('id desc').select()
data = []
for i in range(len(_list)):
tmp = _list[i]
if _list[i]['type'] == "day":
tmp['type'] = '每天'
tmp['cycle'] = mw.getInfo('每天, {1}点{2}分 执行', (str(
_list[i]['where_hour']), str(_list[i]['where_minute'])))
elif _list[i]['type'] == "day-n":
tmp['type'] = mw.getInfo(
'每{1}天', (str(_list[i]['where1']),))
tmp['cycle'] = mw.getInfo('每隔{1}天, {2}点{3}分 执行', (str(
_list[i]['where1']), str(_list[i]['where_hour']), str(_list[i]['where_minute'])))
elif _list[i]['type'] == "hour":
tmp['type'] = '每小时'
tmp['cycle'] = mw.getInfo(
'每小时, 第{1}分钟 执行', (str(_list[i]['where_minute']),))
elif _list[i]['type'] == "hour-n":
tmp['type'] = mw.getInfo(
'每{1}小时', (str(_list[i]['where1']),))
tmp['cycle'] = mw.getInfo('每{1}小时, 第{2}分钟 执行', (str(
_list[i]['where1']), str(_list[i]['where_minute'])))
elif _list[i]['type'] == "minute-n":
tmp['type'] = mw.getInfo(
'每{1}分钟', (str(_list[i]['where1']),))
tmp['cycle'] = mw.getInfo(
'每隔{1}分钟执行', (str(_list[i]['where1']),))
elif _list[i]['type'] == "week":
tmp['type'] = '每周'
if not _list[i]['where1']:
_list[i]['where1'] = '0'
tmp['cycle'] = mw.getInfo('每周{1}, {2}点{3}分执行', (self.toWeek(int(
_list[i]['where1'])), str(_list[i]['where_hour']), str(_list[i]['where_minute'])))
elif _list[i]['type'] == "month":
tmp['type'] = '每月'
tmp['cycle'] = mw.getInfo('每月, {1}日 {2}点{3}分执行', (str(_list[i]['where1']), str(
_list[i]['where_hour']), str(_list[i]['where_minute'])))
data.append(tmp)
rdata = {}
rdata['data'] = data
count = mw.M('crontab').where('', ()).count()
_page = {}
_page['count'] = count
_page['p'] = p
_page['row'] = psize
_page['tojs'] = "getCronData"
rdata['list'] = mw.getPage(_page)
rdata['p'] = p
# backup hook
bh_file = mw.getPanelDataDir() + "/hook_backup.json"
if os.path.exists(bh_file):
hb_data = mw.readFile(bh_file)
hb_data = json.loads(hb_data)
rdata['backup_hook'] = hb_data
return mw.getJson(rdata)
# 设置计划任务状态
def setCronStatusApi(self):
mid = request.form.get('id', '')
cronInfo = mw.M('crontab').where(
'id=?', (mid,)).field(self.field).find()
status = 1
if cronInfo['status'] == status:
status = 0
self.removeForCrond(cronInfo['echo'])
else:
cronInfo['status'] = 1
self.syncToCrond(cronInfo)
mw.M('crontab').where('id=?', (mid,)).setField('status', status)
mw.writeLog(
'计划任务', '修改计划任务[' + cronInfo['name'] + ']状态为[' + str(status) + ']')
return mw.returnJson(True, '设置成功')
# 获取指定任务数据
def getCrondFindApi(self):
sid = request.form.get('id', '')
data = mw.M('crontab').where(
'id=?', (sid,)).field(self.field).find()
return mw.getJson(data)
# 参数校验
def cronCheck(self, params):
if params['stype'] == 'site' or params['stype'] == 'database' or params['stype'].find('database_') > -1 or params['stype'] == 'logs' or params['stype'] == 'path':
if params['save'] == '':
return False, '保留份数不能为空!'
if params['type'] == 'day':
if params['hour'] == '':
return False, '小时不能为空!'
if params['minute'] == '':
return False, '分钟不能为空!'
if params['type'] == 'day-n':
if params['where1'] == '':
return False, '天不能为空!'
if params['hour'] == '':
return False, '小时不能为空!'
if params['minute'] == '':
return False, '分钟不能为空!'
if params['type'] == 'hour':
if params['minute'] == '':
return False, '分钟不能为空!'
if params['type'] == 'hour-n':
if params['where1'] == '':
return False, '小时不能为空!'
if params['minute'] == '':
return False, '分钟不能为空!'
if params['type'] == 'minute-n':
if params['where1'] == '':
return False, '分钟不能为空!'
if params['type'] == 'week':
if params['hour'] == '':
return False, '小时不能为空!'
if params['minute'] == '':
return False, '分钟不能为空!'
if params['type'] == 'month':
if params['where1'] == '':
return False, '日不能为空!'
if params['hour'] == '':
return False, '小时不能为空!'
if params['minute'] == '':
return False, '分钟不能为空!'
return True, 'OK'
def modifyCrondApi(self):
sid = request.form.get('id', '')
iname = request.form.get('name', '')
field_type = request.form.get('type', '')
week = request.form.get('week', '')
where1 = request.form.get('where1', '')
hour = request.form.get('hour', '')
minute = request.form.get('minute', '')
save = request.form.get('save', '')
backup_to = request.form.get('backup_to', '')
stype = request.form.get('stype', '')
sname = request.form.get('sname', '')
sbody = request.form.get('sbody', '')
urladdress = request.form.get('urladdress', '')
if len(iname) < 1:
return mw.returnJson(False, '任务名称不能为空!')
params = {
'name': iname,
'type': field_type,
'week': week,
'where1': where1,
'hour': hour,
'minute': minute,
'save': save,
'backup_to': backup_to,
'stype': stype,
'sname': sname,
'sbody': sbody,
'urladdress': urladdress,
}
is_check_pass, msg = self.cronCheck(params)
if not is_check_pass:
return mw.returnJson(is_check_pass, msg)
cuonConfig, get, name = self.getCrondCycle(params)
cronInfo = mw.M('crontab').where(
'id=?', (sid,)).field(self.field).find()
del(cronInfo['id'])
del(cronInfo['addtime'])
cronInfo['name'] = get['name']
cronInfo['type'] = get['type']
cronInfo['where1'] = get['where1']
cronInfo['where_hour'] = get['hour']
cronInfo['where_minute'] = get['minute']
cronInfo['save'] = get['save']
cronInfo['backup_to'] = get['backup_to']
cronInfo['sbody'] = get['sbody']
cronInfo['urladdress'] = get['urladdress']
addData = mw.M('crontab').where('id=?', (sid,)).save('name,type,where1,where_hour,where_minute,save,backup_to, sname, sbody,urladdress',
(iname, field_type, get['where1'], get['hour'], get['minute'], get['save'], get['backup_to'], sname, get['sbody'], get['urladdress']))
self.removeForCrond(cronInfo['echo'])
self.syncToCrond(cronInfo)
mw.writeLog('计划任务', '修改计划任务[' + cronInfo['name'] + ']成功')
return mw.returnJson(True, '修改成功')
def logsApi(self):
sid = request.form.get('id', '')
echo = mw.M('crontab').where("id=?", (sid,)).field('echo').find()
logFile = mw.getServerDir() + '/cron/' + echo['echo'] + '.log'
if not os.path.exists(logFile):
return mw.returnJson(False, '当前日志为空!')
log = mw.getLastLine(logFile, 500)
return mw.returnJson(True, log)
def addApi(self):
iname = request.form.get('name', '')
field_type = request.form.get('type', '')
week = request.form.get('week', '')
where1 = request.form.get('where1', '')
hour = request.form.get('hour', '')
minute = request.form.get('minute', '')
save = request.form.get('save', '')
backup_to = request.form.get('backupTo', '')
stype = request.form.get('sType', '')
sname = request.form.get('sName', '')
sbody = request.form.get('sBody', '')
urladdress = request.form.get('urladdress', '')
if len(iname) < 1:
return mw.returnJson(False, '任务名称不能为空!')
params = {
'name': iname,
'type': field_type,
'week': week,
'where1': where1,
'hour': hour,
'minute': minute,
'save': save,
'backup_to': backup_to,
'stype': stype,
'sname': sname,
'sbody': sbody,
'urladdress': urladdress,
}
is_check_pass, msg = self.cronCheck(params)
if not is_check_pass:
return mw.returnJson(is_check_pass, msg)
addData = self.add(params)
if addData > 0:
return mw.returnJson(True, '添加成功')
return mw.returnJson(False, '添加失败')
def add(self, params):
iname = params["name"]
field_type = params["type"]
week = params["week"]
where1 = params["where1"]
hour = params["hour"]
minute = params["minute"]
save = params["save"]
backup_to = params["backup_to"]
stype = params["stype"]
sname = params["sname"]
sbody = params["sbody"]
urladdress = params["urladdress"]
# print params
cronConfig, get, name = self.getCrondCycle(params)
cronPath = mw.getServerDir() + '/cron'
cronName = self.getShell(params)
if type(cronName) == dict:
return cronName
cronConfig += ' ' + cronPath + '/' + cronName + \
' >> ' + cronPath + '/' + cronName + '.log 2>&1'
# print(cronConfig)
if not mw.isAppleSystem():
wRes = self.writeShell(cronConfig)
if type(wRes) != bool:
return wRes
self.crondReload()
add_time = time.strftime('%Y-%m-%d %X', time.localtime())
task_id = mw.M('crontab').add('name,type,where1,where_hour,where_minute,echo,addtime,status,save,backup_to,stype,sname,sbody,urladdress',
(iname, field_type, where1, hour, minute, cronName, add_time, 1, save, backup_to, stype, sname, sbody, urladdress,))
return task_id
def startTaskApi(self):
sid = request.form.get('id', '')
echo = mw.M('crontab').where('id=?', (sid,)).getField('echo')
execstr = mw.getServerDir() + '/cron/' + echo
os.system('chmod +x ' + execstr)
os.system('nohup ' + execstr + ' >> ' + execstr + '.log 2>&1 &')
return mw.returnJson(True, '任务已执行!')
def delApi(self):
task_id = request.form.get('id', '')
try:
data = self.delete(task_id)
if not data[0]:
return mw.returnJson(False, data[1])
return mw.returnJson(True, '删除成功')
except Exception as e:
return mw.returnJson(False, '删除失败:' + str(e))
def delete(self, tid):
find = mw.M('crontab').where("id=?", (tid,)).field('name,echo').find()
if not self.removeForCrond(find['echo']):
return (False, '无法写入文件,请检查是否开启了系统加固功能!')
cronPath = mw.getServerDir() + '/cron'
sfile = cronPath + '/' + find['echo']
if os.path.exists(sfile):
os.remove(sfile)
sfile = cronPath + '/' + find['echo'] + '.log'
if os.path.exists(sfile):
os.remove(sfile)
mw.M('crontab').where("id=?", (tid,)).delete()
mw.writeLog('计划任务', mw.getInfo('删除计划任务[{1}]成功!', (find['name'],)))
return (True, "OK")
def delLogsApi(self):
sid = request.form.get('id', '')
try:
echo = mw.M('crontab').where("id=?", (sid,)).getField('echo')
logFile = mw.getServerDir() + '/cron/' + echo + '.log'
os.remove(logFile)
return mw.returnJson(True, '任务日志已清空!')
except:
return mw.returnJson(False, '任务日志清空失败!')
# 取数据列表
def getDataListApi(self):
stype = request.form.get('type', '')
bak_data = []
if stype == 'site' or stype == 'sites' or stype == 'database' or stype.find('database_') > -1 or stype == 'path':
hookPath = mw.getPanelDataDir() + "/hook_backup.json"
if os.path.exists(hookPath):
t = mw.readFile(hookPath)
bak_data = json.loads(t)
if stype == 'database' or stype.find('database_') > -1:
sqlite3_name = 'mysql'
path = mw.getServerDir() + '/mysql'
if stype != 'database':
soft_name = stype.replace('database_', '')
path = mw.getServerDir() + '/' + soft_name
if soft_name == 'postgresql':
sqlite3_name = 'pgsql'
db_list = {}
db_list['orderOpt'] = bak_data
if not os.path.exists(path + '/' + sqlite3_name + '.db'):
db_list['data'] = []
else:
db_list['data'] = mw.M('databases').dbPos(
path, sqlite3_name).field('name,ps').select()
return mw.getJson(db_list)
if stype == 'path':
db_list = {}
db_list['data'] = [{"name": mw.getWwwDir(), "ps": "www"}]
db_list['orderOpt'] = bak_data
return mw.getJson(db_list)
data = {}
data['orderOpt'] = bak_data
default_db = 'sites'
# if stype == 'site' or stype == 'logs':
# stype == 'sites'
data['data'] = mw.M(default_db).field('name,ps').select()
return mw.getJson(data)
##### ----- start ----- ###
# 转换大写星期
def toWeek(self, num):
wheres = {
0: '日',
1: '一',
2: '二',
3: '三',
4: '四',
5: '五',
6: '六'
}
try:
return wheres[num]
except:
return ''
def getCrondCycle(self, params):
cuonConfig = ''
name = ''
if params['type'] == "day":
cuonConfig = self.getDay(params)
name = '每天'
elif params['type'] == "day-n":
cuonConfig = self.getDay_N(params)
name = mw.getInfo('每{1}天', (params['where1'],))
elif params['type'] == "hour":
cuonConfig = self.getHour(params)
name = '每小时'
elif params['type'] == "hour-n":
cuonConfig = self.getHour_N(params)
name = '每小时'
elif params['type'] == "minute-n":
cuonConfig = self.minute_N(params)
elif params['type'] == "week":
params['where1'] = params['week']
cuonConfig = self.week(params)
elif params['type'] == "month":
cuonConfig = self.month(params)
return cuonConfig, params, name
# 取任务构造Day
def getDay(self, param):
cuonConfig = "{0} {1} * * * ".format(param['minute'], param['hour'])
return cuonConfig
# 取任务构造Day_n
def getDay_N(self, param):
cuonConfig = "{0} {1} */{2} * * ".format(
param['minute'], param['hour'], param['where1'])
return cuonConfig
# 取任务构造Hour
def getHour(self, param):
cuonConfig = "{0} * * * * ".format(param['minute'])
return cuonConfig
# 取任务构造Hour-N
def getHour_N(self, param):
cuonConfig = "{0} */{1} * * * ".format(
param['minute'], param['where1'])
return cuonConfig
# 取任务构造Minute-N
def minute_N(self, param):
cuonConfig = "*/{0} * * * * ".format(param['where1'])
return cuonConfig
# 取任务构造week
def week(self, param):
cuonConfig = "{0} {1} * * {2}".format(
param['minute'], param['hour'], param['week'])
return cuonConfig
# 取任务构造Month
def month(self, param):
cuonConfig = "{0} {1} {2} * * ".format(
param['minute'], param['hour'], param['where1'])
return cuonConfig
# 取执行脚本
def getShell(self, param):
# try:
stype = param['stype']
if stype == 'toFile':
shell = param.sFile
else:
head = "#!/bin/bash\nPATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin\nexport PATH\n"
source_bin_activate = '''
export LANG=en_US.UTF-8
MW_PATH=%s/bin/activate
if [ -f $MW_PATH ];then
source $MW_PATH
fi''' % (mw.getRunDir(),)
head = head + source_bin_activate + "\n"
log = '.log'
script_dir = mw.getRunDir() + "/scripts"
source_stype = 'database'
if stype.find('database_') > -1:
plugin_name = stype.replace('database_', '')
script_dir = mw.getRunDir() + "/plugins/" + plugin_name + "/scripts"
source_stype = stype
stype = 'database'
wheres = {
'path': head + "python3 " + script_dir + "/backup.py path " + param['sname'] + " " + str(param['save']),
'site': head + "python3 " + script_dir + "/backup.py site " + param['sname'] + " " + str(param['save']),
'database': head + "python3 " + script_dir + "/backup.py database " + param['sname'] + " " + str(param['save']),
'logs': head + "python3 " + script_dir + "/logs_backup.py " + param['sname'] + log + " " + str(param['save']),
'rememory': head + "/bin/bash " + script_dir + '/rememory.sh'
}
if param['backup_to'] != 'localhost':
cfile = mw.getPluginDir() + "/" + \
param['backup_to'] + "/index.py"
wheres['path'] = head + "python3 " + cfile + \
" path " + param['sname'] + " " + str(param['save'])
wheres['site'] = head + "python3 " + cfile + \
" site " + param['sname'] + " " + str(param['save'])
wheres['database'] = head + "python3 " + cfile + " " + \
source_stype + " " + \
param['sname'] + " " + str(param['save'])
try:
shell = wheres[stype]
except:
if stype == 'toUrl':
shell = head + "curl -sS --connect-timeout 10 -m 60 '" + \
param['urladdress'] + "'"
else:
shell = head + param['sbody'].replace("\r\n", "\n")
shell += '''
echo "----------------------------------------------------------------------------"
endDate=`date +"%Y-%m-%d %H:%M:%S"`
echo "★[$endDate] Successful"
echo "----------------------------------------------------------------------------"
'''
cronPath = mw.getServerDir() + '/cron'
if not os.path.exists(cronPath):
mw.execShell('mkdir -p ' + cronPath)
if not 'echo' in param:
cronName = mw.md5(mw.md5(str(time.time()) + '_mw'))
else:
cronName = param['echo']
file = cronPath + '/' + cronName
mw.writeFile(file, self.checkScript(shell))
mw.execShell('chmod 750 ' + file)
return cronName
# 检查脚本
def checkScript(self, shell):
keys = ['shutdown', 'init 0', 'mkfs', 'passwd',
'chpasswd', '--stdin', 'mkfs.ext', 'mke2fs']
for key in keys:
shell = shell.replace(key, '[***]')
return shell
# 将Shell脚本写到文件
def writeShell(self, config):
u_file = '/var/spool/cron/crontabs/root'
if not os.path.exists(u_file):
file = '/var/spool/cron/root'
if mw.isAppleSystem():
file = '/etc/crontab'
else:
file = u_file
if not os.path.exists(file):
mw.writeFile(file, '')
conf = mw.readFile(file)
conf += str(config) + "\n"
if mw.writeFile(file, conf):
if not os.path.exists(u_file):
mw.execShell("chmod 600 '" + file +
"' && chown root.root " + file)
else:
mw.execShell("chmod 600 '" + file +
"' && chown root.crontab " + file)
return True
return mw.returnJson(False, '文件写入失败,请检查是否开启系统加固功能!')
# 重载配置
def crondReload(self):
if mw.isAppleSystem():
# mw.execShell('/usr/sbin/cron restart')
if os.path.exists('/etc/crontab'):
pass
else:
if os.path.exists('/etc/init.d/crond'):
mw.execShell('/etc/init.d/crond reload')
elif os.path.exists('/etc/init.d/cron'):
mw.execShell('service cron restart')
else:
mw.execShell("systemctl reload crond")
# 从crond删除
def removeForCrond(self, echo):
u_file = '/var/spool/cron/crontabs/root'
if not os.path.exists(u_file):
file = '/var/spool/cron/root'
if mw.isAppleSystem():
file = '/etc/crontab'
if not os.path.exists(file):
return False
else:
file = u_file
if mw.isAppleSystem():
return True
conf = mw.readFile(file)
rep = ".+" + str(echo) + ".+\n"
conf = re.sub(rep, "", conf)
if not mw.writeFile(file, conf):
return False
self.crondReload()
return True
def syncToCrond(self, cronInfo):
if 'status' in cronInfo:
if cronInfo['status'] == 0:
return False
if 'where_hour' in cronInfo:
cronInfo['hour'] = cronInfo['where_hour']
cronInfo['minute'] = cronInfo['where_minute']
cronInfo['week'] = cronInfo['where1']
cuonConfig, cronInfo, name = self.getCrondCycle(cronInfo)
cronPath = mw.getServerDir() + '/cron'
cronName = self.getShell(cronInfo)
if type(cronName) == dict:
return cronName
cuonConfig += ' ' + cronPath + '/' + cronName + \
' >> ' + cronPath + '/' + cronName + '.log 2>&1'
wRes = self.writeShell(cuonConfig)
if type(wRes) != bool:
return False
self.crondReload()
-394
View File
@@ -1,394 +0,0 @@
# coding: utf-8
# ---------------------------------------------------------------------------------
# MW-Linux面板
# ---------------------------------------------------------------------------------
# copyright (c) 2018-∞(https://github.com/midoks/mdserver-web) All rights reserved.
# ---------------------------------------------------------------------------------
# Author: midoks <midoks@163.com>
# ---------------------------------------------------------------------------------
# ---------------------------------------------------------------------------------
# sqlite3操作
# ---------------------------------------------------------------------------------
import sqlite3
import os
import sys
class Sql():
#------------------------------
# 数据库操作类 For sqlite3
#------------------------------
__DB_FILE = None # 数据库文件
__DB_CONN = None # 数据库连接对象
__DB_TABLE = "" # 被操作的表名称
__OPT_WHERE = "" # where条件
__OPT_LIMIT = "" # limit条件
__OPT_GROUP = "" # group条件
__OPT_ORDER = "" # order条件
__OPT_FIELD = "*" # field条件
__OPT_PARAM = () # where值
def __init__(self):
self.__DB_FILE = 'data/default.db'
def __getConn(self):
# 取数据库对象
try:
if self.__DB_CONN == None:
self.__DB_CONN = sqlite3.connect(self.__DB_FILE)
self.__DB_CONN.text_factory = str
except Exception as ex:
return "error: " + str(ex)
def autoTextFactory(self):
if sys.version_info[0] == 3:
self.__DB_CONN.text_factory = lambda x: str(
x, encoding="utf-8", errors='ignore')
else:
self.__DB_CONN.text_factory = lambda x: unicode(
x, "utf-8", "ignore")
def dbfile(self, name):
self.__DB_FILE = 'data/' + name + '.db'
return self
def dbPos(self, path, name):
self.__DB_FILE = path + '/' + name + '.db'
return self
def table(self, table):
# 设置表名
self.__DB_TABLE = table
return self
def where(self, where, param):
# WHERE条件
if where:
self.__OPT_WHERE = " WHERE " + where
self.__OPT_PARAM = param
return self
def andWhere(self, where, param):
# WHERE条件
if where:
self.__OPT_WHERE = self.__OPT_WHERE + " and " + where
# print(param)
# print(self.__OPT_PARAM)
self.__OPT_PARAM = self.__OPT_PARAM + param
return self
def order(self, order):
# ORDER条件
if len(order):
self.__OPT_ORDER = " ORDER BY " + order
else:
self.__OPT_ORDER = ""
return self
def group(self, group):
if len(group):
self.__OPT_GROUP = " GROUP BY " + group
else:
self.__OPT_GROUP = ""
return self
def limit(self, limit):
# LIMIT条件
if len(limit):
self.__OPT_LIMIT = " LIMIT " + limit
else:
self.__OPT_LIMIT = ""
return self
def field(self, field):
# FIELD条件
if len(field):
self.__OPT_FIELD = field
return self
def select(self):
# 查询数据集
self.__getConn()
try:
sql = "SELECT " + self.__OPT_FIELD + " FROM " + self.__DB_TABLE + \
self.__OPT_WHERE + self.__OPT_GROUP + self.__OPT_ORDER + self.__OPT_LIMIT
# print(sql)
# print(self.__OPT_PARAM)
result = self.__DB_CONN.execute(sql, self.__OPT_PARAM)
data = result.fetchall()
# 构造字曲系列
if self.__OPT_FIELD != "*":
field = self.__OPT_FIELD.split(',')
tmp = []
for row in data:
i = 0
tmp1 = {}
for key in field:
tmp1[key] = row[i]
i += 1
tmp.append(tmp1)
del(tmp1)
data = tmp
del(tmp)
else:
# 将元组转换成列表
tmp = map(list, data)
data = tmp
del(tmp)
self.__close()
return data
except Exception as ex:
return "error: " + str(ex)
def inquiry(self, input_field=''):
# 查询数据集
# 不清空查询参数
self.__getConn()
try:
sql = "SELECT " + self.__OPT_FIELD + " FROM " + self.__DB_TABLE + \
self.__OPT_WHERE + self.__OPT_GROUP + self.__OPT_ORDER + self.__OPT_LIMIT
# print(sql, self.__OPT_PARAM)
result = self.__DB_CONN.execute(sql, self.__OPT_PARAM)
data = result.fetchall()
# 构造字曲系列
if self.__OPT_FIELD != "*":
if input_field != "":
field = input_field.split(',')
else:
field = self.__OPT_FIELD.split(',')
tmp = []
for row in data:
i = 0
tmp1 = {}
for key in field:
tmp1[key] = row[i]
i += 1
tmp.append(tmp1)
del(tmp1)
data = tmp
del(tmp)
else:
# 将元组转换成列表
tmp = map(list, data)
data = tmp
del(tmp)
return data
except Exception as ex:
return "error: " + str(ex)
def getField(self, keyName):
# 取回指定字段
result = self.field(keyName).select()
# print(result)
if len(result) == 1:
return result[0][keyName]
return result
def setField(self, keyName, keyValue):
# 更新指定字段
return self.save(keyName, (keyValue,))
def find(self):
# 取一行数据
result = self.limit("1").select()
if len(result) == 1:
return result[0]
return result
def count(self):
# 取行数
key = "COUNT(*)"
data = self.field(key).select()
try:
return int(data[0][key])
except:
return 0
def add(self, keys, param):
# 插入数据
self.__getConn()
try:
values = ""
for key in keys.split(','):
values += "?,"
values = self.checkInput(values[0:len(values) - 1])
sql = "INSERT INTO " + self.__DB_TABLE + \
"(" + keys + ") " + "VALUES(" + values + ")"
result = self.__DB_CONN.execute(sql, param)
last_id = result.lastrowid
self.__close()
self.__DB_CONN.commit()
return last_id
except Exception as ex:
return "error: " + str(ex)
# 插入数据
def insert(self, pdata):
if not pdata:
return False
keys, param = self.__format_pdata(pdata)
return self.add(keys, param)
# 更新数据
def update(self, pdata):
if not pdata:
return False
keys, param = self.__format_pdata(pdata)
return self.save(keys, param)
# 构造数据
def __format_pdata(self, pdata):
keys = pdata.keys()
keys_str = ','.join(keys)
param = []
for k in keys:
param.append(pdata[k])
return keys_str, tuple(param)
def checkInput(self, data):
if not data:
return data
if type(data) != str:
return data
checkList = [
{'d': '<', 'r': '<'},
{'d': '>', 'r': '>'},
{'d': '\'', 'r': '‘'},
{'d': '"', 'r': '“'},
{'d': '&', 'r': '&'},
{'d': '#', 'r': '#'},
{'d': '<', 'r': '<'}
]
for v in checkList:
data = data.replace(v['d'], v['r'])
return data
def addAll(self, keys, param):
# 插入数据
self.__getConn()
try:
values = ""
for key in keys.split(','):
values += "?,"
values = values[0:len(values) - 1]
sql = "INSERT INTO " + self.__DB_TABLE + \
"(" + keys + ") " + "VALUES(" + values + ")"
result = self.__DB_CONN.execute(sql, param)
return True
except Exception as ex:
return "error: " + str(ex)
def commit(self):
self.__close()
self.__DB_CONN.commit()
def save(self, keys, param):
# 更新数据
self.__getConn()
try:
opt = ""
for key in keys.split(','):
opt += key + "=?,"
opt = opt[0:len(opt) - 1]
sql = "UPDATE " + self.__DB_TABLE + " SET " + opt + self.__OPT_WHERE
# import mw
# mw.writeFile('/tmp/test.pl', sql)
# 处理拼接WHERE与UPDATE参数
tmp = list(param)
for arg in self.__OPT_PARAM:
tmp.append(arg)
self.__OPT_PARAM = tuple(tmp)
result = self.__DB_CONN.execute(sql, self.__OPT_PARAM)
self.__close()
self.__DB_CONN.commit()
return result.rowcount
except Exception as ex:
return "error: " + str(ex)
def delete(self, id=None):
# 删除数据
self.__getConn()
try:
if id:
self.__OPT_WHERE = " WHERE id=?"
self.__OPT_PARAM = (id,)
sql = "DELETE FROM " + self.__DB_TABLE + self.__OPT_WHERE
result = self.__DB_CONN.execute(sql, self.__OPT_PARAM)
self.__close()
self.__DB_CONN.commit()
return result.rowcount
except Exception as ex:
return "error: " + str(ex)
def originExecute(self, sql, param=()):
self.__getConn()
try:
result = self.__DB_CONN.execute(sql, param)
self.__DB_CONN.commit()
return result
except Exception as ex:
return "error: " + str(ex)
def execute(self, sql, param=()):
# 执行SQL语句返回受影响行
self.__getConn()
# print sql, param
try:
result = self.__DB_CONN.execute(sql, param)
self.__DB_CONN.commit()
return result.rowcount
except Exception as ex:
return "error: " + str(ex)
def query(self, sql, param=()):
# 执行SQL语句返回数据集
self.__getConn()
try:
result = self.__DB_CONN.execute(sql, param)
# 将元组转换成列表
# data = map(list, result)
return result
except Exception as ex:
return "error: " + str(ex)
def create(self, name):
# 创建数据表
self.__getConn()
import mw
script = mw.readFile('data/' + name + '.sql')
result = self.__DB_CONN.executescript(script)
self.__DB_CONN.commit()
return result.rowcount
def fofile(self, filename):
# 执行脚本
self.__getConn()
import mw
script = mw.readFile(filename)
result = self.__DB_CONN.executescript(script)
self.__DB_CONN.commit()
return result.rowcount
def __close(self):
# 清理条件属性
self.__OPT_WHERE = ""
self.__OPT_FIELD = "*"
self.__OPT_ORDER = ""
self.__OPT_LIMIT = ""
self.__OPT_PARAM = ()
def close(self):
# 释放资源
try:
self.__DB_CONN.close()
self.__DB_CONN = None
except:
pass
File diff suppressed because it is too large Load Diff
-485
View File
@@ -1,485 +0,0 @@
# coding: utf-8
# ---------------------------------------------------------------------------------
# MW-Linux面板
# ---------------------------------------------------------------------------------
# copyright (c) 2018-∞(https://github.com/midoks/mdserver-web) All rights reserved.
# ---------------------------------------------------------------------------------
# Author: midoks <midoks@163.com>
# ---------------------------------------------------------------------------------
# ---------------------------------------------------------------------------------
# 防火墙操作
# ---------------------------------------------------------------------------------
import psutil
import time
import os
import sys
import mw
import re
import json
import pwd
from flask import request
class firewall_api:
__isFirewalld = False
__isIptables = False
__isUfw = False
__isMac = False
def __init__(self):
iptables_file = mw.systemdCfgDir() + '/iptables.service'
if os.path.exists('/usr/sbin/firewalld'):
self.__isFirewalld = True
elif os.path.exists('/usr/sbin/ufw'):
self.__isUfw = True
elif os.path.exists(iptables_file):
self.__isIptables = True
elif mw.isAppleSystem():
self.__isMac = True
##### ----- start ----- ###
# 添加屏蔽IP
def addDropAddressApi(self):
import re
port = request.form.get('port', '').strip()
ps = request.form.get('ps', '').strip()
rep = "^\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}(\/\d{1,2})?$"
if not re.search(rep, port):
return mw.returnJson(False, '您输入的IP地址不合法!')
address = port
if mw.M('firewall').where("port=?", (address,)).count() > 0:
return mw.returnJson(False, '您要放屏蔽的IP已存在屏蔽列表,无需重复处理!')
if self.__isUfw:
mw.execShell('ufw deny from ' + address + ' to any')
else:
if self.__isIptables:
cmd = 'iptables -I INPUT -s ' + address + ' -j DROP'
mw.execShell(cmd)
elif self.__isFirewalld:
cmd = 'firewall-cmd --permanent --add-rich-rule=\'rule family=ipv4 source address="' + \
address + '" drop\''
mw.execShell(cmd)
else:
pass
msg = mw.getInfo('屏蔽IP[{1}]成功!', (address,))
mw.writeLog("防火墙管理", msg)
addtime = time.strftime('%Y-%m-%d %X', time.localtime())
mw.M('firewall').add('port,ps,addtime', (address, ps, addtime))
self.firewallReload()
return mw.returnJson(True, '添加成功!')
# 添加放行端口
def addAcceptPortApi(self):
if not self.getFwStatus():
return mw.returnJson(False, '防火墙启动时,才能添加规则!')
port = request.form.get('port', '').strip()
ps = request.form.get('ps', '').strip()
stype = request.form.get('type', '').strip()
data = self.addAcceptPortArgs(port, ps, stype)
return mw.getJson(data)
# 添加放行端口
def addAcceptPortArgs(self, port, ps, stype):
import re
import time
if not self.getFwStatus():
self.setFw(0)
rep = "^\d{1,5}(:\d{1,5})?$"
if not re.search(rep, port):
return mw.returnData(False, '端口范围不正确!')
if mw.M('firewall').where("port=?", (port,)).count() > 0:
return mw.returnData(False, '您要放行的端口已存在,无需重复放行!')
msg = mw.getInfo('放行端口[{1}]成功', (port,))
mw.writeLog("防火墙管理", msg)
addtime = time.strftime('%Y-%m-%d %X', time.localtime())
mw.M('firewall').add('port,ps,addtime', (port, ps, addtime))
self.addAcceptPort(port)
self.firewallReload()
return mw.returnData(True, '添加放行(' + port + ')端口成功!')
# 删除IP屏蔽
def delDropAddressApi(self):
if not self.getFwStatus():
return mw.returnJson(False, '防火墙启动时,才能删除规则!')
port = request.form.get('port', '').strip()
ps = request.form.get('ps', '').strip()
sid = request.form.get('id', '').strip()
address = port
if self.__isUfw:
mw.execShell('ufw delete deny from ' + address + ' to any')
elif self.__isFirewalld:
mw.execShell(
'firewall-cmd --permanent --remove-rich-rule=\'rule family=ipv4 source address="' + address + '" drop\'')
elif self.__isIptables:
cmd = 'iptables -D INPUT -s ' + address + ' -j DROP'
mw.execShell(cmd)
else:
pass
msg = mw.getInfo('解除IP[{1}]的屏蔽!', (address,))
mw.writeLog("防火墙管理", msg)
mw.M('firewall').where("id=?", (sid,)).delete()
self.firewallReload()
return mw.returnJson(True, '删除成功!')
# 删除放行端口
def delAcceptPortApi(self):
port = request.form.get('port', '').strip()
sid = request.form.get('id', '').strip()
mw_port = mw.readFile('data/port.pl')
try:
if(port == mw_port):
return mw.returnJson(False, '失败,不能删除当前面板端口!')
if self.__isUfw:
mw.execShell('ufw delete allow ' + port + '/tcp')
elif self.__isFirewalld:
port = port.replace(':', '-')
mw.execShell(
'firewall-cmd --permanent --zone=public --remove-port=' + port + '/tcp')
mw.execShell(
'firewall-cmd --permanent --zone=public --remove-port=' + port + '/udp')
elif self.__isIptables:
mw.execShell(
'iptables -D INPUT -p tcp -m state --state NEW -m tcp --dport ' + port + ' -j ACCEPT')
else:
pass
msg = mw.getInfo('删除防火墙放行端口[{1}]成功!', (port,))
mw.writeLog("防火墙管理", msg)
mw.M('firewall').where("id=?", (sid,)).delete()
self.firewallReload()
return mw.returnJson(True, '删除成功!')
except Exception as e:
return mw.returnJson(False, '删除失败!:' + str(e))
def getWwwPathApi(self):
path = mw.getLogsDir()
return mw.getJson({'path': path})
def getListApi(self):
p = request.form.get('p', '1').strip()
limit = request.form.get('limit', '10').strip()
return self.getList(int(p), int(limit))
def getLogListApi(self):
p = request.form.get('p', '1').strip()
limit = request.form.get('limit', '10').strip()
search = request.form.get('search', '').strip()
return self.getLogList(int(p), int(limit), search)
def getSshInfoApi(self):
data = {}
file = '/etc/ssh/sshd_config'
conf = mw.readFile(file)
rep = "#*Port\s+([0-9]+)\s*\n"
port = re.search(rep, conf).groups(0)[0]
isPing = True
try:
if mw.isAppleSystem():
isPing = True
else:
file = '/etc/sysctl.conf'
sys_conf = mw.readFile(file)
rep = "#*net\.ipv4\.icmp_echo_ignore_all\s*=\s*([0-9]+)"
tmp = re.search(rep, sys_conf).groups(0)[0]
if tmp == '1':
isPing = False
except:
isPing = True
# sshd 检测
status = True
cmd = "service sshd status | grep -P '(dead|stop)'|grep -v grep"
ssh_status = mw.execShell(cmd)
if ssh_status[0] != '':
status = False
cmd = "systemctl status sshd.service | grep 'dead'|grep -v grep"
ssh_status = mw.execShell(cmd)
if ssh_status[0] != '':
status = False
data['pass_prohibit_status'] = False
# 密码登陆配置检查
pass_rep = "#PasswordAuthentication\s+(\w*)\s*\n"
pass_status = re.search(pass_rep, conf)
if pass_status:
data['pass_prohibit_status'] = True
if not data['pass_prohibit_status']:
pass_rep = "PasswordAuthentication\s+(\w*)\s*\n"
pass_status = re.search(pass_rep, conf)
if pass_status and pass_status.groups(0)[0].strip() == 'no':
data['pass_prohibit_status'] = True
data['port'] = port
data['status'] = status
data['ping'] = isPing
if mw.isAppleSystem():
data['firewall_status'] = False
else:
data['firewall_status'] = self.getFwStatus()
return mw.getJson(data)
def setSshPortApi(self):
port = request.form.get('port', '1').strip()
if int(port) < 22 or int(port) > 65535:
return mw.returnJson(False, '端口范围必需在22-65535之间!')
ports = ['21', '25', '80', '443', '888']
if port in ports:
return mw.returnJson(False, '(' + port + ')' + '特殊端口不可设置!')
file = '/etc/ssh/sshd_config'
conf = mw.readFile(file)
rep = "#*Port\s+([0-9]+)\s*\n"
conf = re.sub(rep, "Port " + port + "\n", conf)
mw.writeFile(file, conf)
self.addAcceptPortArgs(port, 'SSH端口修改', 'port')
if self.__isUfw:
mw.execShell("service ssh restart")
elif self.__isIptables:
mw.execShell("/etc/init.d/sshd restart")
elif self.__isFirewalld:
mw.execShell("systemctl restart sshd.service")
else:
return mw.returnJson(False, '修改失败!')
return mw.returnJson(True, '修改成功!')
def setSshStatusApi(self):
if mw.isAppleSystem():
return mw.returnJson(True, '开发机不能操作!')
status = request.form.get('status', '1').strip()
msg = 'SSH服务已启用'
act = 'start'
if status == "1":
msg = 'SSH服务已停用'
act = 'stop'
ssh_service = mw.systemdCfgDir() + '/sshd.service'
if os.path.exists(ssh_service):
mw.execShell("systemctl " + act + " sshd.service")
else:
mw.execShell('service sshd ' + act)
if os.path.exists('/etc/init.d/sshd'):
mw.execShell('/etc/init.d/sshd ' + act)
mw.writeLog("防火墙管理", msg)
return mw.returnJson(True, '操作成功!')
def setSshPassStatusApi(self):
if mw.isAppleSystem():
return mw.returnJson(True, '开发机不能操作!')
status = request.form.get('status', '1').strip()
msg = '禁止密码登陆成功'
if status == "1":
msg = '开启密码登陆成功'
file = '/etc/ssh/sshd_config'
if not os.path.exists(file):
return mw.returnJson(False, '无法设置!')
conf = mw.readFile(file)
if status == '1':
rep = "(#)?PasswordAuthentication\s+(\w*)\s*\n"
conf = re.sub(rep, "PasswordAuthentication yes\n", conf)
else:
rep = "(#)?PasswordAuthentication\s+(\w*)\s*\n"
conf = re.sub(rep, "PasswordAuthentication no\n", conf)
mw.writeFile(file, conf)
mw.execShell("systemctl restart sshd.service")
mw.writeLog("SSH管理", msg)
return mw.returnJson(True, msg)
def setPingApi(self):
if mw.isAppleSystem():
return mw.returnJson(True, '开发机不能操作!')
status = request.form.get('status')
filename = '/etc/sysctl.conf'
conf = mw.readFile(filename)
if conf.find('net.ipv4.icmp_echo') != -1:
rep = u"net\.ipv4\.icmp_echo.*"
conf = re.sub(rep, 'net.ipv4.icmp_echo_ignore_all=' + status, conf)
else:
conf += "\nnet.ipv4.icmp_echo_ignore_all=" + status
mw.writeFile(filename, conf)
mw.execShell('sysctl -p')
return mw.returnJson(True, '设置成功!')
def setFwApi(self):
if mw.isAppleSystem():
return mw.returnJson(True, '开发机不能设置!')
status = request.form.get('status', '1')
return mw.getJson(self.setFw(status))
def setFwIptables(self, status):
# iptables特殊处理
if status == '1':
mw.execShell('service iptables save')
mw.execShell('service iptables stop')
else:
# 重新导入数据
_list = mw.M('firewall').field('id,port,ps,addtime').limit(
'0,1000').order('id desc').select()
mw.execShell('iptables -P INPUT DROP')
mw.execShell('iptables -P OUTPUT ACCEPT')
for x in _list:
port = x['port']
if mw.isIpAddr(port):
cmd = 'iptables -I INPUT -s ' + port + ' -j DROP'
mw.execShell(cmd)
else:
self.addAcceptPort(port)
mw.execShell('service iptables save')
mw.execShell('service iptables start')
def setFw(self, status):
if self.__isIptables:
self.setFwIptables(status)
return mw.returnData(True, '设置成功!')
if status == '1':
if self.__isUfw:
mw.execShell('/usr/sbin/ufw disable')
elif self.__isFirewalld:
mw.execShell('systemctl stop firewalld.service')
mw.execShell('systemctl disable firewalld.service')
else:
pass
else:
if self.__isUfw:
mw.execShell("echo 'y'| ufw enable")
elif self.__isFirewalld:
mw.execShell('systemctl start firewalld.service')
mw.execShell('systemctl enable firewalld.service')
else:
pass
return mw.returnData(True, '设置成功!')
def delPanelLogsApi(self):
mw.M('logs').where('id>?', (0,)).delete()
mw.writeLog('面板设置', '面板操作日志已清空!')
return mw.returnJson(True, '面板操作日志已清空!')
##### ----- start ----- ###
def getList(self, page, limit):
start = (page - 1) * limit
_list = mw.M('firewall').field('id,port,ps,addtime').limit(
str(start) + ',' + str(limit)).order('id desc').select()
data = {}
data['data'] = _list
count = mw.M('firewall').count()
_page = {}
_page['count'] = count
_page['tojs'] = 'showAccept'
_page['p'] = page
data['page'] = mw.getPage(_page)
return mw.getJson(data)
def getLogList(self, page, limit, search=''):
find_search = ''
if search != '':
find_search = "type like '%" + search + "%' or log like '%" + \
search + "%' or addtime like '%" + search + "%'"
start = (page - 1) * limit
_list = mw.M('logs').where(find_search, ()).field(
'id,type,log,addtime').limit(str(start) + ',' + str(limit)).order('id desc').select()
data = {}
data['data'] = _list
count = mw.M('logs').where(find_search, ()).count()
_page = {}
_page['count'] = count
_page['tojs'] = 'getLogs'
_page['p'] = page
data['page'] = mw.getPage(_page)
return mw.getJson(data)
def addAcceptPort(self, port):
if self.__isUfw:
mw.execShell('ufw allow ' + port + '/tcp')
elif self.__isFirewalld:
port = port.replace(':', '-')
cmd = 'firewall-cmd --permanent --zone=public --add-port=' + port + '/tcp'
mw.execShell(cmd)
elif self.__isIptables:
cmd = 'iptables -I INPUT -p tcp -m state --state NEW -m tcp --dport ' + port + ' -j ACCEPT'
mw.execShell(cmd)
else:
pass
return True
def firewallReload(self):
if self.__isUfw:
mw.execShell('/usr/sbin/ufw reload')
return
elif self.__isIptables:
mw.execShell('service iptables save')
mw.execShell('service iptables restart')
elif self.__isFirewalld:
mw.execShell('firewall-cmd --reload')
else:
pass
def getFwStatus(self):
if self.__isUfw:
cmd = "/usr/sbin/ufw status| grep Status | awk -F ':' '{print $2}'"
data = mw.execShell(cmd)
if data[0].strip() == 'inactive':
return False
return True
elif self.__isIptables:
cmd = "systemctl status iptables | grep 'inactive'"
data = mw.execShell(cmd)
if data[0] != '':
return False
return True
elif self.__isFirewalld:
cmd = "ps -ef|grep firewalld |grep -v grep | awk '{print $2}'"
data = mw.execShell(cmd)
if data[0] == '':
return False
return True
else:
return False
-1947
View File
File diff suppressed because it is too large Load Diff
-237
View File
@@ -1,237 +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 math
import string
class Page():
#--------------------------
# 分页类 - JS回调版
#--------------------------
__PREV = '上一页'
__NEXT = '下一页'
__START = '首页'
__END = '尾页'
__COUNT_START = '共'
__COUNT_END = '条数据'
__FO = '从'
__LINE = '条'
__LIST_NUM = 4
SHIFT = None # 偏移量
ROW = None # 每页行数
__C_PAGE = None # 当前页
__COUNT_PAGE = None # 总页数
__COUNT_ROW = None # 总行数
__URI = None # URI
__RTURN_JS = False # 是否返回JS回调
__START_NUM = None # 起始行
__END_NUM = None # 结束行
def __init__(self):
if False:
self.__PREV = tmp['PREV']
self.__NEXT = tmp['NEXT']
self.__START = tmp['START']
self.__END = tmp['END']
self.__COUNT_START = tmp['COUNT_START']
self.__COUNT_END = tmp['COUNT_END']
self.__FO = tmp['FO']
self.__LINE = tmp['LINE']
def GetPage(self, pageInfo, limit='1,2,3,4,5,6,7,8'):
# 取分页信息
# @param pageInfo 传入分页参数字典
# @param limit 返回系列
self.__RTURN_JS = pageInfo['return_js']
self.__COUNT_ROW = pageInfo['count']
self.ROW = pageInfo['row']
self.__C_PAGE = self.__GetCpage(pageInfo['p'])
self.__START_NUM = self.__StartRow()
self.__END_NUM = self.__EndRow()
self.__COUNT_PAGE = self.__GetCountPage()
self.__URI = self.__SetUri(pageInfo['uri'])
self.SHIFT = self.__START_NUM - 1
keys = limit.split(',')
pages = {}
# 起始页
pages['1'] = self.__GetStart()
# 上一页
pages['2'] = self.__GetPrev()
# 分页
pages['3'] = self.__GetPages()
# 下一页
pages['4'] = self.__GetNext()
# 尾页
pages['5'] = self.__GetEnd()
# 当前显示页与总页数
pages['6'] = "<span class='Pnumber'>" + \
str(self.__C_PAGE) + "/" + \
str(self.__COUNT_PAGE) + "</span>"
# 本页显示开始与结束行
pages['7'] = "<span class='Pline'>" + str(self.__FO) + \
str(self.__START_NUM) + "-" + \
str(self.__END_NUM) + str(self.__LINE) + "</span>"
# 行数
pages['8'] = "<span class='Pcount'>" + str(self.__COUNT_START) + \
str(self.__COUNT_ROW) + str(self.__COUNT_END) + "</span>"
# 构造返回数据
retuls = '<div>'
for value in keys:
retuls += pages[value]
retuls += '</div>'
# 返回分页数据
return retuls
def __GetEnd(self):
# 构造尾页
endStr = ""
if self.__C_PAGE >= self.__COUNT_PAGE:
endStr = ''
else:
if self.__RTURN_JS == "":
endStr = "<a class='Pend' href='" + self.__URI + "p=" + \
str(self.__COUNT_PAGE) + "'>" + str(self.__END) + "</a>"
else:
endStr = "<a class='Pend' onclick='" + self.__RTURN_JS + \
"(" + str(self.__COUNT_PAGE) + ")'>" + \
str(self.__END) + "</a>"
return endStr
def __GetNext(self):
# 构造下一页
nextStr = ""
if self.__C_PAGE >= self.__COUNT_PAGE:
nextStr = ''
else:
if self.__RTURN_JS == "":
nextStr = "<a class='Pnext' href='" + self.__URI + "p=" + \
str(self.__C_PAGE + 1) + "'>" + str(self.__NEXT) + "</a>"
else:
nextStr = "<a class='Pnext' onclick='" + self.__RTURN_JS + \
"(" + str(self.__C_PAGE + 1) + ")'>" + \
str(self.__NEXT) + "</a>"
return nextStr
def __GetPages(self):
# 构造分页
pages = ''
num = 0
# 当前页之前
if (self.__COUNT_PAGE - self.__C_PAGE) < self.__LIST_NUM:
num = self.__LIST_NUM + \
(self.__LIST_NUM - (self.__COUNT_PAGE - self.__C_PAGE))
else:
num = self.__LIST_NUM
n = 0
for i in range(num):
n = num - i
page = self.__C_PAGE - n
if page > 0:
if self.__RTURN_JS == "":
pages += "<a class='Pnum' href='" + self.__URI + \
"p=" + str(page) + "'>" + str(page) + "</a>"
else:
pages += "<a class='Pnum' onclick='" + self.__RTURN_JS + \
"(" + str(page) + ")'>" + str(page) + "</a>"
# 当前页
if self.__C_PAGE > 0:
pages += "<span class='Pcurrent'>" + \
str(self.__C_PAGE) + "</span>"
# 当前页之后
if self.__C_PAGE <= self.__LIST_NUM:
num = self.__LIST_NUM + (self.__LIST_NUM - self.__C_PAGE) + 1
else:
num = self.__LIST_NUM
for i in range(num):
if i == 0:
continue
page = self.__C_PAGE + i
if page > self.__COUNT_PAGE:
break
if self.__RTURN_JS == "":
pages += "<a class='Pnum' href='" + self.__URI + \
"p=" + str(page) + "'>" + str(page) + "</a>"
else:
pages += "<a class='Pnum' onclick='" + self.__RTURN_JS + \
"(" + str(page) + ")'>" + str(page) + "</a>"
return pages
def __GetPrev(self):
# 构造上一页
startStr = ''
if self.__C_PAGE == 1:
startStr = ''
else:
if self.__RTURN_JS == "":
startStr = "<a class='Ppren' href='" + self.__URI + "p=" + \
str(self.__C_PAGE - 1) + "'>" + str(self.__PREV) + "</a>"
else:
startStr = "<a class='Ppren' onclick='" + self.__RTURN_JS + \
"(" + str(self.__C_PAGE - 1) + ")'>" + \
str(self.__PREV) + "</a>"
return startStr
def __GetStart(self):
# 构造起始分页
startStr = ''
if self.__C_PAGE == 1:
startStr = ''
else:
if self.__RTURN_JS == "":
startStr = "<a class='Pstart' href='" + \
self.__URI + "p=1'>" + str(self.__START) + "</a>"
else:
startStr = "<a class='Pstart' onclick='" + \
self.__RTURN_JS + "(1)'>" + str(self.__START) + "</a>"
return startStr
def __GetCpage(self, p):
# 取当前页
if p:
return p
return 1
def __StartRow(self):
# 从多少行开始
return (self.__C_PAGE - 1) * self.ROW + 1
def __EndRow(self):
# 从多少行结束
if self.ROW > self.__COUNT_ROW:
return self.__COUNT_ROW
return self.__C_PAGE * self.ROW
def __GetCountPage(self):
# 取总页数
return int(math.ceil(self.__COUNT_ROW / float(self.ROW)))
def __SetUri(self, sinput):
# 构造URI
uri = '?'
for key in sinput:
if key == 'p':
continue
uri += key + '=' + sinput[key] + '&'
return str(uri)
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
-456
View File
@@ -1,456 +0,0 @@
# coding: utf-8
# ---------------------------------------------------------------------------------
# MW-Linux面板
# ---------------------------------------------------------------------------------
# copyright (c) 2018-∞(https://github.com/midoks/mdserver-web) All rights reserved.
# ---------------------------------------------------------------------------------
# Author: midoks <midoks@163.com>
# ---------------------------------------------------------------------------------
# ---------------------------------------------------------------------------------
# SSH终端操作
# ---------------------------------------------------------------------------------
import json
import time
import os
import sys
import socket
import threading
import re
from io import BytesIO, StringIO
import mw
import paramiko
from flask_socketio import SocketIO, emit, send
class ssh_terminal:
__debug_file = 'logs/terminal.log'
__log_type = 'SSH终端'
# websocketio 唯一标识
__sid = ''
__host = None
__type = '0'
__port = 22
__user = None
__pass = None
__pkey = None
__key_passwd = None
__rep_ssh_config = False
__rep_ssh_service = False
__sshd_config_backup = None
__ssh = None
__tp = None
__ps = None
__ssh_list = {}
__ssh_last_request_time = {}
def __init__(self):
ht = threading.Thread(target=self.heartbeat)
ht.start()
def debug(self, msg):
msg = "{} - {}:{} => {} \n".format(mw.formatDate(),
self.__host, self.__port, msg)
if not mw.isDebugMode():
return
mw.writeFile(self.__debug_file, msg, 'a+')
def returnMsg(self, status, msg):
return {'status': status, 'msg': msg}
def restartSsh(self, act='reload'):
'''
重启ssh 无参数传递
'''
version = mw.readFile('/etc/redhat-release')
if not os.path.exists('/etc/redhat-release'):
mw.execShell('service ssh ' + act)
elif version.find(' 7.') != -1 or version.find(' 8.') != -1:
mw.execShell("systemctl " + act + " sshd.service")
else:
mw.execShell("/etc/init.d/sshd " + act)
def isRunning(self, rep=False):
try:
if rep and self.__rep_ssh_service:
self.restartSsh('stop')
return True
status = self.getSshStatus()
if not status:
self.restartSsh('start')
self.__rep_ssh_service = True
return True
return False
except:
return False
def setSshdConfig(self, rep=False):
self.isRunning(rep)
if rep and not self.__rep_ssh_config:
return False
try:
sshd_config_file = '/etc/ssh/sshd_config'
if not os.path.exists(sshd_config_file):
return False
sshd_config = mw.readFile(sshd_config_file)
if not sshd_config:
return False
if rep:
if self.__sshd_config_backup:
mw.writeFile(sshd_config_file, self.__sshd_config_backup)
self.restartSsh()
return True
pin = r'^\s*PubkeyAuthentication\s+(yes|no)'
pubkey_status = re.findall(pin, sshd_config, re.I)
if pubkey_status:
if pubkey_status[0] == 'yes':
pubkey_status = True
else:
pubkey_status = False
pin = r'^\s*RSAAuthentication\s+(yes|no)'
rsa_status = re.findall(pin, sshd_config, re.I)
if rsa_status:
if rsa_status[0] == 'yes':
rsa_status = True
else:
rsa_status = False
self._sshd_config_backup = sshd_config
is_write = False
if not pubkey_status:
sshd_config = re.sub(
r'\n#?PubkeyAuthentication\s\w+', '\nPubkeyAuthentication yes', sshd_config)
is_write = True
if not rsa_status:
sshd_config = re.sub(
r'\n#?RSAAuthentication\s\w+', '\nRSAAuthentication yes', sshd_config)
is_write = True
if is_write:
mw.writeFile(sshd_config_file, sshd_config)
self.__rep_ssh_config = True
self.restartSsh()
else:
self.__sshd_config_backup = None
return True
except:
return False
def setSid(self, sid):
self.__sid = sid
def connect(self, sid):
# self.connectBySocket()
if self.__host in ['127.0.0.1', 'localhost']:
return self.connectLocalSsh(sid)
else:
return self.connectBySocket(sid)
def connectLocalSsh(self, sid):
mw.createSshInfo()
self.__ps = paramiko.SSHClient()
self.__ps.set_missing_host_key_policy(paramiko.AutoAddPolicy())
self.__port = mw.getSSHPort()
try:
self.__ps.connect(self.__host, self.__port, timeout=60)
except Exception as e:
self.__ps.connect('127.0.0.1', self.__port)
except Exception as e:
self.__ps.connect('localhost', self.__port)
except Exception as e:
self.setSshdConfig(True)
self.__ps.close()
e = str(e)
if e.find('websocket error!') != -1:
return self.returnMsg(True, '连接成功')
if e.find('Authentication timeout') != -1:
self.debug("认证超时{}".format(e))
return self.returnMsg(False, '认证超时,请按回车重试!{}'.format(e))
if e.find('Connection reset by peer') != -1:
self.debug('目标服务器主动拒绝连接')
return self.returnMsg(False, '目标服务器主动拒绝连接')
if e.find('Error reading SSH protocol banner') != -1:
self.debug('协议头响应超时')
return self.returnMsg(False, '协议头响应超时,与目标服务器之间的网络质量太糟糕:' + e)
if not e:
self.debug('SSH协议握手超时')
return self.returnMsg(False, "SSH协议握手超时,与目标服务器之间的网络质量太糟糕")
err = mw.getTracebackInfo()
self.debug(err)
return self.returnMsg(False, "未知错误: {}".format(err))
self.debug('local-ssh:认证成功,正在构建会话通道')
ssh = self.__ps.invoke_shell(
term='xterm', width=83, height=21)
ssh.setblocking(0)
self.__ssh_list[sid] = ssh
mw.writeLog(self.__log_type, '成功登录到SSH服务器 [{}:{}]'.format(
self.__host, self.__port))
self.debug('local-ssh:通道已构建')
return self.returnMsg(True, '连接成功!')
def connectBySocket(self, sid):
if not self.__host:
return self.returnMsg(False, '错误的连接地址')
if not self.__user:
self.__user = 'root'
if not self.__port:
self.__port = 22
self.setSshdConfig(True)
num = 0
while num < 5:
num += 1
try:
self.debug('正在尝试第{}次连接'.format(num))
if self.__rep_ssh_config:
time.sleep(0.1)
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.settimeout(2 + num)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_SNDBUF, 8192)
sock.connect((self.__host, self.__port))
break
except Exception as e:
if num == 5:
self.setSshdConfig(True)
self.debug('重试连接失败,{}'.format(e))
return self.returnMsg(False, '连接目标服务器失败, {}:{}'.format(self.__host, self.__port))
else:
time.sleep(0.2)
# print(self.__host, sock)
self.__tp = paramiko.Transport(sock)
try:
self.__tp.start_client()
self.__tp.banner_timeout = 60
if not self.__pass and not self.__pkey:
return self.returnMsg(False, '密码或私钥不能都为空: {}:{}'.format(self.__host, self.__port))
if self.__pkey != '' and self.__type != '0':
self.debug('正在认证私钥')
p_file = StringIO(str(self.__pkey.replace('\\n', '\n')))
# p_file = "/tmp/t_ssh_pkey.txt"
# mw.writeFile(p_file, self.__pkey.replace('\\n', '\n'))
# mw.execShell('chmod 600 ' + p_file)
try:
p_file.seek(0)
pkey = paramiko.RSAKey.from_private_key(p_file)
except:
try:
p_file.seek(0) # 重置游标
pkey = paramiko.Ed25519Key.from_private_key(
p_file)
except:
try:
p_file.seek(0)
pkey = paramiko.ECDSAKey.from_private_key(p_file)
except:
p_file.seek(0)
pkey = paramiko.DSSKey.from_private_key(p_file)
self.__tp.auth_publickey(username=self.__user, key=pkey)
else:
try:
self.__tp.auth_none(self.__user)
except Exception as e:
e = str(e)
if e.find('keyboard-interactive') >= 0:
self._auth_interactive()
else:
self.debug('正在认证密码')
self.__tp.auth_password(
username=self.__user, password=self.__pass)
except Exception as e:
self.setSshdConfig(True)
self.__tp.close()
e = str(e)
# print(e)
if e.find('Authentication timeout') != -1:
self.debug("认证超时{}".format(e))
return self.returnMsg(False, '认证超时,请按回车重试!{}'.format(e))
if e.find('Authentication failed') != -1:
self.debug('认证失败{}'.format(e))
return self.returnMsg(False, '帐号或密码错误: {}'.format(e + "," + self.__user + "@" + self.__host + ":" + str(self.__port)))
if e.find('Bad authentication type; allowed types') != -1:
self.debug('认证失败{}'.format(e))
if self.__host in ['127.0.0.1', 'localhost'] and self.__pass == 'none':
return self.returnMsg(False, '帐号或密码错误: {}'.format("Authentication failed ," + self.__user + "@" + self.__host + ":" + str(self.__port)))
return self.returnMsg(False, '不支持的身份验证类型: {}'.format(e))
if e.find('Connection reset by peer') != -1:
self.debug('目标服务器主动拒绝连接')
return self.returnMsg(False, '目标服务器主动拒绝连接')
if e.find('Error reading SSH protocol banner') != -1:
self.debug('协议头响应超时')
return self.returnMsg(False, '协议头响应超时,与目标服务器之间的网络质量太糟糕:' + e)
if not e:
self.debug('SSH协议握手超时')
return self.returnMsg(False, "SSH协议握手超时,与目标服务器之间的网络质量太糟糕")
err = mw.getTracebackInfo()
self.debug(err)
return self.returnMsg(False, "未知错误: {}".format(err))
self.debug('认证成功,正在构建会话通道')
ssh = self.__tp.open_session()
ssh.get_pty(term='xterm', width=100, height=34)
ssh.invoke_shell()
self.__ssh_list[sid] = ssh
mw.writeLog(self.__log_type, '成功登录到SSH服务器 [{}:{}]'.format(
self.__host, self.__port))
self.debug('通道已构建')
return self.returnMsg(True, '连接成功.')
def getSshInfo(self, file):
rdata = mw.readFile(file)
destr = mw.enDoubleCrypt('mdserver-web', rdata)
return json.loads(destr)
def setAttr(self, sid, info):
self.__host = info['host'].strip()
# 外部连接获取
if not self.__host in ['127.0.0.1', 'localhost']:
dst_info = mw.getServerDir() + '/webssh/host/' + self.__host + '/info.json'
if os.path.exists(dst_info):
info = self.getSshInfo(dst_info)
if 'type' in info:
self.__type = info['type']
if 'port' in info:
self.__port = int(info['port'])
if 'username' in info:
self.__user = info['username']
if 'pkey' in info:
self.__pkey = info['pkey']
if 'password' in info:
self.__pass = info['password']
if 'pkey_passwd' in info:
self.__key_passwd = info['pkey_passwd']
# print(self.__host, self.__pass, self.__key_passwd)
try:
result = self.connect(sid)
# print(result)
except Exception as ex:
if str(ex).find("NoneType") == -1:
raise ex
return result
def send(self):
pass
def close(self):
try:
if self.__ssh:
self.__ssh.close()
if self.__tp: # 关闭宿主服务
self.__tp.close()
if self.__ps:
self.__ps.close()
except:
pass
def resize(self, sid, data):
try:
self.__ssh_list[sid].resize_pty(
width=data['cols'], height=data['rows'])
return True
except:
return False
def wsSend(self, recv):
try:
t = recv.decode("utf-8")
return emit('server_response', {'data': t})
except Exception as e:
return emit('server_response', {'data': recv})
def wsSendConnect(self):
return emit('connect', {'data': 'ok'})
def wsSendReConnect(self):
return emit('reconnect', {'data': 'ok'})
def heartbeat(self):
# limit_cos = 10
while True:
time.sleep(3)
cur_time = time.time()
for x in list(self.__ssh_list.keys()):
ssh_last_time = self.__ssh_last_request_time[x]
sid_off_cos = cur_time - ssh_last_time
# print("heartbeat off cos :", x, sid_off_cos)
if sid_off_cos > 3:
cur_ssh = self.__ssh_list[x]
if not cur_ssh:
del(self.__ssh_list[x])
del(self.__ssh_last_request_time[x])
continue
if cur_ssh.exit_status_ready():
del(self.__ssh_list[x])
del(self.__ssh_last_request_time[x])
continue
cur_ssh.send("exit\r\n")
del(self.__ssh_list[x])
del(self.__ssh_last_request_time[x])
def run(self, sid, info):
self.__sid = sid
if not self.__sid:
return self.wsSend('WebSocketIO无效')
self.__ssh_last_request_time[sid] = time.time()
if not sid in self.__ssh_list:
if type(info) == dict and 'host' in info:
result = self.setAttr(sid, info)
if result['status']:
return self.wsSendConnect()
else:
return self.wsSend(result['msg'])
else:
return self.wsSendReConnect()
result = self.returnMsg(False, '')
if sid in self.__ssh_list:
if 'resize' in info:
self.resize(sid, info)
result = self.returnMsg(True, '已连接')
if result['status']:
if type(info) == str:
time.sleep(0.1)
cur_ssh = self.__ssh_list[sid]
if cur_ssh.exit_status_ready():
self.wsSend("logout\r\n")
del(self.__ssh_list[sid])
return
cur_ssh.send(info)
try:
time.sleep(0.005)
recv = cur_ssh.recv(8192)
return self.wsSend(recv)
except Exception as ex:
return self.wsSend('')
else:
return self.wsSend(result['msg'])
-791
View File
@@ -1,791 +0,0 @@
# coding: utf-8
# ---------------------------------------------------------------------------------
# MW-Linux面板
# ---------------------------------------------------------------------------------
# copyright (c) 2018-∞(https://github.com/midoks/mdserver-web) All rights reserved.
# ---------------------------------------------------------------------------------
# Author: midoks <midoks@163.com>
# ---------------------------------------------------------------------------------
# ---------------------------------------------------------------------------------
# 系统信息操作
# ---------------------------------------------------------------------------------
import psutil
import time
import os
import re
import math
import sys
import json
from flask import Flask, session
from flask import request
import db
import mw
import config_api
import requests
from threading import Thread
from time import sleep
def mw_async(f):
def wrapper(*args, **kwargs):
thr = Thread(target=f, args=args, kwargs=kwargs)
thr.start()
return wrapper
class system_api:
setupPath = None
pids = None
def __init__(self):
self.setupPath = mw.getServerDir()
##### ----- start ----- ###
def networkApi(self):
data = self.getNetWork()
return mw.getJson(data)
def updateServerApi(self):
stype = request.args.get('type', 'check')
version = request.args.get('version', '')
return self.updateServer(stype, version)
def systemTotalApi(self):
data = self.getSystemTotal()
return mw.getJson(data)
def diskInfoApi(self):
diskInfo = self.getDiskInfo()
return mw.getJson(diskInfo)
def setControlApi(self):
stype = request.form.get('type', '')
day = request.form.get('day', '')
data = self.setControl(stype, day)
return data
def getLoadAverageApi(self):
start = request.args.get('start', '')
end = request.args.get('end', '')
data = self.getLoadAverageData(start, end)
return mw.getJson(data)
def getCpuIoApi(self):
start = request.args.get('start', '')
end = request.args.get('end', '')
data = self.getCpuIoData(start, end)
return mw.getJson(data)
def getDiskIoApi(self):
start = request.args.get('start', '')
end = request.args.get('end', '')
data = self.getDiskIoData(start, end)
return mw.getJson(data)
def getNetworkIoApi(self):
start = request.args.get('start', '')
end = request.args.get('end', '')
data = self.getNetWorkIoData(start, end)
return mw.getJson(data)
def rememoryApi(self):
os.system('sync')
scriptFile = mw.getRunDir() + '/script/rememory.sh'
mw.execShell("/bin/bash " + scriptFile)
data = self.getMemInfo()
return mw.getJson(data)
# 重启面板
def restartApi(self):
self.restartMw()
return mw.returnJson(True, '面板已重启!')
def restartServerApi(self):
if mw.isAppleSystem():
return mw.returnJson(False, "开发环境不可重起")
self.restartServer()
return mw.returnJson(True, '正在重启服务器!')
##### ----- end ----- ###
def restartTask(self):
initd = mw.getRunDir() + '/scripts/init.d/mw'
if os.path.exists(initd):
os.system(initd + ' ' + 'restart_task')
return True
def restartMw(self):
mw.writeFile('data/restart.pl', 'True')
return True
@mw_async
def restartServer(self):
if not mw.isRestart():
return mw.returnJson(False, '请等待所有安装任务完成再执行!')
mw.execShell("sync && init 6 &")
return mw.returnJson(True, '命令发送成功!')
# 名取PID
def getPid(self, pname):
try:
if not self.pids:
self.pids = psutil.pids()
for pid in self.pids:
if psutil.Process(pid).name() == pname:
return True
return False
except:
return False
# 检查端口是否占用
def isOpen(self, port):
import socket
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
try:
s.connect(('127.0.0.1', int(port)))
s.shutdown(2)
return True
except:
return False
# 检测指定进程是否存活
def checkProcess(self, pid):
try:
if not self.pids:
self.pids = psutil.pids()
if int(pid) in self.pids:
return True
return False
except:
return False
def getEnvInfoApi(self, get=None):
serverInfo = {}
serverInfo['status'] = True
sdir = mw.getServerDir()
serverInfo['webserver'] = '未安装'
if os.path.exists(sdir + '/openresty/nginx/sbin/nginx'):
serverInfo['webserver'] = 'OpenResty'
serverInfo['php'] = []
phpversions = ['52', '53', '54', '55', '56', '70', '71',
'72', '73', '74', '80', '81', '82', '83', '84']
phpPath = sdir + '/php/'
for pv in phpversions:
if not os.path.exists(phpPath + pv + '/bin/php'):
continue
serverInfo['php'].append(pv)
serverInfo['mysql'] = False
if os.path.exists(sdir + '/mysql/bin/mysql'):
serverInfo['mysql'] = True
import psutil
try:
diskInfo = psutil.disk_usage('/www')
except:
diskInfo = psutil.disk_usage('/')
serverInfo['disk'] = diskInfo[2]
return mw.returnJson(True, 'ok', serverInfo)
def getPanelInfo(self, get=None):
# 取面板配置
address = mw.GetLocalIp()
try:
try:
port = web.ctx.host.split(':')[1]
except:
port = mw.readFile('data/port.pl')
except:
port = '7200'
domain = ''
if os.path.exists('data/domain.conf'):
domain = mw.readFile('data/domain.conf')
autoUpdate = ''
if os.path.exists('data/autoUpdate.pl'):
autoUpdate = 'checked'
limitip = ''
if os.path.exists('data/limitip.conf'):
limitip = mw.readFile('data/limitip.conf')
templates = []
for template in os.listdir('templates/'):
if os.path.isdir('templates/' + template):
templates.append(template)
template = mw.readFile('data/templates.pl')
check502 = ''
if os.path.exists('data/502Task.pl'):
check502 = 'checked'
return {'port': port, 'address': address, 'domain': domain, 'auto': autoUpdate, '502': check502, 'limitip': limitip, 'templates': templates, 'template': template}
def getSystemTotal(self, interval=1):
# 取系统统计信息
data = self.getMemInfo()
cpu = self.getCpuInfo(interval)
data['cpuNum'] = cpu[1]
data['cpuRealUsed'] = cpu[0]
data['time'] = self.getBootTime()
data['system'] = self.getSystemVersion()
data['isuser'] = mw.M('users').where(
'username=?', ('admin',)).count()
data['version'] = '0.0.1'
return data
def getLoadAverage(self):
c = os.getloadavg()
data = {}
data['one'] = round(float(c[0]), 2)
data['five'] = round(float(c[1]), 2)
data['fifteen'] = round(float(c[2]), 2)
data['max'] = psutil.cpu_count() * 2
data['limit'] = data['max']
data['safe'] = data['max'] * 0.75
return data
def getAllInfo(self, get):
data = {}
data['load_average'] = self.GetLoadAverage(get)
data['title'] = self.GetTitle()
data['network'] = self.GetNetWorkApi(get)
data['panel_status'] = not os.path.exists(
'/www/server/mdserver-web/data/close.pl')
import firewalls
ssh_info = firewalls.firewalls().GetSshInfo(None)
data['enable_ssh_status'] = ssh_info['status']
data['disable_ping_status'] = not ssh_info['ping']
data['time'] = self.GetBootTime()
# data['system'] = self.GetSystemVersion();
# data['mem'] = self.GetMemInfo();
data['version'] = web.ctx.session.version
return data
def getTitle(self):
titlePl = 'data/title.pl'
title = 'Linux面板'
if os.path.exists(titlePl):
title = mw.readFile(titlePl).strip()
return title
def getSystemVersion(self):
# 取操作系统版本
if mw.getOs() == 'darwin':
data = mw.execShell('sw_vers')[0]
data_list = data.strip().split("\n")
mac_version = ''
for x in data_list:
mac_version += x.split("\t")[1] + ' '
return mac_version
redhat_series = '/etc/redhat-release'
if os.path.exists(redhat_series):
version = mw.readFile('/etc/redhat-release')
version = version.replace('release ', '').strip()
return version
os_series = '/etc/os-release'
if os.path.exists(os_series):
version = mw.execShell(
"cat /etc/*-release | grep PRETTY_NAME | awk -F = '{print $2}' | awk -F '\"' '{print $2}'")
return version[0].strip()
return '未识别系统信息'
def getBootTime(self):
# 取系统启动时间
uptime = mw.readFile('/proc/uptime')
if uptime == False:
start_time = psutil.boot_time()
run_time = time.time() - start_time
else:
run_time = uptime.split()[0]
tStr = float(run_time)
min = tStr / 60
hours = min / 60
days = math.floor(hours / 24)
hours = math.floor(hours - (days * 24))
min = math.floor(min - (days * 60 * 24) - (hours * 60))
return mw.getInfo('已不间断运行: {1}天{2}小时{3}分钟', (str(int(days)), str(int(hours)), str(int(min))))
def getCpuInfo(self, interval=1):
# 取CPU信息
cpuCount = psutil.cpu_count()
cpuLogicalNum = psutil.cpu_count(logical=False)
used = psutil.cpu_percent(interval=interval)
if os.path.exists('/proc/cpuinfo'):
c_tmp = mw.readFile('/proc/cpuinfo')
d_tmp = re.findall("physical id.+", c_tmp)
cpuLogicalNum = len(set(d_tmp))
used_all = psutil.cpu_percent(percpu=True)
cpu_name = mw.getCpuType() + " * {}".format(cpuLogicalNum)
return used, cpuCount, used_all, cpu_name, cpuCount, cpuLogicalNum
def getMemInfo(self):
# 取内存信息
mem = psutil.virtual_memory()
if mw.getOs() == 'darwin':
memInfo = {
'memTotal': mem.total / 1024 / 1024
}
memInfo['memRealUsed'] = memInfo['memTotal'] * (mem.percent / 100)
else:
memInfo = {
'memTotal': mem.total / 1024 / 1024,
'memFree': mem.free / 1024 / 1024,
'memBuffers': mem.buffers / 1024 / 1024,
'memCached': mem.cached / 1024 / 1024
}
memInfo['memRealUsed'] = memInfo['memTotal'] - \
memInfo['memFree'] - memInfo['memBuffers'] - \
memInfo['memCached']
return memInfo
def getMemUsed(self):
# 取内存使用率
try:
import psutil
mem = psutil.virtual_memory()
if mw.getOs() == 'darwin':
return mem.percent
memInfo = {'memTotal': mem.total / 1024 / 1024, 'memFree': mem.free / 1024 / 1024,
'memBuffers': mem.buffers / 1024 / 1024, 'memCached': mem.cached / 1024 / 1024}
tmp = memInfo['memTotal'] - memInfo['memFree'] - \
memInfo['memBuffers'] - memInfo['memCached']
tmp1 = memInfo['memTotal'] / 100
return (tmp / tmp1)
except Exception as ex:
return 1
def getDiskInfo(self):
info = self.getDiskInfo2()
if len(info) != 0:
return info
# 取磁盘分区信息
diskIo = psutil.disk_partitions()
diskInfo = []
for disk in diskIo:
if disk[1] == '/mnt/cdrom':
continue
if disk[1] == '/boot':
continue
tmp = {}
tmp['path'] = disk[1]
size_tmp = psutil.disk_usage(disk[1])
tmp['size'] = [mw.toSize(size_tmp[0]), mw.toSize(
size_tmp[1]), mw.toSize(size_tmp[2]), str(size_tmp[3]) + '%']
diskInfo.append(tmp)
return diskInfo
def getDiskInfo2(self):
# 取磁盘分区信息
temp = mw.execShell(
"df -h -P|grep '/'|grep -v tmpfs | grep -v devfs")[0]
tempInodes = mw.execShell(
"df -i -P|grep '/'|grep -v tmpfs | grep -v devfs")[0]
temp1 = temp.split('\n')
tempInodes1 = tempInodes.split('\n')
diskInfo = []
n = 0
cuts = ['/mnt/cdrom', '/boot', '/boot/efi', '/dev',
'/dev/shm', '/zroot', '/run/lock', '/run', '/run/shm', '/run/user']
for tmp in temp1:
n += 1
inodes = tempInodes1[n - 1].split()
disk = tmp.split()
if len(disk) < 5:
continue
if disk[1].find('M') != -1:
continue
if disk[1].find('K') != -1:
continue
if len(disk[5].split('/')) > 4:
continue
if disk[5] in cuts:
continue
arr = {}
arr['path'] = disk[5]
tmp1 = [disk[1], disk[2], disk[3], disk[4]]
arr['size'] = tmp1
arr['inodes'] = [inodes[1], inodes[2], inodes[3], inodes[4]]
diskInfo.append(arr)
return diskInfo
# 清理系统垃圾
def clearSystem(self, get):
count = total = 0
tmp_total, tmp_count = self.ClearMail()
count += tmp_count
total += tmp_total
tmp_total, tmp_count = self.ClearOther()
count += tmp_count
total += tmp_total
return count, total
# 清理邮件日志
def clearMail(self):
rpath = '/var/spool'
total = count = 0
import shutil
con = ['cron', 'anacron', 'mail']
for d in os.listdir(rpath):
if d in con:
continue
dpath = rpath + '/' + d
time.sleep(0.2)
num = size = 0
for n in os.listdir(dpath):
filename = dpath + '/' + n
fsize = os.path.getsize(filename)
size += fsize
if os.path.isdir(filename):
shutil.rmtree(filename)
else:
os.remove(filename)
print('mail clear ok')
num += 1
total += size
count += num
return total, count
# 清理其它
def clearOther(self):
clearPath = [
{'path': '/www/server/mdserver-web', 'find': 'testDisk_'},
{'path': '/www/wwwlogs', 'find': 'log'},
{'path': '/tmp', 'find': 'panelBoot.pl'},
{'path': '/www/server/mdserver-web/install', 'find': '.rpm'}
]
total = count = 0
for c in clearPath:
for d in os.listdir(c['path']):
if d.find(c['find']) == -1:
continue
filename = c['path'] + '/' + d
fsize = os.path.getsize(filename)
total += fsize
if os.path.isdir(filename):
shutil.rmtree(filename)
else:
os.remove(filename)
count += 1
mw.restartWeb()
os.system('echo > /tmp/panelBoot.pl')
return total, count
def psutilNetIoCounters(self):
'''
统计网卡流量
'''
stat_pl = 'data/only_netio_counters.pl'
if os.path.exists(stat_pl):
local_lo = (0, 0, 0, 0)
ioName = psutil.net_io_counters(pernic=True).keys()
for x in ioName:
if x.find("lo") > -1:
local_lo = psutil.net_io_counters(
pernic=True).get(x)[:4]
all_io = psutil.net_io_counters()[:4]
result_io = tuple([all_io[i] - local_lo[i]
for i in range(0, len(all_io))])
# print(local_lo)
# print(all_io)
# print(result_io)
return result_io
return psutil.net_io_counters()[:4]
def getNetWork(self):
# 取网络流量信息
try:
# 取网络流量信息
networkIo = self.psutilNetIoCounters()
if not "otime" in session:
session['up'] = networkIo[0]
session['down'] = networkIo[1]
session['otime'] = time.time()
ntime = time.time()
networkInfo = {}
networkInfo['upTotal'] = networkIo[0]
networkInfo['downTotal'] = networkIo[1]
networkInfo['up'] = round(float(
networkIo[0] - session['up']) / 1024 / (ntime - session['otime']), 2)
networkInfo['down'] = round(
float(networkIo[1] - session['down']) / 1024 / (ntime - session['otime']), 2)
networkInfo['downPackets'] = networkIo[3]
networkInfo['upPackets'] = networkIo[2]
# print networkIo[1], session['down'], ntime, session['otime']
session['up'] = networkIo[0]
session['down'] = networkIo[1]
session['otime'] = time.time()
networkInfo['cpu'] = self.getCpuInfo()
networkInfo['load'] = self.getLoadAverage()
networkInfo['mem'] = self.getMemInfo()
return networkInfo
except Exception as e:
print("getNetWork error:", e)
return None
def getNetWorkIoData(self, start, end):
# 取指定时间段的网络Io
data = mw.M('network').dbfile('system').where("addtime>=? AND addtime<=?", (start, end)).field(
'id,up,down,total_up,total_down,down_packets,up_packets,addtime').order('id asc').select()
return self.toAddtime(data)
def getDiskIoData(self, start, end):
# 取指定时间段的磁盘Io
data = mw.M('diskio').dbfile('system').where("addtime>=? AND addtime<=?", (start, end)).field(
'id,read_count,write_count,read_bytes,write_bytes,read_time,write_time,addtime').order('id asc').select()
return self.toAddtime(data)
def getCpuIoData(self, start, end):
# 取指定时间段的CpuIo
data = mw.M('cpuio').dbfile('system').where("addtime>=? AND addtime<=?",
(start, end)).field('id,pro,mem,addtime').order('id asc').select()
return self.toAddtime(data, True)
def getLoadAverageData(self, start, end):
data = mw.M('load_average').dbfile('system').where("addtime>=? AND addtime<=?", (
start, end)).field('id,pro,one,five,fifteen,addtime').order('id asc').select()
return self.toAddtime(data)
# 格式化addtime列
def toAddtime(self, data, tomem=False):
import time
if tomem:
import psutil
mPre = (psutil.virtual_memory().total / 1024 / 1024) / 100
length = len(data)
he = 1
if length > 100:
he = 1
if length > 1000:
he = 3
if length > 10000:
he = 15
if he == 1:
for i in range(length):
data[i]['addtime'] = time.strftime(
'%m/%d %H:%M', time.localtime(float(data[i]['addtime'])))
if tomem and data[i]['mem'] > 100:
data[i]['mem'] = data[i]['mem'] / mPre
return data
else:
count = 0
tmp = []
for value in data:
if count < he:
count += 1
continue
value['addtime'] = time.strftime(
'%m/%d %H:%M', time.localtime(float(value['addtime'])))
if tomem and value['mem'] > 100:
value['mem'] = value['mem'] / mPre
tmp.append(value)
count = 0
return tmp
def setControl(self, stype, day):
filename = 'data/control.conf'
stat_pl = 'data/only_netio_counters.pl'
if stype == '0':
mw.execShell("rm -rf " + filename)
elif stype == '1':
_day = int(day)
if _day < 1:
return mw.returnJson(False, "设置失败!")
mw.writeFile(filename, day)
elif stype == '2':
mw.execShell("rm -rf " + stat_pl)
elif stype == '3':
mw.execShell("echo 'True' > " + stat_pl)
elif stype == 'del':
if not mw.isRestart():
return mw.returnJson(False, '请等待所有安装任务完成再执行')
os.remove("data/system.db")
sql = db.Sql().dbfile('system')
csql = mw.readFile('data/sql/system.sql')
csql_list = csql.split(';')
for index in range(len(csql_list)):
sql.execute(csql_list[index], ())
return mw.returnJson(True, "监控服务已关闭")
else:
data = {}
if os.path.exists(filename):
try:
data['day'] = int(mw.readFile(filename))
except:
data['day'] = 30
data['status'] = True
else:
data['day'] = 30
data['status'] = False
if os.path.exists(stat_pl):
data['stat_all_status'] = True
else:
data['stat_all_status'] = False
return mw.getJson(data)
return mw.returnJson(True, "设置成功!")
def versionDiff(self, old, new):
'''
test 测试
new 有新版本
none 没有新版本
'''
new_list = new.split('.')
if len(new_list) > 3:
return 'test'
old_list = old.split('.')
ret = 'none'
isHasNew = True
if int(new_list[0]) == int(old_list[0]) and int(new_list[1]) == int(old_list[1]) and int(new_list[2]) == int(old_list[2]):
isHasNew = False
if isHasNew:
return 'new'
return ret
def getServerInfo(self):
import urllib.request
import ssl
upAddr = 'https://api.github.com/repos/midoks/mdserver-web/releases/latest'
try:
context = ssl._create_unverified_context()
req = urllib.request.urlopen(upAddr, context=context, timeout=3)
result = req.read().decode('utf-8')
version = json.loads(result)
return version
except Exception as e:
print('getServerInfo', e)
return {}
def updateServer(self, stype, version=''):
# 更新服务
try:
if not mw.isRestart():
return mw.returnJson(False, '请等待所有安装任务完成再执行!')
version_new_info = self.getServerInfo()
version_now = config_api.config_api().getVersion()
new_ver = version_new_info['name']
if stype == 'check':
if not 'name' in version_new_info:
return mw.returnJson(False, '服务器数据或网络有问题!')
diff = self.versionDiff(version_now, new_ver)
if diff == 'new':
return mw.returnJson(True, '有新版本!', new_ver)
elif diff == 'test':
return mw.returnJson(True, '有测试版本!', new_ver)
else:
return mw.returnJson(False, '已经是最新,无需更新!')
if stype == 'info':
if not 'name' in version_new_info:
return mw.returnJson(False, '服务器数据有问题!')
diff = self.versionDiff(version_now, new_ver)
data = {}
data['version'] = new_ver
data['content'] = version_new_info[
'body'].replace("\n", "<br/>")
return mw.returnJson(True, '更新信息!', data)
if stype == 'update':
if version == '':
return mw.returnJson(False, '缺少版本信息!')
if new_ver != version:
return mw.returnJson(False, '更新失败,请重试!')
toPath = mw.getRootDir() + '/temp'
if not os.path.exists(toPath):
mw.execShell('mkdir -p ' + toPath)
newUrl = "https://github.com/midoks/mdserver-web/archive/refs/tags/" + version + ".zip"
dist_mw = toPath + '/mw.zip'
if not os.path.exists(dist_mw):
mw.execShell(
'wget --no-check-certificate -O ' + dist_mw + ' ' + newUrl)
dist_to = toPath + "/mdserver-web-" + version
if not os.path.exists(dist_to):
os.system('unzip -o ' + toPath +
'/mw.zip' + ' -d ' + toPath)
cmd_cp = 'cp -rf ' + toPath + '/mdserver-web-' + \
version + '/* ' + mw.getServerDir() + '/mdserver-web'
mw.execShell(cmd_cp)
mw.execShell('rm -rf ' + toPath + '/mdserver-web-' + version)
mw.execShell('rm -rf ' + toPath + '/mw.zip')
update_env = '''
#!/bin/bash
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
if [ ! -f /www/server/mdserver-web/bin/activate ];then
cd /www/server/mdserver-web && python3 -m venv .
cd /www/server/mdserver-web && source /www/server/mdserver-web/bin/activate
else
cd /www/server/mdserver-web && source /www/server/mdserver-web/bin/activate
fi
cn=$(curl -fsSL -m 10 http://ipinfo.io/json | grep "\"country\": \"CN\"")
PIPSRC="https://pypi.python.org/simple"
if [ ! -z "$cn" ];then
PIPSRC="https://pypi.tuna.tsinghua.edu.cn/simple"
fi
cd /www/server/mdserver-web && pip3 install -r /www/server/mdserver-web/requirements.txt -i $PIPSRC
'''
os.system(update_env)
self.restartMw()
return mw.returnJson(True, '安装更新成功!')
return mw.returnJson(False, '已经是最新,无需更新!')
except Exception as ex:
# print('updateServer', ex)
return mw.returnJson(False, "连接服务器失败!" + str(ex))
# 修复面板
def repPanel(self, get):
vp = ''
if mw.readFile('/www/server/mdserver-web/class/common.py').find('checkSafe') != -1:
vp = '_pro'
mw.ExecShell("wget -O update.sh " + mw.get_url() +
"/install/update" + vp + ".sh && bash update.sh")
if hasattr(web.ctx.session, 'getCloudPlugin'):
del(web.ctx.session['getCloudPlugin'])
return True
-103
View File
@@ -1,103 +0,0 @@
# coding: utf-8
# ---------------------------------------------------------------------------------
# MW-Linux面板
# ---------------------------------------------------------------------------------
# copyright (c) 2018-∞(https://github.com/midoks/mdserver-web) All rights reserved.
# ---------------------------------------------------------------------------------
# Author: midoks <midoks@163.com>
# ---------------------------------------------------------------------------------
# ---------------------------------------------------------------------------------
# 任务页操作
# ---------------------------------------------------------------------------------
import psutil
import time
import os
import sys
import mw
import re
import json
import pwd
from flask import request
class task_api:
def __init__(self):
pass
def countApi(self):
c = mw.M('tasks').where("status!=?", ('1',)).count()
return str(c)
def listApi(self):
p = request.form.get('p', '1')
limit = request.form.get('limit', '10').strip()
search = request.form.get('search', '').strip()
start = (int(p) - 1) * int(limit)
limit_str = str(start) + ',' + str(limit)
_list = mw.M('tasks').where('', ()).field(
'id,name,type,status,addtime,start,end').limit(limit_str).order('id desc').select()
_ret = {}
_ret['data'] = _list
count = mw.M('tasks').where('', ()).count()
_page = {}
_page['count'] = count
_page['tojs'] = 'remind'
_page['p'] = p
# return data
_ret['count'] = count
_ret['page'] = mw.getPage(_page)
return mw.getJson(_ret)
def getExecLogApi(self):
file = os.getcwd() + "/tmp/panelExec.log"
v = mw.getLastLine(file, 100)
return v
def getTaskSpeedApi(self):
tempFile = mw.getRunDir() + '/tmp/panelExec.log'
freshFile = mw.getRunDir() + '/tmp/panelFresh'
find = mw.M('tasks').where('status=? OR status=?',
('-1', '0')).field('id,type,name,execstr').find()
if not len(find):
return mw.returnJson(False, '当前没有任务队列在执行-2!')
mw.triggerTask()
data = {}
data['name'] = find['name']
data['execstr'] = find['execstr']
if find['type'] == 'download':
readLine = ""
for i in range(3):
try:
readLine = mw.readFile(tempFile)
if len(readLine) > 10:
data['msg'] = json.loads(readLine)
data['isDownload'] = True
break
except Exception as e:
if i == 2:
mw.M('tasks').where("id=?", (find['id'],)).save(
'status', ('0',))
return mw.returnJson(False, '当前没有任务队列在执行-4:' + str(e))
time.sleep(0.5)
else:
data['msg'] = mw.getLastLine(tempFile, 10)
data['isDownload'] = False
data['task'] = mw.M('tasks').where("status!=?", ('1',)).field(
'id,status,name,type').order("id asc").select()
return mw.getJson(data)
-143
View File
@@ -1,143 +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 random
import math
from PIL import Image, ImageDraw, ImageFont, ImageFilter
class vieCode:
__fontSize = 20 # 字体大小
__width = 120 # 画布宽度
__heigth = 45 # 画布高度
__length = 4 # 验证码长度
__draw = None # 画布
__img = None # 图片资源
__code = None # 验证码字符
__str = None # 自定义验证码字符集
__inCurve = True # 是否画干扰线
__inNoise = True # 是否画干扰点
__type = 2 # 验证码类型 1、纯字母 2、数字字母混合
__fontPatn = 'class/fonts/2.ttf' # 字体
def GetCodeImage(self, size=80, length=4):
'''获取验证码图片
@param int size 验证码大小
@param int length 验证码长度
'''
# 准备基础数据
self.__length = length
self.__fontSize = size
self.__width = self.__fontSize * self.__length
self.__heigth = int(self.__fontSize * 1.5)
# 生成验证码图片
self.__createCode()
self.__createImage()
self.__createNoise()
self.__printString()
self.__cerateFilter()
return self.__img, self.__code
def __cerateFilter(self):
'''模糊处理'''
self.__img = self.__img.filter(ImageFilter.BLUR)
filter = ImageFilter.ModeFilter(8)
self.__img = self.__img.filter(filter)
def __createCode(self):
'''创建验证码字符'''
# 是否自定义字符集合
if not self.__str:
# 源文本
number = "3456789"
srcLetter = "qwertyuipasdfghjkzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM"
srcUpper = srcLetter.upper()
if self.__type == 1:
self.__str = number
else:
self.__str = srcLetter + srcUpper + number
# 构造验证码
self.__code = random.sample(self.__str, self.__length)
def __createImage(self):
'''创建画布'''
bgColor = (random.randint(200, 255), random.randint(
200, 255), random.randint(200, 255))
self.__img = Image.new('RGB', (self.__width, self.__heigth), bgColor)
self.__draw = ImageDraw.Draw(self.__img)
def __createNoise(self):
'''画干扰点'''
if not self.__inNoise:
return
font = ImageFont.truetype(self.__fontPatn, int(self.__fontSize / 1.5))
for i in range(5):
# 杂点颜色
noiseColor = (random.randint(150, 200), random.randint(
150, 200), random.randint(150, 200))
putStr = random.sample(self.__str, 2)
for j in range(2):
# 绘杂点
size = (random.randint(-10, self.__width),
random.randint(-10, self.__heigth))
self.__draw.text(size, putStr[j], font=font, fill=noiseColor)
pass
def __createCurve(self):
'''画干扰线'''
if not self.__inCurve:
return
x = y = 0
# 计算曲线系数
a = random.uniform(1, self.__heigth / 2)
b = random.uniform(-self.__width / 4, self.__heigth / 4)
f = random.uniform(-self.__heigth / 4, self.__heigth / 4)
t = random.uniform(self.__heigth, self.__width * 2)
xend = random.randint(self.__width / 2, self.__width * 2)
w = (2 * math.pi) / t
# 画曲线
color = (random.randint(30, 150), random.randint(
30, 150), random.randint(30, 150))
for x in range(xend):
if w != 0:
for k in range(int(self.__heigth / 10)):
y = a * math.sin(w * x + f) + b + self.__heigth / 2
i = int(self.__fontSize / 5)
while i > 0:
px = x + i
py = y + i + k
self.__draw.point((px, py), color)
i -= i
def __printString(self):
'''打印验证码字符串'''
font = ImageFont.truetype(self.__fontPatn, self.__fontSize)
x = 0
# 打印字符到画板
for i in range(self.__length):
# 设置字体随机颜色
color = (random.randint(30, 150), random.randint(
30, 150), random.randint(30, 150))
# 计算座标
x = random.uniform(self.__fontSize * i * 0.95,
self.__fontSize * i * 1.1)
y = self.__fontSize * random.uniform(0.3, 0.5)
# 打印字符
self.__draw.text((x, y), self.__code[i], font=font, fill=color)
BIN
View File
Binary file not shown.
View File
View File
-5
View File
@@ -1,5 +0,0 @@
- fcgi-client.py
```
https://pypi.org/project/flup6/1.1.1/#files
```
-408
View File
@@ -1,408 +0,0 @@
# Copyright (c) 2006 Allan Saddi <allan@saddi.com>
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
# 1. Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
# 2. Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
# SUCH DAMAGE.
#
# $Id$
#
# Copyright (c) 2011 Vladimir Rusinov <vladimir@greenmice.info>
__author__ = 'Allan Saddi <allan@saddi.com>'
__version__ = '$Revision$'
import sys
import select
import struct
import socket
import errno
import types
__all__ = ['FCGIApp']
# Constants from the spec.
FCGI_LISTENSOCK_FILENO = 0
FCGI_HEADER_LEN = 8
FCGI_VERSION_1 = 1
FCGI_BEGIN_REQUEST = 1
FCGI_ABORT_REQUEST = 2
FCGI_END_REQUEST = 3
FCGI_PARAMS = 4
FCGI_STDIN = 5
FCGI_STDOUT = 6
FCGI_STDERR = 7
FCGI_DATA = 8
FCGI_GET_VALUES = 9
FCGI_GET_VALUES_RESULT = 10
FCGI_UNKNOWN_TYPE = 11
FCGI_MAXTYPE = FCGI_UNKNOWN_TYPE
FCGI_NULL_REQUEST_ID = 0
FCGI_KEEP_CONN = 1
FCGI_RESPONDER = 1
FCGI_AUTHORIZER = 2
FCGI_FILTER = 3
FCGI_REQUEST_COMPLETE = 0
FCGI_CANT_MPX_CONN = 1
FCGI_OVERLOADED = 2
FCGI_UNKNOWN_ROLE = 3
FCGI_MAX_CONNS = 'FCGI_MAX_CONNS'
FCGI_MAX_REQS = 'FCGI_MAX_REQS'
FCGI_MPXS_CONNS = 'FCGI_MPXS_CONNS'
FCGI_Header = '!BBHHBx'
FCGI_BeginRequestBody = '!HB5x'
FCGI_EndRequestBody = '!LB3x'
FCGI_UnknownTypeBody = '!B7x'
FCGI_BeginRequestBody_LEN = struct.calcsize(FCGI_BeginRequestBody)
FCGI_EndRequestBody_LEN = struct.calcsize(FCGI_EndRequestBody)
FCGI_UnknownTypeBody_LEN = struct.calcsize(FCGI_UnknownTypeBody)
if __debug__:
import time
# Set non-zero to write debug output to a file.
DEBUG = 0
DEBUGLOG = '/www/server/mdserver-web/logs/fastcgi.log'
def _debug(level, msg):
if DEBUG < level:
return
try:
f = open(DEBUGLOG, 'a')
f.write('%sfcgi: %s\n' % (time.ctime()[4:-4], msg))
f.close()
except:
pass
def decode_pair(s, pos=0):
"""
Decodes a name/value pair.
The number of bytes decoded as well as the name/value pair
are returned.
"""
nameLength = ord(s[pos])
if nameLength & 128:
nameLength = struct.unpack('!L', s[pos:pos + 4])[0] & 0x7fffffff
pos += 4
else:
pos += 1
valueLength = ord(s[pos])
if valueLength & 128:
valueLength = struct.unpack('!L', s[pos:pos + 4])[0] & 0x7fffffff
pos += 4
else:
pos += 1
name = s[pos:pos + nameLength]
pos += nameLength
value = s[pos:pos + valueLength]
pos += valueLength
return (pos, (name, value))
def encode_pair(name, value):
"""
Encodes a name/value pair.
The encoded string is returned.
"""
nameLength = len(name)
if nameLength < 128:
s = chr(nameLength).encode()
else:
s = struct.pack('!L', nameLength | 0x80000000)
valueLength = len(value)
if valueLength < 128:
s += chr(valueLength).encode()
else:
s += struct.pack('!L', valueLength | 0x80000000)
return s + name + value
class Record(object):
"""
A FastCGI Record.
Used for encoding/decoding records.
"""
def __init__(self, type=FCGI_UNKNOWN_TYPE, requestId=FCGI_NULL_REQUEST_ID):
self.version = FCGI_VERSION_1
self.type = type
self.requestId = requestId
self.contentLength = 0
self.paddingLength = 0
self.contentData = ''
def _recvall(sock, length):
"""
Attempts to receive length bytes from a socket, blocking if necessary.
(Socket may be blocking or non-blocking.)
"""
dataList = []
recvLen = 0
while length:
try:
data = sock.recv(length)
except socket.error as e:
if e[0] == errno.EAGAIN:
select.select([sock], [], [])
continue
else:
raise
if not data: # EOF
break
dataList.append(data)
dataLen = len(data)
recvLen += dataLen
length -= dataLen
return b''.join(dataList), recvLen
_recvall = staticmethod(_recvall)
def read(self, sock):
"""Read and decode a Record from a socket."""
try:
header, length = self._recvall(sock, FCGI_HEADER_LEN)
except:
raise EOFError
if length < FCGI_HEADER_LEN:
raise EOFError
self.version, self.type, self.requestId, self.contentLength, \
self.paddingLength = struct.unpack(FCGI_Header, header)
if __debug__:
_debug(9, 'read: fd = %d, type = %d, requestId = %d, '
'contentLength = %d' %
(sock.fileno(), self.type, self.requestId,
self.contentLength))
if self.contentLength:
try:
self.contentData, length = self._recvall(sock,
self.contentLength)
except:
raise EOFError
if length < self.contentLength:
raise EOFError
if self.paddingLength:
try:
self._recvall(sock, self.paddingLength)
except:
raise EOFError
def _sendall(sock, data):
"""
Writes data to a socket and does not return until all the data is sent.
"""
length = len(data)
while length:
try:
sent = sock.send(data)
except socket.error as e:
if e[0] == errno.EAGAIN:
select.select([], [sock], [])
continue
else:
raise
data = data[sent:]
length -= sent
_sendall = staticmethod(_sendall)
def write(self, sock):
"""Encode and write a Record to a socket."""
self.paddingLength = -self.contentLength & 7
if __debug__:
_debug(9, 'write: fd = %d, type = %d, requestId = %d, '
'contentLength = %d' %
(sock.fileno(), self.type, self.requestId,
self.contentLength))
header = struct.pack(FCGI_Header, self.version, self.type,
self.requestId, self.contentLength,
self.paddingLength)
self._sendall(sock, header)
if self.contentLength:
self._sendall(sock, self.contentData)
if self.paddingLength:
self._sendall(sock, b'\x00' * self.paddingLength)
class FCGIApp(object):
def __init__(self, connect=None, host=None, port=None, filterEnviron=True):
if host is not None:
assert port is not None
connect = (host, port)
self._connect = connect
self._filterEnviron = filterEnviron
def __call__(self, environ, io, start_response=None):
# For sanity's sake, we don't care about FCGI_MPXS_CONN
# (connection multiplexing). For every request, we obtain a new
# transport socket, perform the request, then discard the socket.
# This is, I believe, how mod_fastcgi does things...
sock = self._getConnection()
# Since this is going to be the only request on this connection,
# set the request ID to 1.
requestId = 1
# Begin the request
rec = Record(FCGI_BEGIN_REQUEST, requestId)
rec.contentData = struct.pack(FCGI_BeginRequestBody, FCGI_RESPONDER, 0)
rec.contentLength = FCGI_BeginRequestBody_LEN
rec.write(sock)
# Filter WSGI environ and send it as FCGI_PARAMS
if self._filterEnviron:
params = self._defaultFilterEnviron(environ)
else:
params = self._lightFilterEnviron(environ)
# TODO: Anything not from environ that needs to be sent also?
# return '200 OK',[],str(params),''
self._fcgiParams(sock, requestId, params)
self._fcgiParams(sock, requestId, {})
# Transfer wsgi.input to FCGI_STDIN
content_length = int(environ.get('CONTENT_LENGTH') or 0)
s = ''
#io = StringIO(stdin)
while True:
if not io:
break
chunk_size = min(content_length, 4096)
s = io.read(chunk_size)
content_length -= len(s)
rec = Record(FCGI_STDIN, requestId)
rec.contentData = s
rec.contentLength = len(s)
rec.write(sock)
if not s:
break
# Empty FCGI_DATA stream
rec = Record(FCGI_DATA, requestId)
rec.write(sock)
return sock
def _getConnection(self):
if self._connect is not None:
# The simple case. Create a socket and connect to the
# application.
if isinstance(self._connect, str):
sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
sock.connect(self._connect)
elif hasattr(socket, 'create_connection'):
sock = socket.create_connection(self._connect)
else:
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect(self._connect)
return sock
# To be done when I have more time...
# , 'Launching and managing FastCGI programs not yet implemented'
raise NotImplementedError
def _fcgiGetValues(self, sock, vars):
# Construct FCGI_GET_VALUES record
outrec = Record(FCGI_GET_VALUES)
data = []
for name in vars:
data.append(encode_pair(name, ''))
data = ''.join(data)
outrec.contentData = data
outrec.contentLength = len(data)
outrec.write(sock)
# Await response
inrec = Record()
inrec.read(sock)
result = {}
if inrec.type == FCGI_GET_VALUES_RESULT:
pos = 0
while pos < inrec.contentLength:
pos, (name, value) = decode_pair(inrec.contentData, pos)
result[name] = value
return result
def _fcgiParams(self, sock, requestId, params):
rec = Record(FCGI_PARAMS, requestId)
data = []
for name, value in params.items():
data.append(encode_pair(name.encode(
'latin-1'), value.encode('latin-1')))
data = b''.join(data)
rec.contentData = data
rec.contentLength = len(data)
rec.write(sock)
_environPrefixes = ['SERVER_', 'HTTP_', 'REQUEST_', 'REMOTE_', 'PATH_',
'CONTENT_', 'DOCUMENT_', 'SCRIPT_']
_environCopies = ['SCRIPT_NAME', 'QUERY_STRING', 'AUTH_TYPE']
_environRenames = []
def _defaultFilterEnviron(self, environ):
result = {}
for n in environ.keys():
iv = False
for p in self._environPrefixes:
if n.startswith(p):
result[n] = environ[n]
iv = True
if n in self._environCopies:
result[n] = environ[n]
iv = True
if n in self._environRenames:
result[self._environRenames[n]] = environ[n]
iv = True
if not iv:
result[n] = environ[n]
return result
def _lightFilterEnviron(self, environ):
result = {}
for n in environ.keys():
if n.upper() == n:
result[n] = environ[n]
return result
-208
View File
@@ -1,208 +0,0 @@
# coding:utf-8
import json
import os
import time
import re
import sys
import time
import struct
import fcgi_client
FCGI_Header = '!BBHHBx'
if sys.version_info[0] == 2:
try:
from cStringIO import StringIO
except:
from StringIO import StringIO
else:
from io import BytesIO as StringIO
def get_header_data(sock):
'''
@name 获取头部32KB数据
@param sock socketobject(fastcgi套接字对象)
@return bytes
'''
headers_data = b''
total_len = 0
header_len = 1024 * 128
while True:
fastcgi_header = sock.recv(8)
if not fastcgi_header:
break
if len(fastcgi_header) != 8:
headers_data += fastcgi_header
break
fast_pack = struct.unpack(FCGI_Header, fastcgi_header)
if fast_pack[1] == 3:
break
tlen = fast_pack[3]
while tlen > 0:
sd = sock.recv(tlen)
if not sd:
break
headers_data += sd
tlen -= len(sd)
total_len += fast_pack[3]
if fast_pack[4]:
sock.recv(fast_pack[4])
if total_len > header_len:
break
return headers_data
def format_header_data(headers_data):
'''
@name 格式化响应头
@param headers_data bytes(fastcgi头部32KB数据)
@return status int(响应状态), headers dict(响应头), bdata bytes(格式化响应头后的多余数据)
'''
status = '200 OK'
headers = {}
pos = 0
while True:
eolpos = headers_data.find(b'\n', pos)
if eolpos < 0:
break
line = headers_data[pos:eolpos - 1]
pos = eolpos + 1
line = line.strip()
if len(line) < 2:
break
if line.find(b':') == -1:
continue
header, value = line.split(b':', 1)
header = header.strip()
value = value.strip()
if isinstance(header, bytes):
header = header.decode()
value = value.decode()
if header == 'Status':
status = value
if status.find(' ') < 0:
status += ' BTPanel'
else:
headers[header] = value
bdata = headers_data[pos:]
status = int(status.split(' ')[0])
return status, headers, bdata
def resp_sock(sock, bdata):
'''
@name 以流的方式发送剩余数据
@param sock socketobject(fastcgi套接字对象)
@param bdata bytes(格式化响应头后的多余数据)
@return yield bytes
'''
# 发送除响应头以外的多余头部数据
yield bdata
while True:
fastcgi_header = sock.recv(8)
if not fastcgi_header:
break
if len(fastcgi_header) != 8:
yield fastcgi_header
break
fast_pack = struct.unpack(FCGI_Header, fastcgi_header)
if fast_pack[1] == 3:
break
tlen = fast_pack[3]
while tlen > 0:
sd = sock.recv(tlen)
if not sd:
break
tlen -= len(sd)
if sd:
yield sd
if fast_pack[4]:
sock.recv(fast_pack[4])
sock.close()
class fpm(object):
def __init__(self, sock=None, document_root='', last_path=''):
'''
@name 实例化FPM对象
@param sock string(unixsocket路径)
@param document_root string(PHP文档根目录)
@return FPM
'''
if sock:
self.fcgi_sock = sock
if document_root[-1:] != '/':
document_root += '/'
self.document_root = document_root
self.last_path = last_path
def load_url_public(self, url, content=b'', method='GET', content_type='application/x-www-form-urlencoded'):
'''
@name 转发URL到PHP-FPM 公共
@param url string(URI地址)
@param content stream(POST数据io对象)
@return fastcgi-socket
'''
fcgi = fcgi_client.FCGIApp(connect=self.fcgi_sock)
try:
script_name, query_string = url.split('?')
except ValueError:
script_name = url
query_string = ''
content_length = len(content)
if content:
content = StringIO(content)
env = {
'SCRIPT_FILENAME': '%s%s' % (self.document_root, script_name),
'QUERY_STRING': query_string,
'REQUEST_METHOD': method,
'SCRIPT_NAME': self.last_path + script_name,
'REQUEST_URI': url,
'GATEWAY_INTERFACE': 'CGI/1.1',
'SERVER_SOFTWARE': 'MW-Panel',
'REDIRECT_STATUS': '200',
'CONTENT_TYPE': content_type,
'CONTENT_LENGTH': str(content_length),
'DOCUMENT_URI': script_name,
'DOCUMENT_ROOT': self.document_root,
'SERVER_PROTOCOL': 'HTTP/1.1',
'REMOTE_ADDR': '127.0.0.1',
'REMOTE_PORT': '7200',
'SERVER_ADDR': '127.0.0.1',
'SERVER_PORT': '80',
'SERVER_NAME': 'MW-Panel'
}
fpm_sock = fcgi(env, content)
_data = b''
while True:
fastcgi_header = fpm_sock.recv(8)
if not fastcgi_header:
break
if len(fastcgi_header) != 8:
_data += fastcgi_header
break
fast_pack = struct.unpack(FCGI_Header, fastcgi_header)
if fast_pack[1] == 3:
break
tlen = fast_pack[3]
while tlen > 0:
sd = fpm_sock.recv(tlen)
if not sd:
break
tlen -= len(sd)
_data += sd
if fast_pack[4]:
fpm_sock.recv(fast_pack[4])
status, headers, data = format_header_data(_data)
return data
-116
View File
@@ -1,116 +0,0 @@
# coding: utf-8
import re
import os
import sys
import pymysql.cursors
class ORM:
__DB_PASS = None
__DB_USER = 'root'
__DB_PORT = 3306
__DB_NAME = ''
__DB_HOST = 'localhost'
__DB_CONN = None
__DB_CUR = None
__DB_ERR = None
__DB_CNF = '/etc/my.cnf'
__DB_SOCKET = '/www/server/mysql/mysql.sock'
__DB_CHARSET = "utf8"
def __Conn(self):
'''连接数据库'''
try:
if os.path.exists(self.__DB_SOCKET):
try:
self.__DB_CONN = pymysql.connect(host=self.__DB_HOST, user=self.__DB_USER, passwd=self.__DB_PASS,
database=self.__DB_NAME,
port=int(self.__DB_PORT), charset=self.__DB_CHARSET, connect_timeout=1,
unix_socket=self.__DB_SOCKET, cursorclass=pymysql.cursors.DictCursor)
except Exception as e:
self.__DB_HOST = '127.0.0.1'
self.__DB_CONN = pymysql.connect(host=self.__DB_HOST, user=self.__DB_USER, passwd=self.__DB_PASS,
database=self.__DB_NAME,
port=int(self.__DB_PORT), charset=self.__DB_CHARSET, connect_timeout=1,
unix_socket=self.__DB_SOCKET, cursorclass=pymysql.cursors.DictCursor)
else:
try:
self.__DB_CONN = pymysql.connect(host=self.__DB_HOST, user=self.__DB_USER, passwd=self.__DB_PASS,
database=self.__DB_NAME,
port=int(self.__DB_PORT), charset=self.__DB_CHARSET, connect_timeout=1,
cursorclass=pymysql.cursors.DictCursor)
except Exception as e:
self.__DB_HOST = '127.0.0.1'
self.__DB_CONN = pymysql.connect(host=self.__DB_HOST, user=self.__DB_USER, passwd=self.__DB_PASS,
database=self.__DB_NAME,
port=int(self.__DB_PORT), charset=self.__DB_CHARSET, connect_timeout=1,
cursorclass=pymysql.cursors.DictCursor)
self.__DB_CUR = self.__DB_CONN.cursor()
return True
except Exception as e:
self.__DB_ERR = e
return False
def setDbConf(self, conf):
self.__DB_CNF = conf
def setSocket(self, sock):
self.__DB_SOCKET = sock
def setCharset(self, charset):
self.__DB_CHARSET = charset
def setHost(self, host):
self.__DB_HOST = host
def setPort(self, port):
self.__DB_PORT = port
def setUser(self, user):
self.__DB_USER = user
def setPwd(self, pwd):
self.__DB_PASS = pwd
def getPwd(self):
return self.__DB_PASS
def setDbName(self, name):
self.__DB_NAME = name
def execute(self, sql):
# 执行SQL语句返回受影响行
if not self.__Conn():
return self.__DB_ERR
try:
result = self.__DB_CUR.execute(sql)
self.__DB_CONN.commit()
self.__Close()
return result
except Exception as ex:
return ex
def query(self, sql):
# 执行SQL语句返回数据集
if not self.__Conn():
return self.__DB_ERR
try:
self.__DB_CUR.execute(sql)
result = self.__DB_CUR.fetchall()
# print(result)
# 将元组转换成列表
# data = map(list, result)
self.__Close()
return result
except Exception as ex:
return ex
def __Close(self):
# 关闭连接
self.__DB_CUR.close()
self.__DB_CONN.close()
-115
View File
@@ -1,115 +0,0 @@
# coding: utf-8
import re
import os
import sys
class ORM:
__DB_PASS = None
__DB_USER = 'root'
__DB_PORT = 3306
__DB_HOST = 'localhost'
__DB_CONN = None
__DB_CUR = None
__DB_ERR = None
__DB_CNF = '/etc/my.cnf'
__DB_SOCKET = '/www/server/mysql/mysql.sock'
__DB_CHARSET = 'utf8'
def __Conn(self):
'''连接MYSQL数据库'''
try:
try:
import MySQLdb
except Exception as ex:
self.__DB_ERR = ex
return False
# print(self.__DB_HOST)
# print(self.__DB_USER)
# print(self.__DB_PASS)
# print(self.__DB_PORT)
# print(self.__DB_CHARSET)
# print(self.__DB_SOCKET)
if os.path.exists(self.__DB_SOCKET):
try:
self.__DB_CONN = MySQLdb.connect(host=self.__DB_HOST, user=self.__DB_USER, passwd=self.__DB_PASS,
port=int(self.__DB_PORT), charset=self.__DB_CHARSET, connect_timeout=1, unix_socket=self.__DB_SOCKET)
except Exception as e:
print(e)
self.__DB_HOST = '127.0.0.1'
self.__DB_CONN = MySQLdb.connect(host=self.__DB_HOST, user=self.__DB_USER, passwd=self.__DB_PASS,
port=int(self.__DB_PORT), charset=self.__DB_CHARSET, connect_timeout=1, unix_socket=self.__DB_SOCKET)
else:
try:
self.__DB_CONN = MySQLdb.connect(host=self.__DB_HOST, user=self.__DB_USER, passwd=self.__DB_PASS,
port=int(self.__DB_PORT), charset=self.__DB_CHARSET, connect_timeout=1)
except Exception as e:
self.__DB_HOST = '127.0.0.1'
self.__DB_CONN = MySQLdb.connect(host=self.__DB_HOST, user=self.__DB_USER, passwd=self.__DB_PASS,
port=int(self.__DB_PORT), charset=self.__DB_CHARSET, connect_timeout=1)
self.__DB_CUR = self.__DB_CONN.cursor()
return True
except MySQLdb.Error as e:
self.__DB_ERR = e
return False
def setDbConf(self, conf):
self.__DB_CNF = conf
def setSocket(self, sock):
self.__DB_SOCKET = sock
def setCharset(self, charset):
self.__DB_CHARSET = charset
def setPort(self, port):
self.__DB_PORT = port
def setPwd(self, pwd):
self.__DB_PASS = pwd
def getPwd(self):
return self.__DB_PASS
def setDbName(self, name):
self.__DB_NAME = name
def setUser(self, user):
self.__DB_USER = user
def execute(self, sql):
# 执行SQL语句返回受影响行
if not self.__Conn():
return self.__DB_ERR
try:
result = self.__DB_CUR.execute(sql)
self.__DB_CONN.commit()
self.__Close()
return result
except Exception as ex:
return ex
def query(self, sql):
# 执行SQL语句返回数据集
if not self.__Conn():
return self.__DB_ERR
try:
self.__DB_CUR.execute(sql)
result = self.__DB_CUR.fetchall()
# 将元组转换成列表
data = map(list, result)
self.__Close()
return data
except Exception as ex:
return ex
# 关闭连接
def __Close(self):
self.__DB_CUR.close()
self.__DB_CONN.close()
-7
View File
@@ -1,7 +0,0 @@
#!/bin/bash
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin:/usr/local/lib/python2.7/bin
find . -name .DS_Store | xargs rm -rf
find . -type d -name "*.pyc" | xargs rm -rf
-91
View File
@@ -1,91 +0,0 @@
#!/bin/bash
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
DIR=$(cd "$(dirname "$0")"; pwd)
MDIR=$(dirname "$DIR")
PATH=$PATH:$DIR/bin
if [ -f bin/activate ];then
source bin/activate
fi
export LC_ALL="en_US.UTF-8"
mw_start_task()
{
isStart=$(ps aux |grep 'task.py'|grep -v grep|awk '{print $2}')
if [ "$isStart" == '' ];then
echo -e "Starting mw-tasks... \c"
cd $DIR && python3 task.py >> ${DIR}/logs/task.log 2>&1 &
sleep 0.3
isStart=$(ps aux |grep 'task.py'|grep -v grep|awk '{print $2}')
if [ "$isStart" == '' ];then
echo -e "\033[31mfailed\033[0m"
echo '------------------------------------------------------'
tail -n 20 $DIR/logs/task.log
echo '------------------------------------------------------'
echo -e "\033[31mError: mw-tasks service startup failed.\033[0m"
return;
fi
echo -e "\033[32mdone\033[0m"
else
echo "Starting mw-tasks... mw-tasks (pid $(echo $isStart)) already running"
fi
}
mw_start(){
gunicorn -c setting.py app:app
#安全启动
mw_start_task
}
mw_start_debug(){
python3 task.py >> $DIR/logs/task.log 2>&1 &
port=7200
if [ -f /www/server/mdserver-web/data/port.pl ];then
port=$(cat /www/server/mdserver-web/data/port.pl)
fi
# gunicorn -b :${port} -k gevent -w 1 app:app
gunicorn -b :${port} -k geventwebsocket.gunicorn.workers.GeventWebSocketWorker -w 1 app:app
}
mw_start_debug2(){
python3 task.py >> $DIR/logs/task.log 2>&1 &
gunicorn -b :7200 -k geventwebsocket.gunicorn.workers.GeventWebSocketWorker -w 1 app:app
}
mw_stop()
{
PLIST=`ps -ef|grep app:app |grep -v grep|awk '{print $2}'`
for i in $PLIST
do
kill -9 $i > /dev/null 2>&1
done
pids=`ps -ef|grep task.py | grep -v grep |awk '{print $2}'`
arr=($pids)
for p in ${arr[@]}
do
kill -9 $p > /dev/null 2>&1
done
}
case "$1" in
'start') mw_start;;
'stop') mw_stop;;
'restart')
mw_stop
mw_start
;;
'debug')
mw_stop
mw_start_debug
;;
'debug2')
mw_stop
mw_start_debug2
;;
esac
-68
View File
@@ -1,68 +0,0 @@
### 常用命令说明
- 面板相关命令
```
/etc/init.d/mw default
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
```
-68
View File
@@ -1,68 +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 |✅ |✅ |
| 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 |
| ----------------- |-------|-------|-------|-------|-------|-------|-------|-------|-------|-------|-------|
| CentOS 7.9 |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |
| CentOS 8.4 |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |
| CentOS 8 Stream |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |
| CentOS 9 Stream |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |
| Debian 10.3 |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |
| Debian 11.3 |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |✅ |
| 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 |✅ |✅ |✅ |✅ |
| Ubuntu 18.04 |✅ |✅ |✅ |✅ |
| Ubuntu 20.04 |✅ |✅ |✅ |✅ |
| Ubuntu 22.04 |✅ |✅ |✅ |✅ |
| Fedora 31 |✅ |✅ |✅ |✅ |
| Fedora 32 |✅ |✅ |✅ |✅ |
| AlmaLinux 9 |✅ |✅ |✅ |✅ |
| RockyLinux 8.6 |✅ |✅ |✅ |✅ |
| Arch Linux |✅ |✅ |✅ |✅ |
| openSUSE 15.4 |✅ |✅ |✅ |✅ |
-42
View File
@@ -1,42 +0,0 @@
[
{
"title":"全部",
"type":0,
"ps":""
},
{
"title":"已安装",
"type":-1,
"ps":""
},
{
"title":"运行环境",
"type":1,
"ps":""
},
{
"title":"数据软件",
"type":2,
"ps":""
},
{
"title":"代码管理",
"type":3,
"ps":""
},
{
"title":"系统工具",
"type":4,
"ps":""
},
{
"title":"其他插件",
"type":5,
"ps":""
},
{
"title":"辅助插件",
"type":6,
"ps":""
}
]
-1
View File
@@ -1 +0,0 @@
True
-134
View File
@@ -1,134 +0,0 @@
CREATE TABLE IF NOT EXISTS `backup` (
`id` INTEGER PRIMARY KEY AUTOINCREMENT,
`type` INTEGER,
`name` TEXT,
`pid` INTEGER,
`filename` TEXT,
`size` INTEGER,
`addtime` TEXT
);
CREATE TABLE IF NOT EXISTS `binding` (
`id` INTEGER PRIMARY KEY AUTOINCREMENT,
`pid` INTEGER,
`domain` TEXT,
`path` TEXT,
`port` INTEGER,
`addtime` TEXT
);
CREATE TABLE IF NOT EXISTS `crontab` (
`id` INTEGER PRIMARY KEY AUTOINCREMENT,
`name` TEXT,
`type` TEXT,
`where1` TEXT,
`where_hour` INTEGER,
`where_minute` INTEGER,
`echo` TEXT,
`addtime` TEXT,
`status` INTEGER DEFAULT '1',
`save` INTEGER DEFAULT '3',
`backup_to` TEXT DEFAULT 'off',
`sname` TEXT,
`sbody` TEXT,
'stype' TEXT,
`urladdress` TEXT
);
CREATE TABLE IF NOT EXISTS `firewall` (
`id` INTEGER PRIMARY KEY AUTOINCREMENT,
`port` TEXT,
`ps` TEXT,
`addtime` TEXT
);
INSERT INTO `firewall` (`id`, `port`, `ps`, `addtime`) VALUES
(1, '80', '网站默认端口', '0000-00-00 00:00:00'),
(2, '22', 'SSH远程管理服务', '0000-00-00 00:00:00'),
(3, '443', 'HTTPS', '0000-00-00 00:00:00'),
(4, '888', 'phpMyAdmin默认端口', '0000-00-00 00:00:00');
CREATE TABLE IF NOT EXISTS `logs` (
`id` INTEGER PRIMARY KEY AUTOINCREMENT,
`type` TEXT,
`log` TEXT,
`uid` TEXT,
`addtime` TEXT
);
ALTER TABLE `logs` ADD COLUMN `uid` INTEGER DEFAULT '1';
CREATE TABLE IF NOT EXISTS `sites` (
`id` INTEGER PRIMARY KEY AUTOINCREMENT,
`name` TEXT,
`path` TEXT,
`status` TEXT,
`index` TEXT,
`type_id` INTEGER,
`ps` TEXT,
`edate` TEXT,
`addtime` TEXT
);
CREATE TABLE IF NOT EXISTS `site_types` (
`id` INTEGER PRIMARY KEY AUTOINCREMENT,
`name` TEXT
);
CREATE TABLE IF NOT EXISTS `domain` (
`id` INTEGER PRIMARY KEY AUTOINCREMENT,
`pid` INTEGER,
`name` TEXT,
`port` INTEGER,
`addtime` TEXT
);
CREATE TABLE IF NOT EXISTS `users` (
`id` INTEGER PRIMARY KEY AUTOINCREMENT,
`username` TEXT,
`password` TEXT,
`login_ip` TEXT,
`login_time` TEXT,
`phone` TEXT,
`email` TEXT
);
INSERT INTO `users` (`id`, `username`, `password`, `login_ip`, `login_time`, `phone`, `email`) VALUES
(1, 'admin', '21232f297a57a5a743894a0e4a801fc3', '192.168.0.10', '2022-02-02 00:00:00', 0, 'midoks@163.com');
CREATE TABLE IF NOT EXISTS `tasks` (
`id` INTEGER PRIMARY KEY AUTOINCREMENT,
`name` TEXT,
`type` TEXT,
`status` TEXT,
`addtime` TEXT,
`start` INTEGER,
`end` INTEGER,
`execstr` TEXT
);
CREATE TABLE IF NOT EXISTS `temp_login` (
`id` INTEGER PRIMARY KEY AUTOINCREMENT,
`token` REAL,
`salt` REAL,
`state` INTEGER,
`login_time` INTEGER,
`login_addr` REAL,
`logout_time` INTEGER,
`expire` INTEGER,
`addtime` INTEGER
);
CREATE TABLE IF NOT EXISTS `panel` (
`id` INTEGER PRIMARY KEY AUTOINCREMENT,
`title` TEXT,
`url` TEXT,
`username` TEXT,
`password` TEXT,
`click` INTEGER,
`addtime` INTEGER
);
-37
View File
@@ -1,37 +0,0 @@
CREATE TABLE IF NOT EXISTS `network` (
`id` INTEGER PRIMARY KEY AUTOINCREMENT,
`up` INTEGER,
`down` INTEGER,
`total_up` INTEGER,
`total_down` INTEGER,
`down_packets` INTEGER,
`up_packets` INTEGER,
`addtime` INTEGER
);
CREATE TABLE IF NOT EXISTS `cpuio` (
`id` INTEGER PRIMARY KEY AUTOINCREMENT,
`pro` INTEGER,
`mem` INTEGER,
`addtime` INTEGER
);
CREATE TABLE IF NOT EXISTS `diskio` (
`id` INTEGER PRIMARY KEY AUTOINCREMENT,
`read_count` INTEGER,
`write_count` INTEGER,
`read_bytes` INTEGER,
`write_bytes` INTEGER,
`read_time` INTEGER,
`write_time` INTEGER,
`addtime` INTEGER
);
CREATE TABLE IF NOT EXISTS `load_average` (
`id` INTEGER PRIMARY KEY AUTOINCREMENT,
`pro` REAL,
`one` REAL,
`five` REAL,
`fifteen` REAL,
`addtime` INTEGER
);
-58
View File
@@ -1,58 +0,0 @@
server
{
listen {$PORT};
listen [::]:{$PORT};
server_name {$SERVER_NAME};
index index.php index.html index.htm default.php default.htm default.html;
root {$ROOT_DIR};
#SSL-START
#error_page 404/404.html;
#SSL-END
#301-START
#PROXY-START
#ERROR-PAGE-START
#error_page 404 /404.html;
#error_page 502 /502.html;
#ERROR-PAGE-END
#PHP-INFO-START
include {$PHP_DIR}/conf/enable-php-{$PHPVER}.conf;
#PHP-INFO-END
#REWRITE-START
include {$OR_REWRITE}/{$SERVER_NAME}.conf;
#REWRITE-END
#禁止访问的文件或目录
location ~ ^/(\.user.ini|\.htaccess|\.git|\.svn|\.project|LICENSE|README.md)
{
return 404;
}
#一键申请SSL证书验证目录相关设置
location ~ \.well-known{
allow all;
}
location ~ .*\\.(gif|jpg|jpeg|png|bmp|swf)$
{
expires 30d;
error_log off;
access_log /dev/null;
}
location ~ .*\\.(js|css)?$
{
expires 12h;
error_log off;
access_log /dev/null;
}
access_log {$LOGPATH}/{$SERVER_NAME}.log main;
error_log {$LOGPATH}/{$SERVER_NAME}.error.log;
}
-55
View File
@@ -1,55 +0,0 @@
#BINDING-{$DIRBIND}-START
server
{
listen {$PORT};
server_name {$DIRBIND};
index index.php index.html index.htm default.php default.htm default.html;
root {$ROOT_DIR};
#SSL-START
#error_page 404/404.html;
#SSL-END
#ERROR-PAGE-START
#error_page 404 /404.html;
#error_page 502 /502.html;
#ERROR-PAGE-END
#PHP-INFO-START
include {$PHP_DIR}/conf/enable-php-{$PHPVER}.conf;
#PHP-INFO-END
#REWRITE-START
include {$OR_REWRITE}/{$SERVER_MAIN}.conf;
#REWRITE-END
#禁止访问的文件或目录
location ~ ^/(\.user.ini|\.htaccess|\.git|\.svn|\.project|LICENSE|README.md)
{
return 404;
}
#一键申请SSL证书验证目录相关设置
location ~ \.well-known{
allow all;
}
location ~ .*\\.(gif|jpg|jpeg|png|bmp|swf)$
{
expires 30d;
error_log off;
access_log /dev/null;
}
location ~ .*\\.(js|css)?$
{
expires 12h;
error_log off;
access_log /dev/null;
}
access_log {$LOGPATH}/{$SERVER_MAIN}_{$DIRBIND}.log;
error_log {$LOGPATH}/{$SERVER_MAIN}_{$DIRBIND}.error.log;
}
#BINDING-{$DIRBIND}-END
-23
View File
@@ -1,23 +0,0 @@
server
{
listen {$PORT};
listen [::]:{$PORT};
server_name {$SERVER_NAME};
index index.php index.html index.htm default.php default.htm default.html;
#SSL-START
#error_page 404/404.html;
#SSL-END
#PROXY-START
location ^~ / {
proxy_pass http://0.0.0.0:{$PANAL_PORT}/;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection 'upgrade';
}
#PROXY-END
error_log {$LOGPATH}/{$SERVER_NAME}.error.log;
}
-62
View File
@@ -1,62 +0,0 @@
FROM debian:10.12-slim
STOPSIGNAL SIGRTMIN+3
RUN rm -f /lib/systemd/system/multi-user.target.wants/* \
/etc/systemd/system/*.wants/* \
/lib/systemd/system/local-fs.target.wants/* \
/lib/systemd/system/sockets.target.wants/*udev* \
/lib/systemd/system/sockets.target.wants/*initctl* \
/lib/systemd/system/sysinit.target.wants/systemd-tmpfiles-setup* \
/lib/systemd/system/systemd-update-utmp*
RUN apt update -y && \
apt install -y devscripts && \
apt install -y wget zip unzip && \
apt-get install -y locales && \
localedef -i en_US -c -f UTF-8 -A /usr/share/locale/locale.alias en_US.UTF-8 && \
locale-gen en_US.UTF-8 && \
export LC_ALL=en_US.UTF-8 && \
export LANG=en_US.UTF-8 && \
export LANGUAGE=en_US.UTF-8 && \
localedef -i en_US -f UTF-8 en_US.UTF-8 && \
mkdir -p /www/server && \
mkdir -p /www/wwwroot && \
mkdir -p /www/wwwlogs && \
mkdir -p /www/backup/database && \
mkdir -p /www/backup/site && \
wget -O /tmp/dev.zip https://github.com/midoks/mdserver-web/archive/refs/heads/dev.zip && \
cd /tmp && unzip /tmp/dev.zip && \
mv -f /tmp/mdserver-web-dev /www/server/mdserver-web && \
rm -rf /tmp/dev.zip && \
rm -rf /tmp/mdserver-web-dev && \
cd /www/server/mdserver-web && \
bash scripts/install/debian.sh && \
cd /www/server/mdserver-web/ && \
/www/server/mdserver-web/bin/python tools.py username username && \
cd /www/server/mdserver-web/ && \
/www/server/mdserver-web/bin/python tools.py panel password
RUN cd /www/server/mdserver-web/plugins/php && \
bash install.sh install 74 && \
cd /www/server/mdserver-web/plugins/openresty && \
bash install.sh install 1.21.4.1 && \
cd /www/server/mdserver-web/plugins/mysql && \
bash install.sh install 5.6 && \
cd /www/server/mdserver-web/plugins/phpmyadmin && \
bash install.sh install 4.4.15 && \
systemctl enable openresty && \
systemctl enable php74 && \
systemctl enable mysql
RUN rm -rf /www/server/mysql/data
ADD ./start.sh /start.sh
ADD start.service /usr/lib/systemd/system/start.service
#RUN systemctl enable start
CMD [ "/lib/systemd/systemd", "log-level=info", "unit=sysinit.target" ]
EXPOSE 7200 80 443 888
VOLUME [ "/www" ]
-9
View File
@@ -1,9 +0,0 @@
[Unit]
Description=Customize the script
After=network.target
[Service]
ExecStart=/start.sh
[Install]
WantedBy=multi-user.target
-1
View File
@@ -1 +0,0 @@
#!/bin/bash
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>
-420
View File
@@ -1,420 +0,0 @@
# coding:utf-8
'''
doc: https://docs.python.org/zh-cn/3/library/ftplib.html
'''
import sys
import io
import os
import time
import re
import json
import paramiko
import ftplib
sys.path.append(os.getcwd() + "/class/core")
import mw
DEBUG = True
BLOCK_SIZE = 1024 * 1024 * 2
# BLOCK_SIZE = 50
PROGRESS_FILE_NAME = "PROGRESS_FILE_NAME"
"""
=============自定义异常===================
"""
class OsError(Exception):
"""OS端异常"""
class ObjectNotFound(OsError):
"""对象不存在时抛出的异常"""
def __init__(self, *args, **kwargs):
message = "文件对象不存在。"
super(ObjectNotFound, self).__init__(message, *args, **kwargs)
class APIError(Exception):
"""API参数错误异常"""
def __init__(self, *args, **kwargs):
_api_error_msg = 'API资料校验失败,请核实!'
super(APIError, self).__init__(_api_error_msg, *args, **kwargs)
class FtpPSClient:
_title = "FTP"
_name = "ftp"
__host = None
__port = None
__user = None
__password = None
default_port = 21
default_backup_path = "/backup/"
config_file = "cfg.json"
def __init__(self, load_config=True, timeout=10):
self.timeout = timeout
if load_config:
data = self.get_config()
self.injection_config(data)
def get_config(self):
default_config = {
"ftp_host": '',
"ftp_user": '',
"ftp_pass": '',
"backup_path": self.default_backup_path
}
cfg = mw.getServerDir() + "/backup_ftp/" + self.config_file
if os.path.exists(cfg):
data = mw.readFile(cfg)
return json.loads(data)
else:
return default_config
def injection_config(self, data):
self.__host = host = data["ftp_host"].strip()
if host.find(':') == -1:
self.__port = self.default_port
else:
self.__host = host.split(':')[0]
self.__port = host.split(':')[1]
self.__user = data['ftp_user'].strip()
self.__password = data['ftp_pass'].strip()
bp = data['backup_path'].strip()
if bp:
self.backup_path = self.getPath(bp)
else:
self.backup_path = self.getPath(self.default_backup_path)
def authorize(self):
try:
if self.timeout is not None:
ftp = ftplib.FTP(timeout=self.timeout)
else:
ftp = ftplib.FTP()
debuglevel = 0
# if DEBUG:
# debuglevel = 3
ftp.set_debuglevel(debuglevel)
# ftp.set_pasv(True)
ftp.connect(self.__host, int(self.__port))
ftp.login(self.__user, self.__password)
return ftp
except Exception as e:
raise OsError("无法连接FTP客户端,请检查配置参数是否正确!")
# 取目录路径
def getPath(self, path):
if path[-1:] != '/':
path += '/'
if path[:1] != '/':
path = '/' + path
return path.replace('//', '/')
def generateDownloadUrl(self, object_name):
return 'ftp://' + \
self.__user + ':' + \
self.__password + '@' + \
self.__host + ':' + \
"/" + object_name
def buildDirName(self, data_type, file_name):
import re
prefix_dict = {
"site": "web",
"database": "db",
"path": "path",
}
file_regx = prefix_dict.get(data_type) + "_(.+)_20\d+_\d+\."
sub_search = re.search(file_regx, file_name)
sub_path_name = ""
if sub_search:
sub_path_name = sub_search.groups()[0]
sub_path_name += '/'
# 构建OS存储路径
object_name = self.backup_path + \
data_type + '/' + \
sub_path_name + \
file_name
return object_name
def uploadFile(self, filename, data_type=None, *args, **kwargs):
client = self.authorize()
local_file_name = filename
filename = os.path.abspath(filename)
dirname = os.path.dirname(filename)
temp_name = os.path.split(filename)[1]
object_name = self.buildDirName(data_type, temp_name)
upload_tmp_dir = os.path.join(dirname, ".upload_tmp")
if not os.path.exists(upload_tmp_dir):
os.mkdir(upload_tmp_dir)
print("|-正在上传文件到 {}".format(object_name))
total_bytes = os.path.getsize(filename)
object_md5_name = mw.md5(object_name)
pg_file = os.path.join(upload_tmp_dir, object_md5_name + ".pl")
block_size = BLOCK_SIZE
if kwargs.get("block_size"):
try:
block_size = float(kwargs.get("block_size"))
except:
pass
remote_file_size = None
if not os.path.exists(pg_file):
# import uuid
# uid = str(uuid.uuid1())
progress_info = {
"filename": local_file_name,
"total_bytes": total_bytes,
"uploaded_bytes": 0,
}
mw.writeFile(pg_file, json.dumps(progress_info))
else:
progress_info = json.loads(mw.readFile(pg_file))
if total_bytes == progress_info.get("total_bytes"):
# 取远程文件大小
_max_loop = 10
while _max_loop > 0:
try:
time.sleep(1)
remote_file_size = client.size(object_name)
if remote_file_size > total_bytes:
remote_file_size = None
break
except Exception as e:
if DEBUG:
print(type(e))
print(e)
_max_loop -= 1
else:
remote_file_size = None
uploaded_bytes = 0 if remote_file_size is None else remote_file_size
dir_name = os.path.split(object_name)[0]
if dir_name:
self.createDirP(dir_name)
upload_start = time.time()
try:
if total_bytes > 1024 * 1024 * 1024:
with open(local_file_name, 'rb') as file_handler:
if remote_file_size is not None:
file_handler.seek(remote_file_size)
client.voidcmd("TYPE I")
datasock = ''
esize = ''
datasock, esize = client.ntransfercmd(
"STOR " + object_name, remote_file_size)
while True:
buf = file_handler.read(block_size)
if not len(buf):
break
datasock.sendall(buf)
uploaded_bytes += len(buf)
if DEBUG:
print('\ruploading %.2f%%' %
(float(uploaded_bytes) / total_bytes * 100))
print("uploaded_bytes", uploaded_bytes)
if uploaded_bytes == total_bytes:
break
datasock.close()
if DEBUG:
print('close data handle')
try:
client.voidcmd('NOOP')
except Exception as e:
if DEBUG:
print("Send NOOP command error:")
print(e)
else:
if DEBUG:
print('keep alive cmd success')
client.voidresp()
if DEBUG:
print('No loop cmd')
else:
# 小于1G文件直接上传
file_handler = open(local_file_name, "rb")
client.storbinary('STOR %s' % object_name,
file_handler, blocksize=block_size)
file_handler.close()
except Exception as e:
print(str(e))
completed_file_size = None
_max_loop = 10
while _max_loop > 0:
try:
time.sleep(1)
completed_file_size = client.size(object_name)
break
except Exception as e:
_max_loop -= 1
if DEBUG:
print("size error:" + str(e))
# 上传完成
if completed_file_size == total_bytes:
if DEBUG:
upload_completed = time.time()
upload_diff = upload_completed - upload_start
print("文件上传成功, 耗时: {}s。".format(upload_diff))
if os.path.exists(pg_file):
os.remove(pg_file)
return True
else:
if os.path.exists(pg_file):
os.remove(pg_file)
print("文件上传后大小不一致!")
print("completed_file_size:" + str(completed_file_size))
print("total_bytes:", total_bytes)
print("object_md5_name:", object_md5_name)
print("pg_file:", pg_file)
print("filename:", filename)
print("dirname:", dirname)
print("object_name:", object_name)
return False
def createDirP(self, dir_name):
"""创建远程目录
:param dir_name: 目录名称
:return:
"""
try:
dirnames = dir_name.split('/')
ftp = self.authorize()
# ftp.cwd(get.path);
for dirname in dirnames:
if not dirname or not dirname.strip():
continue
try:
flist = ftp.nlst()
if not dirname in flist:
ftp.mkd(dirname)
except:
# print("mlsd mode.")
try:
flist = list(ftp.mlsd())[1:]
for f in flist:
if dirname == f[0]:
break
else:
ftp.mkd(dirname)
except:
return False
ftp.cwd(dirname)
return True
except:
return False
def createDir(self, path, name):
ftp = self.authorize()
path = self.getPath(path)
ftp.cwd(path)
try:
ftp.mkd(name)
ftp.close()
return True
except Exception as e:
print(str(e))
ftp.close()
return False
def deleteDir(self, path, dir_name):
try:
ftp = self.authorize()
ftp.rmd(dir_name)
return True
except ftplib.error_perm as e:
print("deleteDir:" + str(e) + ":" + dir_name)
except Exception as e:
print(e)
return False
def deleteFile(self, filename):
try:
ftp = self.authorize()
ftp.delete(filename)
return True
except Exception as e:
print(str(e))
return False
def getList(self, path="/"):
ftp = self.authorize()
path = self.getPath(path)
ftp.cwd(path)
mlsd = False
try:
files = list(ftp.mlsd())
mlsd = True
except:
try:
files = ftp.nlst(path)
mlsd = False
except:
raise RuntimeError("FTP服务器数据返回异常!")
ftp.close()
# print(files)
f_list = []
dirs = []
data = []
default_time = '1971/01/01 01:01:01'
for dt in files:
# print(dt)
if mlsd:
dt_name = dt[0]
dt_info = dt[1]
else:
if dt.find("/") >= 0:
dt = dt.split("/")[-1]
tmp = {}
tmp['name'] = dt_name
if dt_name == '.' or dt_name == '..':
continue
tmp['time'] = dt_info['modify']
try:
tmp['size'] = dt_info['size']
tmp['type'] = "File"
tmp['download'] = self.generateDownloadUrl(path + dt_name)
f_list.append(tmp)
except:
tmp['size'] = dt_info['sizd']
tmp['type'] = None
tmp['download'] = ''
dirs.append(tmp)
data = dirs + f_list
mlist = {}
mlist['path'] = path
mlist['list'] = data
return mlist
Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

-101
View File
@@ -1,101 +0,0 @@
<style>
.upyunCon {
height: 428px;
}
.up-place {
height: 62px;
border-bottom: 1px solid #ddd;
}
.up-place .btn {
border-radius: 0;
}
.up-place .place-input {
background-color: #f3f3f3;
border: 1px solid #ccc;
height: 30px;
line-height: 28px;
overflow: hidden;
margin: 1px 0 0 -1px;
width: 340px;
}
.place-input ul {
display: inline-block;
position: relative;
width: auto;
}
.place-input ul li {
background: url("/static/img/ico/ico-ltr.png") no-repeat right center;
float: left;
padding-left: 10px;
padding-right: 18px;
}
.place-input ul li a {
height: 28px;
cursor: pointer;
display: inline-block;
}
.upyunlist {
height: 516px;
overflow: auto;
}
.up-bottom {
background-color: #fafafa;
border-top: 1px solid #eee;
bottom: 0;
position: absolute;
width: 100%;
}
.up-use {
line-height: 50px
}
.list-list .cursor span {
line-height: 30px;
}
.btn-title {
margin-top: 1px
}
.tip {
font-size: 10px;
font-style: oblique;
color: green;
}
</style>
<div class="upyunCon">
<div class="up-place pd15">
<button id="backBtn" class="btn btn-default btn-sm glyphicon glyphicon-arrow-left pull-left" title="后退"></button>
<input id="myPath" style="display:none;" type="text" value="">
<div class="place-input pull-left">
<div style="width:1400px;height:28px"><ul></ul></div>
</div>
<button class="refreshBtn btn btn-default btn-sm glyphicon glyphicon-refresh pull-left mr20" title="刷新" style="margin-left:-1px;"></button>
<button class="btn btn-default btn-sm pull-right btn-title" onclick="upyunApi()">帐户设置</button>
<button class="btn btn-default btn-sm pull-right mr20 btn-title" onclick="createDir()">新建文件夹</button>
</div>
<div class="upyunlist pd15">
<div class="divtable" style="margin-bottom:15px">
<table class="table table-hover">
<thead><tr><th>名称</th><th>大小</th><th>更新时间</th><th class="text-right">操作</th></tr></thead>
<tbody class="list-list"></tbody>
</table>
</div>
</div>
</div>
<script type="text/javascript">
$.getScript( "/plugins/file?name=backup_ftp&f=js/backup_ftp.js", function(){
osList('/');
});
</script>
-268
View File
@@ -1,268 +0,0 @@
# coding:utf-8
import sys
import io
import os
import time
import re
import json
# print(sys.platform)
if sys.platform != "darwin":
os.chdir("/www/server/mdserver-web")
sys.path.append(os.getcwd() + "/class/core")
import mw
_ver = sys.version_info
is_py2 = (_ver[0] == 2)
is_py3 = (_ver[0] == 3)
DEBUG = False
if is_py2:
reload(sys)
sys.setdefaultencoding('utf-8')
app_debug = False
if mw.isAppleSystem():
app_debug = True
def getPluginName():
return 'backup_ftp'
def getPluginDir():
return mw.getPluginDir() + '/' + getPluginName()
sys.path.append(getPluginDir() + "/class")
from ftp_client import FtpPSClient
def getServerDir():
return mw.getServerDir() + '/' + getPluginName()
def getArgs():
args = sys.argv[2:]
tmp = {}
args_len = len(args)
if args_len == 1:
t = args[0].strip('{').strip('}')
t = t.split(':')
tmp[t[0]] = t[1]
elif args_len > 1:
for i in range(len(args)):
t = args[i].split(':', 1)
tmp[t[0]] = t[1]
return tmp
def checkArgs(data, ck=[]):
for i in range(len(ck)):
if not ck[i] in data:
return (False, mw.returnJson(False, '参数:(' + ck[i] + ')没有!'))
return (True, mw.returnJson(True, 'ok'))
def status():
return 'start'
def getConf():
cfg = getServerDir() + "/cfg.json"
if not os.path.exists(cfg):
return mw.returnJson(False, "未配置", [])
data = mw.readFile(cfg)
data = json.loads(data)
return mw.returnJson(True, "OK", data)
def setConf():
args = getArgs()
data = checkArgs(args, ['use_sftp', 'ftp_user',
'ftp_pass', 'ftp_host', 'backup_path'])
if not data[0]:
return data[1]
cfg = getServerDir() + "/cfg.json"
values = ['ftp_user',
'ftp_pass',
'ftp_host']
for v in values:
if args[v] == '':
return mw.returnJson(False, '必填资料不能为空,请核实!', [])
if args['backup_path'] == '':
args['backup_path'] = "/backup"
try:
ftp = FtpPSClient(load_config=False)
ftp.injection_config(args)
data = ftp.getList("/")
if data:
mw.writeFile(cfg, mw.getJson(args))
return mw.returnJson(True, '设置成功', [])
except Exception as e:
return mw.returnJson(False, "FTP校验失败,请核实!\n" + str(e), [])
def getList():
cfg = getServerDir() + "/cfg.json"
if not os.path.exists(cfg):
return mw.returnJson(False, "未配置FTP,请点击`账户设置`", [])
args = getArgs()
data = checkArgs(args, ['path'])
if not data[0]:
return data[1]
try:
ftp = FtpPSClient()
flist = ftp.getList(args['path'])
return mw.returnJson(True, "ok", flist)
except Exception as e:
return mw.returnJson(False, str(e), [])
def createDir():
cfg = getServerDir() + "/cfg.json"
if not os.path.exists(cfg):
return mw.returnJson(False, "未配置FTP,请点击`账户设置`", [])
args = getArgs()
data = checkArgs(args, ['path', 'name'])
if not data[0]:
return data[1]
ftp = FtpPSClient()
isok = ftp.createDir(args['path'], args['name'])
if isok:
return mw.returnJson(True, "创建成功")
return mw.returnJson(False, "创建失败")
def deleteDir():
args = getArgs()
data = checkArgs(args, ['dir_name', 'path'])
if not data[0]:
return data[1]
ftp = FtpPSClient()
isok = ftp.deleteDir(args['path'], args['dir_name'])
if isok:
return mw.returnJson(True, "删除成功")
return mw.returnJson(False, "删除失败")
def deleteFile():
args = getArgs()
data = checkArgs(args, ['path', 'filename'])
if not data[0]:
return data[1]
ftp = FtpPSClient()
isok = ftp.deleteFile(args['path'] + "/" + args['filename'])
if isok:
return mw.returnJson(True, "删除成功")
return mw.returnJson(False, "删除失败")
def backupAllFunc(stype):
os.chdir(mw.getRunDir())
name = sys.argv[2]
num = sys.argv[3]
prefix_dict = {
"site": "web",
"database": "db",
"path": "path",
}
args = stype + " " + name + " " + num
cmd = 'python3 ' + mw.getRunDir() + '/scripts/backup.py ' + args
if stype.find('database_') > -1:
plugin_name = stype.replace('database_', '')
args = "database " + name + " " + num
cmd = 'python3 ' + mw.getRunDir() + '/plugins/' + plugin_name + \
'/scripts/backup.py ' + args
os.system(cmd)
# 开始执行上传信息.
if stype.find('database_') > -1:
bk_name = 'database'
plugin_name = stype.replace('database_', '')
bk_prefix = plugin_name + '/db'
stype = 'database'
else:
bk_prefix = prefix_dict[stype]
bk_name = stype
find_path = mw.getBackupDir() + '/' + bk_name + '/' + bk_prefix + '_' + name
find_new_file = "ls " + find_path + \
"_* | grep '.gz' | cut -d \ -f 1 | awk 'END {print}'"
filename = mw.execShell(find_new_file)[0].strip()
if filename == "":
mw.echoInfo("not find upload file!")
return False
print("|-准备上传文件 {}".format(filename))
ftp = FtpPSClient()
ftp.uploadFile(filename, stype)
return ''
def backupSite():
# 备份站点
pass
def in_array(name, arr=[]):
for x in arr:
if name == x:
return True
return False
def installPreInspection():
return 'ok'
if __name__ == "__main__":
func = sys.argv[1]
if func == 'status':
print(status())
elif func == 'start':
print(start())
elif func == 'stop':
print(stop())
elif func == 'restart':
print(restart())
elif func == 'reload':
print(reload())
elif func == 'install_pre_inspection':
print(installPreInspection())
elif func == 'conf':
print(getConf())
elif func == 'set_config':
print(setConf())
elif func == "get_list":
print(getList())
elif func == "create_dir":
print(createDir())
elif func == "delete_dir":
print(deleteDir())
elif func == 'delete_file':
print(deleteFile())
elif in_array(func, ['site', 'database', 'path']) or func.find('database_') > -1:
print(backupAllFunc(func))
else:
print('error')
-17
View File
@@ -1,17 +0,0 @@
{
"title":"FTP存储空间",
"hook":["backup"],
"tip":"soft",
"name":"backup_ftp",
"type":"运行环境",
"ps":"将网站或数据库打包备份到FTP存储空间",
"versions":["1.0"],
"install_pre_inspection":false,
"shell":"install.sh",
"checks":"server/backup_ftp",
"path": "server/backup_ftp",
"author":"midoks",
"home":"https://github.com/midoks/mdserver-web",
"date":"2022-10-23",
"pid": "4"
}
-32
View File
@@ -1,32 +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")
install_tmp=${rootPath}/tmp/mw_install.pl
sys_os=`uname`
Install_App()
{
mkdir -p ${serverPath}/backup_ftp
echo "${1}" > ${serverPath}/backup_ftp/version.pl
echo '安装完成' > $install_tmp
}
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

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

Before

Width:  |  Height:  |  Size: 26 KiB

-24
View File
@@ -1,24 +0,0 @@
<div class="bt-form">
<div class="bt-w-main">
<div class="bt-w-menu">
<p class="bgw" onclick="pluginService('gitea');">服务</p>
<p onclick="pluginInitD('gitea');">自启动</p>
<!-- <p onclick="pluginConfig('gitea',null, 'init_conf');">启动配置</p> -->
<p onclick="gogsEdit();">手动编辑</p>
<p onclick="pluginConfig('gitea',null, 'conf');">配置文件</p>
<p onclick="gogsSetConfig();">配置修改</p>
<p onclick="giteaUserList();">用户列表</p>
<!-- <p onclick="pluginLogs('gitea',null,'run_log');">运行日志</p> -->
<!-- <p onclick="pluginLogs('gitea',null,'post_receive_log');" title="提交post-receive日志">提交日志</p> -->
<p onclick="giteaRead();">使用说明</p>
</div>
<div class="bt-w-con pd15">
<div class="soft-man-con"></div>
</div>
</div>
</div>
<script type="text/javascript">
$.getScript( "/plugins/file?name=gitea&f=js/gitea.js",function(){
pluginService('gitea');
});
</script>
-767
View File
@@ -1,767 +0,0 @@
# coding: utf-8
import time
import os
import sys
import re
sys.path.append(os.getcwd() + "/class/core")
import mw
# cmd = 'ls /usr/local/lib/ | grep python | cut -d \\ -f 1 | awk \'END {print}\''
# info = mw.execShell(cmd)
# p = "/usr/local/lib/" + info[0].strip() + "/site-packages"
# sys.path.append(p)
app_debug = False
if mw.isAppleSystem():
app_debug = True
def getPluginName():
return 'gitea'
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 getArgs():
args = sys.argv[2:]
tmp = {}
args_len = len(args)
if args_len == 1:
t = args[0].strip('{').strip('}')
t = t.split(':', 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 getInitdConfTpl():
path = getPluginDir() + "/init.d/gitea.tpl"
return path
def getInitdConf():
path = getServerDir() + "/init.d/gitea"
return path
def getConf():
path = getServerDir() + "/custom/conf/app.ini"
if not os.path.exists(path):
return mw.returnJson(False, "请先安装初始化!<br/>默认地址:http://" + mw.getLocalIp() + ":3000")
return path
def getConfTpl():
path = getPluginDir() + "/conf/app.ini"
return path
def status():
data = mw.execShell(
"ps -ef|grep " + getPluginName() + " |grep -v grep | grep -v python | awk '{print $2}'")
if data[0] == '':
return 'stop'
return 'start'
def getHomeDir():
if mw.isAppleSystem():
user = mw.execShell(
"who | sed -n '2, 1p' |awk '{print $1}'")[0].strip()
return '/Users/' + user
else:
return 'www'
def getRunUser():
if mw.isAppleSystem():
user = mw.execShell(
"who | sed -n '2, 1p' |awk '{print $1}'")[0].strip()
return user
else:
return 'www'
__SR = '''#!/bin/bash
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH
export USER=%s
export HOME=%s && ''' % ( getRunUser(), getHomeDir())
def contentReplace(content):
service_path = mw.getServerDir()
content = content.replace('{$ROOT_PATH}', mw.getRootDir())
content = content.replace('{$SERVER_PATH}', service_path)
content = content.replace('{$RUN_USER}', getRunUser())
content = content.replace('{$HOME_DIR}', getHomeDir())
return content
def initDreplace():
file_tpl = getInitdConfTpl()
service_path = mw.getServerDir()
git_dir = mw.getServerDir() + '/git'
if not os.path.exists(git_dir):
mw.execShell('mkdir -p ' + git_dir)
initD_path = getServerDir() + '/init.d'
if not os.path.exists(initD_path):
os.mkdir(initD_path)
file_bin = initD_path + '/' + getPluginName()
if not os.path.exists(file_bin):
content = mw.readFile(file_tpl)
content = contentReplace(content)
mw.writeFile(file_bin, content)
mw.execShell('chmod +x ' + file_bin)
# systemd
systemDir = mw.systemdCfgDir()
systemService = systemDir + '/gitea.service'
systemServiceTpl = getPluginDir() + '/init.d/gitea.service.tpl'
if os.path.exists(systemDir) and not os.path.exists(systemService):
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')
log_path = getServerDir() + '/log'
if not os.path.exists(log_path):
os.mkdir(log_path)
return file_bin
def getRootUrl():
content = mw.readFile(getConf())
rep = 'ROOT_URL\s*=\s*(.*)'
tmp = re.search(rep, content)
if tmp:
return tmp.groups()[0]
rep = 'EXTERNAL_URL\s*=\s*(.*)'
tmp = re.search(rep, content)
if tmp:
return tmp.groups()[0]
return ''
def getSshPort():
content = mw.readFile(getConf())
rep = 'SSH_PORT\s*=\s*(.*)'
tmp = re.search(rep, content)
if not tmp:
return ''
return tmp.groups()[0]
def getHttpPort():
content = mw.readFile(getConf())
rep = 'HTTP_PORT\s*=\s*(.*)'
tmp = re.search(rep, content)
if not tmp:
return ''
return tmp.groups()[0]
def getRootPath():
content = mw.readFile(getConf())
rep = 'ROOT\s*=\s*(.*)'
tmp = re.search(rep, content)
if not tmp:
return ''
return tmp.groups()[0]
def getDbConfValue():
conf = getConf()
if not os.path.exists(conf):
return {}
content = mw.readFile(conf)
rep_scope = "\[database\](.*?)\["
tmp = re.findall(rep_scope, content, re.S)
rep = '(\w*)\s*=\s*(.*)'
tmp = re.findall(rep, tmp[0])
r = {}
for x in range(len(tmp)):
k = tmp[x][0]
v = tmp[x][1]
r[k] = v
return r
def pMysqlDb(conf):
host = conf['HOST'].split(':')
# pymysql
db = mw.getMyORM()
# MySQLdb |
# db = mw.getMyORMDb()
db.setPort(int(host[1]))
db.setUser(conf['USER'])
if 'PASSWD' in conf:
db.setPwd(conf['PASSWD'])
else:
db.setPwd(conf['PASSWORD'])
db.setDbName(conf['NAME'])
# db.setSocket(getSocketFile())
db.setCharset("utf8")
return db
def pSqliteDb(conf):
# print(conf)
import db
psDb = db.Sql()
# 默认
gsdir = getServerDir() + '/data'
dbname = 'gitea'
if conf['PATH'][0] == '/':
# 绝对路径
pass
else:
path = conf['PATH'].split('/')
gsdir = getServerDir() + '/' + path[0]
dbname = path[1].split('.')[0]
# print(gsdir, dbname)
psDb.dbPos(gsdir, dbname)
return psDb
def getGiteaDbType(conf):
if 'DB_TYPE' in conf:
return conf['DB_TYPE']
if 'TYPE' in conf:
return conf['TYPE']
return 'NONE'
def pQuery(sql):
conf = getDbConfValue()
gtype = getGiteaDbType(conf)
if gtype == 'sqlite3':
db = pSqliteDb(conf)
data = db.query(sql, []).fetchall()
return data
elif gtype == 'mysql':
db = pMysqlDb(conf)
return db.query(sql)
print("仅支持mysql|sqlite3配置")
exit(0)
def isSqlError(mysqlMsg):
# 检测数据库执行错误
_mysqlMsg = str(mysqlMsg)
# print _mysqlMsg
if "MySQLdb" in _mysqlMsg:
return mw.returnData(False, 'MySQLdb组件缺失! <br>进入SSH命令行输入: pip install mysql-python')
if "2002," in _mysqlMsg:
return mw.returnData(False, '数据库连接失败,请检查数据库服务是否启动!')
if "using password:" in _mysqlMsg:
return mw.returnData(False, '数据库管理密码错误!')
if "Connection refused" in _mysqlMsg:
return mw.returnData(False, '数据库连接失败,请检查数据库服务是否启动!')
if "1133," in _mysqlMsg:
return mw.returnData(False, '数据库用户不存在!')
if "1007," in _mysqlMsg:
return mw.returnData(False, '数据库已经存在!')
if "1044," in _mysqlMsg:
return mw.returnData(False, mysqlMsg[1])
if "2003," in _mysqlMsg:
return mw.returnData(False, "Can't connect to MySQL server on '127.0.0.1' (61)")
return mw.returnData(True, 'OK')
def appOp(method):
file = initDreplace()
if not mw.isAppleSystem():
data = mw.execShell('systemctl ' + method + ' ' + getPluginName())
if data[1] == '':
return 'ok'
return 'fail'
data = mw.execShell(__SR + file + ' ' + method)
if data[1] == '':
return 'ok'
return data[0]
def start():
return appOp('start')
def stop():
return appOp('stop')
def restart():
return appOp('restart')
def reload():
return appOp('reload')
def initdStatus():
if mw.isAppleSystem():
return "Apple Computer does not support"
shell_cmd = 'systemctl status gitea | grep loaded | grep "enabled;"'
data = mw.execShell(shell_cmd)
if data[0] == '':
return 'fail'
return 'ok'
def initdInstall():
if mw.isAppleSystem():
return "Apple Computer does not support"
mw.execShell('systemctl enable gitea')
return 'ok'
def initdUinstall():
if mw.isAppleSystem():
return "Apple Computer does not support"
mw.execShell('systemctl disable gitea')
return 'ok'
def runLog():
log_path = getServerDir() + '/log/gitea.log'
return log_path
def postReceiveLog():
log_path = getServerDir() + '/log/hooks/post-receive.log'
return log_path
def getGogsConf():
conf = getConf()
if not os.path.exists(conf):
return mw.returnJson(False, "请先安装初始化!<br/>默认地址:http://" + mw.getLocalIp() + ":3000")
gets = [
{'name': 'DOMAIN', 'type': -1, 'ps': '服务器域名'},
{'name': 'ROOT_URL', 'type': -1, 'ps': '公开的完整URL路径'},
{'name': 'HTTP_ADDR', 'type': -1, 'ps': '应用HTTP监听地址'},
{'name': 'HTTP_PORT', 'type': -1, 'ps': '应用 HTTP 监听端口号'},
{'name': 'START_SSH_SERVER', 'type': 2, 'ps': '启动内置SSH服务器'},
{'name': 'SSH_PORT', 'type': -1, 'ps': 'SSH 端口号'},
{'name': 'REQUIRE_SIGNIN_VIEW', 'type': 2, 'ps': '强制登录浏览'},
{'name': 'ENABLE_CAPTCHA', 'type': 2, 'ps': '启用验证码服务'},
{'name': 'DISABLE_REGISTRATION', 'type': 2, 'ps': '禁止注册,只能由管理员创建帐号'},
{'name': 'ENABLE_NOTIFY_MAIL', 'type': 2, 'ps': '是否开启邮件通知'},
{'name': 'FORCE_PRIVATE', 'type': 2, 'ps': '强制要求所有新建的仓库都是私有'},
{'name': 'SHOW_FOOTER_BRANDING', 'type': 2, 'ps': 'Gogs推广信息'},
{'name': 'SHOW_FOOTER_VERSION', 'type': 2, 'ps': 'Gogs版本信息'},
{'name': 'SHOW_FOOTER_TEMPLATE_LOAD_TIME', 'type': 2, 'ps': 'Gogs模板加载时间'},
]
conf = mw.readFile(conf)
result = []
for g in gets:
rep = g['name'] + '\s*=\s*(.*)'
tmp = re.search(rep, conf)
if not tmp:
continue
g['value'] = tmp.groups()[0]
result.append(g)
return mw.returnJson(True, 'OK', result)
def submitGogsConf():
gets = ['DOMAIN',
'ROOT_URL',
'HTTP_ADDR',
'HTTP_PORT',
'START_SSH_SERVER',
'SSH_PORT',
'REQUIRE_SIGNIN_VIEW',
'FORCE_PRIVATE',
'ENABLE_CAPTCHA',
'DISABLE_REGISTRATION',
'ENABLE_NOTIFY_MAIL',
'SHOW_FOOTER_BRANDING',
'SHOW_FOOTER_VERSION',
'SHOW_FOOTER_TEMPLATE_LOAD_TIME']
args = getArgs()
filename = getConf()
conf = mw.readFile(filename)
for g in gets:
if g in args:
rep = g + '\s*=\s*(.*)'
val = g + ' = ' + args[g]
conf = re.sub(rep, val, conf)
mw.writeFile(filename, conf)
restart()
return mw.returnJson(True, '设置成功')
def userList():
conf = getConf()
if not os.path.exists(conf):
return mw.returnJson(False, "请先安装初始化!<br/>默认地址:http://" + mw.getLocalIp() + ":3000")
conf = getDbConfValue()
gtype = getGiteaDbType(conf)
if gtype != 'mysql':
return mw.returnJson(False, "仅支持mysql数据操作!")
import math
args = getArgs()
data = checkArgs(args, ['page', 'page_size'])
if not data[0]:
return data[1]
page = int(args['page'])
page_size = int(args['page_size'])
tojs = args['tojs']
search = ''
if 'search' in args:
search = args['search']
data = {}
data['root_url'] = getRootUrl()
start = (page - 1) * page_size
list_count = pQuery('select count(id) as num from user')
count = list_count[0]["num"]
list_data = pQuery(
'select id,name,email from user order by id desc limit ' + str(start) + ',' + str(page_size))
data['list'] = mw.getPage(
{'count': count, 'p': page, 'row': page_size, 'tojs': tojs})
data['page'] = page
data['page_size'] = page_size
data['page_count'] = int(math.ceil(count / page_size))
data['data'] = list_data
return mw.returnJson(True, 'OK', data)
def getAllUserProject(user, search=''):
path = getRootPath() + '/' + user
dlist = []
if os.path.exists(path):
for filename in os.listdir(path):
tmp = {}
filePath = path + '/' + filename
if os.path.isdir(filePath):
if search == '':
tmp['name'] = filename.replace('.git', '')
dlist.append(tmp)
else:
if filename.find(search) != -1:
tmp['name'] = filename.replace('.git', '')
dlist.append(tmp)
return dlist
def checkProjectListIsHasScript(user, data):
path = getRootPath() + '/' + user
for x in range(len(data)):
name = data[x]['name'] + '.git'
path_tmp = path + '/' + name + '/hooks/post-receive.d/post-receive'
if os.path.exists(path_tmp):
data[x]['has_hook'] = True
else:
data[x]['has_hook'] = False
return data
def userProjectList():
import math
args = getArgs()
# print args
page = 1
page_size = 5
search = ''
if not 'name' in args:
return mw.returnJson(False, '缺少参数name')
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']
data = {}
ulist = getAllUserProject(args['name'])
dlist_sum = len(ulist)
start = (page - 1) * page_size
ret_data = ulist[start:start + page_size]
ret_data = checkProjectListIsHasScript(args['name'], ret_data)
data['root_url'] = getRootUrl()
data['data'] = ret_data
data['args'] = args
data['list'] = mw.getPage(
{'count': dlist_sum, 'p': page, 'row': page_size, 'tojs': 'userProjectListPost'})
return mw.returnJson(True, 'OK', data)
def projectScriptEdit():
args = getArgs()
if not 'user' in args:
return mw.returnJson(True, 'username missing')
if not 'name' in args:
return mw.returnJson(True, 'project name missing')
user = args['user']
name = args['name'] + '.git'
post_receive = getRootPath() + '/' + user + '/' + name + \
'/custom_hooks/commit'
if os.path.exists(post_receive):
return mw.returnJson(True, 'OK', {'path': post_receive})
else:
return mw.returnJson(False, 'file does not exist')
def projectScriptLoad():
args = getArgs()
if not 'user' in args:
return mw.returnJson(True, 'username missing')
if not 'name' in args:
return mw.returnJson(True, 'project name missing')
user = args['user']
name = args['name'] + '.git'
path = getRootPath() + '/' + user + '/' + name
post_receive_tpl = getPluginDir() + '/hook/post-receive.tpl'
post_receive = path + '/hooks/post-receive.d/post-receive'
if not os.path.exists(path + '/custom_hooks'):
mw.execShell('mkdir -p ' + path + '/custom_hooks')
mw.execShell('chown -R www:www ' + path + '/custom_hooks')
pct_content = mw.readFile(post_receive_tpl)
pct_content = pct_content.replace('{$PATH}', path + '/custom_hooks')
mw.writeFile(post_receive, pct_content)
mw.execShell('chmod 777 ' + post_receive)
mw.execShell('chown -R www:www ' + post_receive)
commit_tpl = getPluginDir() + '/hook/commit.tpl'
commit = path + '/custom_hooks/commit'
codeDir = mw.getRootDir() + '/git'
cc_content = mw.readFile(commit_tpl)
gitPath = getRootPath()
cc_content = cc_content.replace('{$GITROOTURL}', gitPath)
cc_content = cc_content.replace('{$CODE_DIR}', codeDir)
cc_content = cc_content.replace('{$USERNAME}', user)
cc_content = cc_content.replace('{$PROJECT}', args['name'])
cc_content = cc_content.replace('{$WEB_ROOT}', mw.getWwwDir())
mw.writeFile(commit, cc_content)
mw.execShell('chmod 777 ' + commit)
mw.execShell('chown -R www:www ' + commit)
return 'ok'
def projectScriptUnload():
args = getArgs()
data = checkArgs(args, ['user', 'name'])
if not data[0]:
return data[1]
user = args['user']
name = args['name'] + '.git'
post_receive = getRootPath() + '/' + user + '/' + name + \
'/hooks/post-receive.d/post-receive'
mw.execShell('rm -f ' + post_receive)
commit = getRootPath() + '/' + user + '/' + name + \
'/custom_hooks/commit'
mw.execShell('rm -f ' + commit)
return 'ok'
def projectScriptDebug():
args = getArgs()
data = checkArgs(args, ['user', 'name'])
if not data[0]:
return data[1]
user = args['user']
name = args['name'] + '.git'
commit_log = getRootPath() + '/' + user + '/' + name + \
'/custom_hooks/sh.log'
data = {}
if os.path.exists(commit_log):
data['status'] = True
data['path'] = commit_log
else:
data['status'] = False
data['msg'] = '没有日志文件'
return mw.getJson(data)
def gogsEdit():
data = {}
data['post_receive'] = getPluginDir() + '/hook/post-receive.tpl'
data['commit'] = getPluginDir() + '/hook/commit.tpl'
return mw.getJson(data)
def getRsaPublic():
path = getHomeDir()
path += '/.ssh/id_rsa.pub'
content = mw.readFile(path)
data = {}
data['mw'] = content
return mw.getJson(data)
def getTotalStatistics():
st = status()
data = {}
if st.strip() == 'start':
list_count = pQuery('select count(id) as num from repository')
count = list_count[0]["num"]
data['status'] = True
data['count'] = count
data['ver'] = mw.readFile(getServerDir() + '/version.pl').strip()
return mw.returnJson(True, 'ok', data)
data['status'] = False
data['count'] = 0
return mw.returnJson(False, 'fail', data)
def uninstallPreInspection():
repo_dir = getServerDir() + "/data/gitea-repositories"
if not os.path.exists(repo_dir):
return 'ok'
dir_list = os.listdir(repo_dir)
if len(dir_list) > 0:
return "有项目数据!请手动删除Gitea<br/> rm -rf {}".format(getServerDir())
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 == 'initd_status':
print(initdStatus())
elif func == 'initd_install':
print(initdInstall())
elif func == 'initd_uninstall':
print(initdUinstall())
elif func == 'uninstall_pre_inspection':
print(uninstallPreInspection())
elif func == 'run_log':
print(runLog())
elif func == 'post_receive_log':
print(postReceiveLog())
elif func == 'conf':
print(getConf())
elif func == 'init_conf':
print(getInitdConf())
elif func == 'get_gogs_conf':
print(getGogsConf())
elif func == 'submit_gogs_conf':
print(submitGogsConf())
elif func == 'user_list':
print(userList())
elif func == 'user_project_list':
print(userProjectList())
elif func == 'project_script_edit':
print(projectScriptEdit())
elif func == 'project_script_load':
print(projectScriptLoad())
elif func == 'project_script_unload':
print(projectScriptUnload())
elif func == 'project_script_debug':
print(projectScriptDebug())
elif func == 'gogs_edit':
print(gogsEdit())
elif func == 'get_rsa_public':
print(getRsaPublic())
elif func == 'get_total_statistics':
print(getTotalStatistics())
else:
print('fail')
-18
View File
@@ -1,18 +0,0 @@
{
"ps": "Gitea 是一个开源社区驱动的轻量级代码托管解决方案。",
"name": "gitea",
"title": "Gitea",
"versions": ["1.17.2"],
"tip": "soft",
"install_pre_inspection":false,
"uninstall_pre_inspection":true,
"checks": "server/gitea",
"path":"server/gitea",
"author": "gitea",
"date": "2022-10-03",
"home": "https://dl.gitea.io/",
"type": "Git服务器",
"shell": "install.sh",
"pid": "3",
"sort": 7
}
-20
View File
@@ -1,20 +0,0 @@
[Unit]
Description=Gitea (Git with a cup of tea)
After=syslog.target
After=network.target
[Service]
RestartSec=2s
Type=simple
User=www
Group=www
WorkingDirectory={$SERVER_PATH}/gitea
ExecStart={$SERVER_PATH}/gitea/gitea web
Restart=always
Environment=USER=www HOME=/home/www GITEA_WORK_DIR={$SERVER_PATH}/gitea
RemainAfterExit=yes
#AmbientCapabilities=CAP_NET_BIND_SERVICE
#CapabilityBoundingSet=CAP_NET_BIND_SERVICE
[Install]
WantedBy=multi-user.target
-110
View File
@@ -1,110 +0,0 @@
#!/bin/sh
#
# /etc/rc.d/init.d/Gitea
#
# Runs the Gogs
#
#
# chkconfig: - 85 15
#
### BEGIN INIT INFO
# Provides: Gitea
# Required-Start: $remote_fs $syslog
# Required-Stop: $remote_fs $syslog
# Should-Start: mysql postgresql
# Should-Stop: mysql postgresql
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
# Short-Description: Start Gitea at boot time.
# Description: Control Gitea.
### END INIT INFO
# Source function library.
if [ -f /etc/init.d/functions ];then
. /etc/init.d/functions
fi
if [ -f /etc/rc.d/init.d/functions ];then
. /etc/rc.d/init.d/functions
fi
# Default values
export HOME={$HOME_DIR}
export USER={$RUN_USER}
NAME=gitea
GOGS_HOME={$SERVER_PATH}/gitea
GOGS_PATH=${GOGS_HOME}/$NAME
GOGS_USER={$RUN_USER}
SERVICENAME="gitea"
LOCKFILE=/tmp/gitea.lock
LOGPATH=${GOGS_HOME}/log
LOGFILE=${LOGPATH}/gitea.log
RETVAL=0
[ -r /etc/sysconfig/$NAME ] && . /etc/sysconfig/$NAME
DAEMON_OPTS="--check $NAME"
[ ! -z "$GOGS_USER" ] && DAEMON_OPTS="$DAEMON_OPTS --user=${GOGS_USER}"
status(){
isStart=`ps -ef|grep 'gitea web' |grep -v grep|awk '{print $2}'`
if [ "$isStart" == '' ];then
echo -e "${SERVICENAME} not running"
else
echo -e "${SERVICENAME}(pid $(echo $isStart)) already running"
fi
}
start() {
isStart=`ps -ef|grep 'gitea web' |grep -v grep|awk '{print $2}'`
if [ "$isStart" != '' ];then
echo "${SERVICENAME}(pid $(echo $isStart)) already running"
return $RETVAL
fi
cd ${GOGS_HOME}
echo -e "Starting ${SERVICENAME}: \c"
${GOGS_PATH} web > ${LOGFILE} 2>&1 &
RETVAL=$?
[ $RETVAL = 0 ] && touch ${LOCKFILE} && echo -e "\033[32mdone\033[0m"
return $RETVAL
}
stop() {
pids=`ps -ef|grep 'gitea web' |grep -v grep|awk '{print $2}'`
arr=($pids)
echo -e "Stopping gitea... \c"
for p in ${arr[@]}
do
kill -9 $p
done
echo -e "\033[32mdone\033[0m"
}
case "$1" in
start)
start
;;
stop)
stop
;;
status)
status
;;
restart)
stop
start
;;
reload)
stop
start
;;
*)
echo "Usage: ${NAME} {start|stop|status|restart}"
exit 1
;;
esac
exit $RETVAL
-128
View File
@@ -1,128 +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")
install_tmp=${rootPath}/tmp/mw_install.pl
if [ -f ${rootPath}/bin/activate ];then
source ${rootPath}/bin/activate
fi
URL_DOWNLOAD=https://dl.gitea.io/
bash ${rootPath}/scripts/getos.sh
OSNAME=`cat ${rootPath}/data/osname.pl`
OSNAME_ID=`cat /etc/*-release | grep VERSION_ID | awk -F = '{print $2}' | awk -F "\"" '{print $2}'`
getBit(){
echo `getconf LONG_BIT`
}
Install_Rsync(){
if [ "$OSNAME" == "debian" ] || [ "$OSNAME" == "ubuntu" ];then
apt install -y rsync
elif [[ "$OSNAME" == "arch" ]]; then
echo y | pacman -Sy rsync
elif [[ "$OSNAME" == "macos" ]]; then
# brew install rsync
# brew install lsyncd
echo "ok"
else
yum install -y rsync
fi
}
Install_App()
{
Install_Rsync
mkdir -p $serverPath/source/gitea
if id www &> /dev/null ;then
echo "www uid is `id -u www`"
echo "www shell is `grep "^www:" /etc/passwd |cut -d':' -f7 `"
else
groupadd www
useradd -g www www
fi
if [ "macos" != "$OSNAME" ];then
if [ ! -d /home/www ];then
mkdir -p /home/www
chown -R www:www /home/www
fi
fi
echo '正在安装脚本文件...' > $install_tmp
version=$1
git config --global push.default simple
if [ "macos" == "$OSNAME" ];then
file=gitea-${version}-darwin-10.12-amd64
else
file=gitea-${version}-linux-amd64
fi
file_xz="${file}.xz"
echo "wget -O $serverPath/source/gitea/$file_xz ${URL_DOWNLOAD}/gitea/${version}/${file_xz}"
if [ ! -f $serverPath/source/gitea/$file_xz ];then
wget --no-check-certificate -O $serverPath/source/gitea/$file_xz ${URL_DOWNLOAD}/gitea/${version}/${file_xz}
fi
cd $serverPath/source/gitea && xz -k -d $file_xz
if [ -f $file ];then
mkdir -p $serverPath/gitea
mv $serverPath/source/gitea/$file $serverPath/gitea/gitea
chmod +x $serverPath/gitea/gitea
chown -R www:www $serverPath/gitea
fi
if [ -d $serverPath/gitea ];then
echo $version > $serverPath/gitea/version.pl
cd ${rootPath} && python3 plugins/gitea/index.py start
cd ${rootPath} && python3 plugins/gitea/index.py initd_install
fi
echo 'install success' > $install_tmp
}
Uninstall_App()
{
if [ -f /usr/lib/systemd/system/gitea.service ];then
systemctl stop gitea
systemctl disable gitea
rm -rf /usr/lib/systemd/system/gitea.service
systemctl daemon-reload
fi
if [ -f $serverPath/gitea/initd/gitea ];then
$serverPath/gitea/initd/gitea stop
fi
rm -rf $serverPath/gitea
echo 'uninstall success' > $install_tmp
}
action=$1
version=$2
if [ "${1}" == 'install' ];then
Install_App $version
else
Uninstall_App $version
fi
-370
View File
@@ -1,370 +0,0 @@
function gogsPost(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:'gitea', 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 gogsSetConfig(){
gogsPost('get_gogs_conf', '', function(data){
var rrdata = $.parseJSON(data.data);
if (!rrdata.status){
layer.msg(rrdata.msg,{icon:0,time:2000,shade: [0.3, '#000']});
return;
}
var rdata = rrdata.data;
var mlist = '';
for (var i = 0; i < rdata.length; i++) {
var w = '140';
if (rdata[i].name == 'error_reporting') w = '250';
var ibody = '<input style="width: ' + w + 'px;" class="bt-input-text mr5" name="' + rdata[i].name + '" value="' + rdata[i].value + '" type="text" >';
switch (rdata[i].type) {
case 0:
var selected_1 = (rdata[i].value == 1) ? 'selected' : '';
var selected_0 = (rdata[i].value == 0) ? 'selected' : '';
ibody = '<select class="bt-input-text mr5" name="' + rdata[i].name + '" style="width: ' + w + 'px;">\
<option value="1" ' + selected_1 + '>开启</option>\
<option value="0" ' + selected_0 + '>关闭</option>\
</select>';
break;
case 1:
var selected_1 = (rdata[i].value == 'On') ? 'selected' : '';
var selected_0 = (rdata[i].value == 'Off') ? 'selected' : '';
ibody = '<select class="bt-input-text mr5" name="' + rdata[i].name + '" style="width: ' + w + 'px;">\
<option value="On" ' + selected_1 + '>开启</option>\
<option value="Off" ' + selected_0 + '>关闭</option></select>'
break;
case 2:
var selected_1 = (rdata[i].value == 'true') ? 'selected' : '';
var selected_0 = (rdata[i].value == 'false') ? 'selected' : '';
ibody = '<select class="bt-input-text mr5" name="' + rdata[i].name + '" style="width: ' + w + 'px;">\
<option value="true" ' + selected_1 + '>开启</option>\
<option value="false" ' + selected_0 + '>关闭</option></select>'
break;
}
mlist += '<p><span>' + rdata[i].name + '</span>' + ibody + ', <font>' + rdata[i].ps + '</font></p>'
}
var html = '<style>.conf_p p{margin-bottom: 2px}</style><div class="conf_p" style="margin-bottom:0">\
' + mlist + '\
<div style="margin-top:10px; padding-right:15px" class="text-right">\
<button class="btn btn-success btn-sm mr5" onclick="gogsSetConfig()">刷新</button>\
<button class="btn btn-success btn-sm" onclick="submitGogsConf()">保存</button></div>\
</div>';
$(".soft-man-con").html(html);
});
}
//提交PHP配置
function submitGogsConf() {
var data = {
DOMAIN: $("input[name='DOMAIN']").val(),
ROOT_URL: $("input[name='ROOT_URL']").val(),
HTTP_ADDR: $("select[name='HTTP_ADDR']").val(),
HTTP_PORT: $("input[name='HTTP_PORT']").val(),
START_SSH_SERVER: $("select[name='START_SSH_SERVER']").val() || 'false',
SSH_PORT: $("input[name='SSH_PORT']").val(),
REQUIRE_SIGNIN_VIEW: $("select[name='REQUIRE_SIGNIN_VIEW']").val() || 'false',
ENABLE_CAPTCHA: $("select[name='ENABLE_CAPTCHA']").val() || 'true',
DISABLE_REGISTRATION: $("select[name='DISABLE_REGISTRATION']").val() || 'false',
ENABLE_NOTIFY_MAIL: $("select[name='ENABLE_NOTIFY_MAIL']").val() || 'false',
FORCE_PRIVATE: $("select[name='FORCE_PRIVATE']").val() || 'false',
SHOW_FOOTER_BRANDING: $("select[name='SHOW_FOOTER_BRANDING']").val() || 'false',
SHOW_FOOTER_VERSION: $("select[name='SHOW_FOOTER_VERSION']").val() || 'false',
SHOW_FOOTER_TEMPLATE_LOAD_TIME: $("select[name='SHOW_FOOTER_TEMPLATE_LOAD_TIME']").val() || 'false',
};
gogsPost('submit_gogs_conf', data, function(ret_data){
var rdata = $.parseJSON(ret_data.data);
layer.msg(rdata.msg, { icon: rdata.status ? 1 : 2 });
gogsSetConfig();
});
}
function gogsEdit(){
gogsPost('gogs_edit',{} , function(data){
// console.log(data);
var rdata = $.parseJSON(data.data);
var edit = '<p class="status">通用的手动编辑:</p>';
edit +='<div class="sfm-opt">\
<button class="btn btn-default btn-sm" onclick="onlineEditFile(0,\''+rdata['post_receive']+'\');">post-receive.tpl</button>\
<button class="btn btn-default btn-sm" onclick="onlineEditFile(0,\''+rdata['commit']+'\');">commit.tpl</button>\
</div>';
$(".soft-man-con").html(edit);
});
}
function giteaUserList(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;
}
_data['tojs'] = 'giteaUserList';
gogsPost('user_list', _data, function(data){
var rdata = $.parseJSON(data.data);
if (!rdata.status){
layer.msg(rdata.msg,{icon:0,time:2000,shade: [0.3, '#000']});
return;
}
content = '<div class="finduser"><input class="bt-input-text mr5 outline_no" type="text" placeholder="查找用户名" id="find_user" style="height: 28px; border-radius: 3px;width: 435px;">';
content += '<button class="btn btn-success btn-sm" onclick="userFind();">查找</button></div>';
content += '<div class="divtable" style="margin-top:5px;"><table class="table table-hover" width="100%" cellspacing="0" cellpadding="0" border="0">';
content += '<thead><tr>';
content += '<th>序号</th>';
content += '<th>用户或组织</th>';
content += '<th>邮件地址</th>';
content += '<th>操作(<a href="'+rdata['data']['root_url']+'" class="btlink" target="_blank">WEB管理</a>)</th>';
content += '</tr></thead>';
content += '<tbody>';
ulist = rdata['data']['data'];
for (i in ulist){
email = ulist[i]["email"] == '' ? '无' : ulist[i]["email"];
content += '<tr><td>'+ulist[i]["id"]+'</td>'+
'<td>'+ulist[i]["name"]+'</td>'+
'<td>'+email+'</td>'+
'<td><a class="btlink" onclick="userProjectList(\''+ulist[i]["name"]+'\')">项目管理</a></td>'+
'</tr>';
}
content += '</tbody>';
content += '</table></div>';
var page = '<div class="dataTables_paginate paging_bootstrap pagination" style="margin-top:0px;"><ul id="softPage" class="page"><div>';
page += rdata['data']['list'];
page += '</div></ul></div>';
content += page;
$(".soft-man-con").html(content);
});
}
function userProjectList(user, search){
var loadOpen = layer.open({
type: 1,
title: '用户('+user+')项目列表',
area: '500px',
content:"<div class='bt-form pd20 c6'>\
<div>\
<div id='gitea_table' class='divtable' style='margin-top:5px;'>\
<table class='table table-hover'>\
<thead><tr><th>项目</th><th>操作</th></tr></thead>\
<tbody></tbody>\
</table>\
<div class='dataTables_paginate paging_bootstrap pagination' style='margin-top:0px;'><ul class='page'><div class='gitea_page'></div></ul></div>\
</div>\
</div>\
</div>",
success:function(){
userProjectListPost(user,search);
}
});
}
function userProjectListPost(user, search){
var req = {};
if (!isNaN(user)){
req['page'] = user;
req['name'] = user = getCookie('gogsUserSelected');
} else {
req['page'] = 1;
req['name'] = user;
setCookie('gogsUserSelected', user);
}
req['page_size'] = 5;
req['search'] = '';
if(typeof(search) != 'undefined'){
req['search'] = search;
}
gogsPost('user_project_list', req, function(data){
var rdata = [];
try {
rdata = $.parseJSON(data.data);
} catch(e){}
if (!rdata['status']){
layer.msg(rdata['msg'], { icon: 2 });
return;
}
var list = '';
// console.log(rdata);
var project_list = rdata['data']['data'];
for (i in project_list) {
var name = project_list[i]['name'];
list += '<tr>\
<td>'+name+'</td>\
<td>\
<a class="btlink" target="_blank" href="'+rdata['data']['root_url']+user+'/'+name+'">源码</a> | \
<a class="btlink" onclick="projectScript(\''+user+'\',\''+name+'\','+project_list[i]['has_hook']+');">脚本</a>\
</td>\
</tr>';
}
$('#gitea_table tbody').html(list);
var page = rdata['data']['list'];
$('#gitea_table .gitea_page').html(page);
});
}
function projectScript(user, name,has_hook){
// console.log(user,name,has_hook);
var html = '';
if (has_hook){
html += '<button class="btn btn-default btn-sm hook_edit">手动编辑</button>';
html += '<button class="btn btn-default btn-sm hook_log">调试日志</button>';
html += '<button class="btn btn-default btn-sm hook_load">重新加载</button>';
html += '<button class="btn btn-default btn-sm hook_unload">卸载脚本</button>';
} else {
html += '<button class="btn btn-default btn-sm hook_load">加载脚本</button>';
}
var loadOpen = layer.open({
type: 1,
title: '['+user+']['+name+']脚本设置',
area: '240px',
content:'<div class="change-default pd20">'+html+'</div>',
success:function(layero,index) {
$('.hook_edit').click(function(){
projectScriptEdit(user,name,index);
});
$('.hook_log').click(function(){
projectScriptDebug(user,name,index);
});
$('.hook_load').click(function(){
projectScriptLoad(user,name,index);
});
$('.hook_unload').click(function(){
projectScriptUnload(user,name,index);
});
}
});
}
function projectScriptEdit(user,name,index){
gogsPost('project_script_edit', {'user':user,'name':name}, function(data){
var rdata = $.parseJSON(data.data);
if (rdata['status']){
onlineEditFile(0, rdata['data']['path']);
} else {
layer.msg(rdata.msg,{icon:1,time:2000,shade: [0.3, '#000']});
}
});
}
function projectScriptLoad(user,name,index){
gogsPost('project_script_load', {'user':user,'name':name}, function(data){
if (data.data != 'ok'){
layer.msg(data.data,{icon:0,time:2000,shade: [0.3, '#000']});
return;
}
showMsg('加载成功!',function(){
layer.close(index);
userProjectListPost(1);
},{icon:1,time:2000,shade: [0.3, '#000']},2000);
});
}
function projectScriptUnload(user,name,index){
gogsPost('project_script_unload', {'user':user,'name':name}, function(data){
if (data.data != 'ok'){
layer.msg(data.data,{icon:0,time:2000,shade: [0.3, '#000']});
return;
}
showMsg('卸载成功!',function(){
layer.close(index);
userProjectListPost(1);
},{icon:1,time:2000,shade: [0.3, '#000']},2000);
});
}
function projectScriptDebug(user,name,index){
gogsPost('project_script_debug', {'user':user,'name':name}, function(data){
var rdata = $.parseJSON(data.data);
if (rdata['status']){
onlineEditFile(0, rdata['path']);
} else {
showMsg(rdata.msg,function(){
},{icon:1,time:2000,shade: [0.3, '#000']},2000);
}
});
}
function getRsaPublic(){
gogsPost('get_rsa_public', {}, function(data){
var rdata = $.parseJSON(data.data);
var con = '<div class="tab-con">\
<div class="myKeyCon ptb15">\
<textarea style="margin:0px;width:580px;height:110px;outline:none;" spellcheck="false">'+rdata.mw+'</textarea>\
</div>\
<ul class="help-info-text c7 pull-left"></ul>\
</div>'
layer.open({
type: 1,
area: "600px",
title: '本机公钥',
closeBtn: 2,
shift: 5,
shadeClose: false,
content:con
});
});
}
function giteaRead(){
var readme = '<ul class="help-info-text c7">';
readme += '<li>默认使用MySQL,第一个启动加载各种配置,并修改成正确的数据库配置</li>';
readme += '<li>邮件端口使用456,gogs仅支持使用STARTTLS的SMTP协议</li>';
readme += '<li>如果使用项目中脚本本地同步,<a target="_blank" href="https://github.com/midoks/mdserver-web/wiki/%E6%8F%92%E4%BB%B6%E7%AE%A1%E7%90%86%5Bgogs%E4%BD%BF%E7%94%A8%E8%AF%B4%E6%98%8E%5D#%E5%90%AF%E5%8A%A8gogs%E5%90%8E%E5%A6%82%E6%9E%9C%E8%A6%81%E4%BD%BF%E7%94%A8hook%E8%84%9A%E6%9C%AC%E5%90%8C%E6%AD%A5%E4%BB%A3%E7%A0%81%E9%9C%80%E8%A6%81%E5%BC%80%E5%90%AFssh%E7%AB%AF%E5%8F%A3">点击查看</></li>';
readme += '<li><a href="#" onclick="getRsaPublic();">点击查看本机公钥</></li>';
readme += '</ul>';
$('.soft-man-con').html(readme);
}
-63
View File
@@ -1,63 +0,0 @@
APP_NAME = Gogs
RUN_USER = {$RUN_USER}
RUN_MODE = prod
[database]
DB_TYPE = mysql
HOST = 127.0.0.1:3306
NAME = gogs
USER = gogs
PASSWD = gogs
SSL_MODE = disable
PATH = data/gogs.db
[repository]
ROOT = {$ROOT_PATH}/gogs-repositories
FORCE_PRIVATE = true
[server]
DOMAIN = 127.0.0.1
HTTP_PORT = 3000
ROOT_URL = http://127.0.0.1:3000/
DISABLE_SSH = true
SSH_PORT = 2000
START_SSH_SERVER = false
OFFLINE_MODE = false
LANDING_PAGE = home
[mailer]
ENABLED = false
HOST = smtp.163.com:465
USER = midoks@163.com
PASSWD = ***
USE_PLAIN_TEXT = true
[service]
REGISTER_EMAIL_CONFIRM = false
ENABLE_NOTIFY_MAIL = false
DISABLE_REGISTRATION = true
ENABLE_CAPTCHA = true
REQUIRE_SIGNIN_VIEW = true
[picture]
DISABLE_GRAVATAR = false
ENABLE_FEDERATED_AVATAR = false
[session]
PROVIDER = file
[log]
MODE = console, file
LEVEL = Info
ROOT_PATH = {$SERVER_PATH}/gogs/log
[security]
INSTALL_LOCK = true
SECRET_KEY = jmGSJXDBH5Ng4wt
[other]
SHOW_FOOTER_BRANDING = false
SHOW_FOOTER_VERSION = false
SHOW_FOOTER_TEMPLATE_LOAD_TIME = false
-39
View File
@@ -1,39 +0,0 @@
#!/bin/bash
echo `date`
GITADDR="{$GITROOTURL}/{$USERNAME}/{$PROJECT}.git"
GIT_SDIR="{$CODE_DIR}"
GIT_USER_DIR="${GIT_SDIR}/{$USERNAME}"
GIT_PROJECT_DIR="${GIT_USER_DIR}/{$PROJECT}"
git config --global credential.helper store
# echo $GIT_PROJECT_DIR
if [ ! -d $GIT_PROJECT_DIR ];then
mkdir -p $GIT_USER_DIR && cd $GIT_USER_DIR
git clone $GITADDR
fi
unset GIT_DIR
cd $GIT_PROJECT_DIR && git pull
# func 2
# cd $GIT_PROJECT_DIR && env -i git pull origin master
#更新的目的地址
WEB_PATH={$WEB_ROOT}/{$USERNAME}/{$PROJECT}
mkdir -p $WEB_PATH
rsync -vauP --delete --exclude=".*" $GIT_PROJECT_DIR/ $WEB_PATH
sysName=`uname`
if [ $sysName == 'Darwin' ]; then
USER=$(who | sed -n "2,1p" |awk '{print $1}')
chown -R $USER:staff $WEB_PATH
else
chown -R www:www $WEB_PATH
fi
-36
View File
@@ -1,36 +0,0 @@
#!/bin/bash
echo `date`
GITADDR="{$GITROOTURL}/{$USERNAME}/{$PROJECT}"
GIT_SDIR="{$CODE_DIR}"
GIT_USER_DIR="${GIT_SDIR}/{$USERNAME}"
GIT_PROJECT_DIR="${GIT_USER_DIR}/{$PROJECT}"
# echo $GIT_PROJECT_DIR
if [ ! -d $GIT_PROJECT_DIR ];then
mkdir -p $GIT_USER_DIR && cd $GIT_USER_DIR
git clone $GITADDR
fi
unset GIT_DIR
cd $GIT_PROJECT_DIR && git pull
# func 2
# cd $GIT_PROJECT_DIR && env -i git pull origin master
WEB_PATH={$WEB_ROOT}/{$USERNAME}/{$PROJECT}
mkdir -p $WEB_PATH
rsync -vauP --delete --exclude=".*" $GIT_PROJECT_DIR/ $WEB_PATH
sysName=`uname`
if [ $sysName == 'Darwin' ]; then
USER=$(who | sed -n "2,1p" |awk '{print $1}')
chown -R $USER:staff $WEB_PATH
else
chown -R www:www $WEB_PATH
fi
-3
View File
@@ -1,3 +0,0 @@
#!/bin/bash
sh -x {$PATH}/commit 2>{$PATH}/sh.log
Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

-23
View File
@@ -1,23 +0,0 @@
<div class="bt-form">
<div class="bt-w-main">
<div class="bt-w-menu">
<p class="bgw" onclick="pluginService('gogs');">服务</p>
<p onclick="pluginInitD('gogs');">自启动</p>
<!-- <p onclick="pluginConfig('gogs',null, 'init_conf');">启动配置</p> -->
<p onclick="gogsEdit();">手动编辑</p>
<p onclick="pluginConfig('gogs',null, 'conf');">配置文件</p>
<p onclick="gogsSetConfig();">配置修改</p>
<p onclick="gogsUserList();">用户列表</p>
<p onclick="pluginLogs('gogs',null,'run_log');">运行日志</p>
<p onclick="pluginLogs('gogs',null,'post_receive_log');" title="提交post-receive日志">提交日志</p>
<p onclick="gogsRead();">使用说明</p>
</div>
<div class="bt-w-con pd15">
<div class="soft-man-con"></div>
</div>
</div>
</div>
<script type="text/javascript">
$.getScript( "/plugins/file?name=gogs&f=js/gogs.js");
pluginService('gogs');
</script>
-754
View File
@@ -1,754 +0,0 @@
# coding: utf-8
import time
import os
import sys
import re
sys.path.append(os.getcwd() + "/class/core")
import mw
# cmd = 'ls /usr/local/lib/ | grep python | cut -d \\ -f 1 | awk \'END {print}\''
# info = mw.execShell(cmd)
# p = "/usr/local/lib/" + info[0].strip() + "/site-packages"
# sys.path.append(p)
import psutil
app_debug = False
if mw.isAppleSystem():
app_debug = True
def getPluginName():
return 'gogs'
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 getArgs():
args = sys.argv[2:]
tmp = {}
args_len = len(args)
if args_len == 1:
t = args[0].strip('{').strip('}')
t = t.split(':', 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 getInitdConfTpl():
path = getPluginDir() + "/init.d/gogs.tpl"
return path
def getInitdConf():
path = getServerDir() + "/init.d/gogs"
return path
def getConf():
path = getServerDir() + "/custom/conf/app.ini"
return path
def getConfTpl():
path = getPluginDir() + "/conf/app.ini"
return path
def status():
data = mw.execShell(
"ps -ef|grep " + getPluginName() + " |grep -v grep | grep -v python | awk '{print $2}'")
if data[0] == '':
return 'stop'
return 'start'
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'
__SR = '''#!/bin/bash
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH
export USER=%s
export HOME=%s && ''' % ( getRunUser(), getHomeDir())
def contentReplace(content):
service_path = mw.getServerDir()
content = content.replace('{$ROOT_PATH}', mw.getRootDir())
content = content.replace('{$SERVER_PATH}', service_path)
content = content.replace('{$RUN_USER}', getRunUser())
content = content.replace('{$HOME_DIR}', getHomeDir())
return content
def initDreplace():
file_tpl = getInitdConfTpl()
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()
if not os.path.exists(file_bin):
content = mw.readFile(file_tpl)
content = contentReplace(content)
mw.writeFile(file_bin, content)
mw.execShell('chmod +x ' + file_bin)
# conf_bin = getConf()
# if not os.path.exists(conf_bin):
# mw.execShell('mkdir -p ' + getServerDir() + '/custom/conf')
# conf_tpl = getConfTpl()
# content = mw.readFile(conf_tpl)
# content = contentReplace(content)
# mw.writeFile(conf_bin, content)
# systemd
systemDir = mw.systemdCfgDir()
systemService = systemDir + '/gogs.service'
systemServiceTpl = getPluginDir() + '/init.d/gogs.service.tpl'
if os.path.exists(systemDir) and not os.path.exists(systemService):
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')
log_path = getServerDir() + '/log'
if not os.path.exists(log_path):
os.mkdir(log_path)
return file_bin
def getRootUrl():
content = mw.readFile(getConf())
rep = 'ROOT_URL\s*=\s*(.*)'
tmp = re.search(rep, content)
if tmp:
return tmp.groups()[0]
rep = 'EXTERNAL_URL\s*=\s*(.*)'
tmp = re.search(rep, content)
if tmp:
return tmp.groups()[0]
return ''
def getSshPort():
content = mw.readFile(getConf())
rep = 'SSH_PORT\s*=\s*(.*)'
tmp = re.search(rep, content)
if not tmp:
return ''
return tmp.groups()[0]
def getHttpPort():
content = mw.readFile(getConf())
rep = 'HTTP_PORT\s*=\s*(.*)'
tmp = re.search(rep, content)
if not tmp:
return ''
return tmp.groups()[0]
def getRootPath():
content = mw.readFile(getConf())
rep = 'ROOT\s*=\s*(.*)'
tmp = re.search(rep, content)
if not tmp:
return ''
return tmp.groups()[0]
def getDbConfValue():
conf = getConf()
if not os.path.exists(conf):
return {}
content = mw.readFile(conf)
rep_scope = "\[database\](.*?)\["
tmp = re.findall(rep_scope, content, re.S)
rep = '(\w*)\s*=\s*(.*)'
tmp = re.findall(rep, tmp[0])
r = {}
for x in range(len(tmp)):
k = tmp[x][0]
v = tmp[x][1]
r[k] = v
return r
def pMysqlDb(conf):
host = conf['HOST'].split(':')
# pymysql
db = mw.getMyORM()
# MySQLdb |
# db = mw.getMyORMDb()
db.setPort(int(host[1]))
db.setUser(conf['USER'])
if 'PASSWD' in conf:
db.setPwd(conf['PASSWD'])
else:
db.setPwd(conf['PASSWORD'])
db.setDbName(conf['NAME'])
# db.setSocket(getSocketFile())
db.setCharset("utf8")
return db
def pSqliteDb(conf):
# print(conf)
import db
psDb = db.Sql()
# 默认
gsdir = getServerDir() + '/data'
dbname = 'gogs'
if conf['PATH'][0] == '/':
# 绝对路径
pass
else:
path = conf['PATH'].split('/')
gsdir = getServerDir() + '/' + path[0]
dbname = path[1].split('.')[0]
# print(gsdir, dbname)
psDb.dbPos(gsdir, dbname)
return psDb
def getGogsDbType(conf):
if 'DB_TYPE' in conf:
return conf['DB_TYPE']
if 'TYPE' in conf:
return conf['TYPE']
return 'NONE'
def pQuery(sql):
conf = getDbConfValue()
gtype = getGogsDbType(conf)
if gtype == 'sqlite3':
db = pSqliteDb(conf)
data = db.query(sql, []).fetchall()
return data
elif gtype == 'mysql':
db = pMysqlDb(conf)
return db.query(sql)
print("仅支持mysql|sqlite3配置")
exit(0)
def isSqlError(mysqlMsg):
# 检测数据库执行错误
_mysqlMsg = str(mysqlMsg)
# print _mysqlMsg
if "MySQLdb" in _mysqlMsg:
return mw.returnData(False, 'MySQLdb组件缺失! <br>进入SSH命令行输入: pip install mysql-python')
if "2002," in _mysqlMsg:
return mw.returnData(False, '数据库连接失败,请检查数据库服务是否启动!')
if "using password:" in _mysqlMsg:
return mw.returnData(False, '数据库管理密码错误!')
if "Connection refused" in _mysqlMsg:
return mw.returnData(False, '数据库连接失败,请检查数据库服务是否启动!')
if "1133," in _mysqlMsg:
return mw.returnData(False, '数据库用户不存在!')
if "1007," in _mysqlMsg:
return mw.returnData(False, '数据库已经存在!')
if "1044," in _mysqlMsg:
return mw.returnData(False, mysqlMsg[1])
if "2003," in _mysqlMsg:
return mw.returnData(False, "Can't connect to MySQL server on '127.0.0.1' (61)")
return mw.returnData(True, 'OK')
def gogsOp(method):
file = initDreplace()
if not mw.isAppleSystem():
data = mw.execShell('systemctl ' + method + ' gogs')
if data[1] == '':
return 'ok'
return 'fail'
data = mw.execShell(__SR + file + ' ' + method)
if data[1] == '':
return 'ok'
return data[0]
def start():
return gogsOp('start')
def stop():
return gogsOp('stop')
def restart():
return gogsOp('restart')
def reload():
return gogsOp('reload')
def initdStatus():
if mw.isAppleSystem():
return "Apple Computer does not support"
shell_cmd = 'systemctl status gogs | grep loaded | grep "enabled;"'
data = mw.execShell(shell_cmd)
if data[0] == '':
return 'fail'
return 'ok'
def initdInstall():
if mw.isAppleSystem():
return "Apple Computer does not support"
mw.execShell('systemctl enable gogs')
return 'ok'
def initdUinstall():
if mw.isAppleSystem():
return "Apple Computer does not support"
mw.execShell('systemctl disable gogs')
return 'ok'
def runLog():
log_path = getServerDir() + '/log/gogs.log'
return log_path
def postReceiveLog():
log_path = getServerDir() + '/log/hooks/post-receive.log'
return log_path
def getGogsConf():
conf = getConf()
if not os.path.exists(conf):
return mw.returnJson(False, "请先安装初始化!<br/>默认地址:http://" + mw.getLocalIp() + ":3000")
gets = [
{'name': 'DOMAIN', 'type': -1, 'ps': '服务器域名'},
{'name': 'ROOT_URL', 'type': -1, 'ps': '公开的完整URL路径'},
{'name': 'HTTP_ADDR', 'type': -1, 'ps': '应用HTTP监听地址'},
{'name': 'HTTP_PORT', 'type': -1, 'ps': '应用 HTTP 监听端口号'},
{'name': 'START_SSH_SERVER', 'type': 2, 'ps': '启动内置SSH服务器'},
{'name': 'SSH_PORT', 'type': -1, 'ps': 'SSH 端口号'},
{'name': 'REQUIRE_SIGNIN_VIEW', 'type': 2, 'ps': '强制登录浏览'},
{'name': 'ENABLE_CAPTCHA', 'type': 2, 'ps': '启用验证码服务'},
{'name': 'DISABLE_REGISTRATION', 'type': 2, 'ps': '禁止注册,只能由管理员创建帐号'},
{'name': 'ENABLE_NOTIFY_MAIL', 'type': 2, 'ps': '是否开启邮件通知'},
{'name': 'FORCE_PRIVATE', 'type': 2, 'ps': '强制要求所有新建的仓库都是私有'},
{'name': 'SHOW_FOOTER_BRANDING', 'type': 2, 'ps': 'Gogs推广信息'},
{'name': 'SHOW_FOOTER_VERSION', 'type': 2, 'ps': 'Gogs版本信息'},
{'name': 'SHOW_FOOTER_TEMPLATE_LOAD_TIME', 'type': 2, 'ps': 'Gogs模板加载时间'},
]
conf = mw.readFile(conf)
result = []
for g in gets:
rep = g['name'] + '\s*=\s*(.*)'
tmp = re.search(rep, conf)
if not tmp:
continue
g['value'] = tmp.groups()[0]
result.append(g)
return mw.returnJson(True, 'OK', result)
def submitGogsConf():
gets = ['DOMAIN',
'ROOT_URL',
'HTTP_ADDR',
'HTTP_PORT',
'START_SSH_SERVER',
'SSH_PORT',
'REQUIRE_SIGNIN_VIEW',
'FORCE_PRIVATE',
'ENABLE_CAPTCHA',
'DISABLE_REGISTRATION',
'ENABLE_NOTIFY_MAIL',
'SHOW_FOOTER_BRANDING',
'SHOW_FOOTER_VERSION',
'SHOW_FOOTER_TEMPLATE_LOAD_TIME']
args = getArgs()
filename = getConf()
conf = mw.readFile(filename)
for g in gets:
if g in args:
rep = g + '\s*=\s*(.*)'
val = g + ' = ' + args[g]
conf = re.sub(rep, val, conf)
mw.writeFile(filename, conf)
reload()
return mw.returnJson(True, '设置成功')
def userList():
conf = getConf()
if not os.path.exists(conf):
return mw.returnJson(False, "请先安装初始化!<br/>默认地址:http://" + mw.getLocalIp() + ":3000")
conf = getDbConfValue()
gtype = getGogsDbType(conf)
if gtype != 'mysql':
return mw.returnJson(False, "仅支持mysql数据操作!")
import math
args = getArgs()
data = checkArgs(args, ['page', 'page_size'])
if not data[0]:
return data[1]
page = int(args['page'])
page_size = int(args['page_size'])
search = ''
if 'search' in args:
search = args['search']
data = {}
data['root_url'] = getRootUrl()
start = (page - 1) * page_size
list_count = pQuery('select count(id) as num from user')
count = list_count[0]["num"]
list_data = pQuery(
'select id,name,email from user order by id desc limit ' + str(start) + ',' + str(page_size))
data['list'] = mw.getPage({'count': count, 'p': page,
'row': page_size, 'tojs': 'gogsUserList'})
data['page'] = page
data['page_size'] = page_size
data['page_count'] = int(math.ceil(count / page_size))
data['data'] = list_data
return mw.returnJson(True, 'OK', data)
def getAllUserProject(user, search=''):
path = getRootPath() + '/' + user
dlist = []
if os.path.exists(path):
for filename in os.listdir(path):
tmp = {}
filePath = path + '/' + filename
if os.path.isdir(filePath):
if search == '':
tmp['name'] = filename.replace('.git', '')
dlist.append(tmp)
else:
if filename.find(search) != -1:
tmp['name'] = filename.replace('.git', '')
dlist.append(tmp)
return dlist
def checkProjectListIsHasScript(user, data):
path = getRootPath() + '/' + user
for x in range(len(data)):
name = data[x]['name'] + '.git'
path_tmp = path + '/' + name + '/custom_hooks/post-receive'
if os.path.exists(path_tmp):
data[x]['has_hook'] = True
else:
data[x]['has_hook'] = False
return data
def userProjectList():
import math
args = getArgs()
# print args
page = 1
page_size = 5
search = ''
if not 'name' in args:
return mw.returnJson(False, '缺少参数name')
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']
data = {}
ulist = getAllUserProject(args['name'])
dlist_sum = len(ulist)
start = (page - 1) * page_size
ret_data = ulist[start:start + page_size]
ret_data = checkProjectListIsHasScript(args['name'], ret_data)
data['root_url'] = getRootUrl()
data['data'] = ret_data
data['args'] = args
data['list'] = mw.getPage(
{'count': dlist_sum, 'p': page, 'row': page_size, 'tojs': 'userProjectList'})
return mw.returnJson(True, 'OK', data)
def projectScriptEdit():
args = getArgs()
if not 'user' in args:
return mw.returnJson(True, 'username missing')
if not 'name' in args:
return mw.returnJson(True, 'project name missing')
user = args['user']
name = args['name'] + '.git'
post_receive = getRootPath() + '/' + user + '/' + name + \
'/custom_hooks/commit'
if os.path.exists(post_receive):
return mw.returnJson(True, 'OK', {'path': post_receive})
else:
return mw.returnJson(False, 'file does not exist')
def projectScriptLoad():
args = getArgs()
if not 'user' in args:
return mw.returnJson(True, 'username missing')
if not 'name' in args:
return mw.returnJson(True, 'project name missing')
user = args['user']
name = args['name'] + '.git'
path = getRootPath() + '/' + user + '/' + name
post_receive_tpl = getPluginDir() + '/hook/post-receive.tpl'
post_receive = path + '/custom_hooks/post-receive'
if not os.path.exists(path + '/custom_hooks'):
mw.execShell('mkdir -p ' + path + '/custom_hooks')
pct_content = mw.readFile(post_receive_tpl)
pct_content = pct_content.replace('{$PATH}', path + '/custom_hooks')
mw.writeFile(post_receive, pct_content)
mw.execShell('chmod 777 ' + post_receive)
commit_tpl = getPluginDir() + '/hook/commit.tpl'
commit = path + '/custom_hooks/commit'
codeDir = mw.getRootDir() + '/git'
cc_content = mw.readFile(commit_tpl)
gitPath = getRootPath()
cc_content = cc_content.replace('{$GITROOTURL}', gitPath)
cc_content = cc_content.replace('{$CODE_DIR}', codeDir)
cc_content = cc_content.replace('{$USERNAME}', user)
cc_content = cc_content.replace('{$PROJECT}', args['name'])
cc_content = cc_content.replace('{$WEB_ROOT}', mw.getWwwDir())
mw.writeFile(commit, cc_content)
mw.execShell('chmod 777 ' + commit)
return 'ok'
def projectScriptUnload():
args = getArgs()
data = checkArgs(args, ['user', 'name'])
if not data[0]:
return data[1]
user = args['user']
name = args['name'] + '.git'
post_receive = getRootPath() + '/' + user + '/' + name + \
'/custom_hooks/post-receive'
mw.execShell('rm -f ' + post_receive)
commit = getRootPath() + '/' + user + '/' + name + \
'/custom_hooks/commit'
mw.execShell('rm -f ' + commit)
return 'ok'
def projectScriptDebug():
args = getArgs()
data = checkArgs(args, ['user', 'name'])
if not data[0]:
return data[1]
user = args['user']
name = args['name'] + '.git'
commit_log = getRootPath() + '/' + user + '/' + name + \
'/custom_hooks/sh.log'
data = {}
if os.path.exists(commit_log):
data['status'] = True
data['path'] = commit_log
else:
data['status'] = False
data['msg'] = '没有日志文件'
return mw.getJson(data)
def gogsEdit():
data = {}
data['post_receive'] = getPluginDir() + '/hook/post-receive.tpl'
data['commit'] = getPluginDir() + '/hook/commit.tpl'
return mw.getJson(data)
def getRsaPublic():
path = getHomeDir()
path += '/.ssh/id_rsa.pub'
content = mw.readFile(path)
data = {}
data['mw'] = content
return mw.getJson(data)
def getTotalStatistics():
st = status()
data = {}
if st.strip() == 'start':
list_count = pQuery('select count(id) as num from repository')
count = list_count[0]["num"]
data['status'] = True
data['count'] = count
data['ver'] = mw.readFile(getServerDir() + '/version.pl').strip()
return mw.returnJson(True, 'ok', data)
data['status'] = False
data['count'] = 0
return mw.returnJson(False, 'fail', data)
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 == 'run_log':
print(runLog())
elif func == 'post_receive_log':
print(postReceiveLog())
elif func == 'conf':
print(getConf())
elif func == 'init_conf':
print(getInitdConf())
elif func == 'get_gogs_conf':
print(getGogsConf())
elif func == 'submit_gogs_conf':
print(submitGogsConf())
elif func == 'user_list':
print(userList())
elif func == 'user_project_list':
print(userProjectList())
elif func == 'project_script_edit':
print(projectScriptEdit())
elif func == 'project_script_load':
print(projectScriptLoad())
elif func == 'project_script_unload':
print(projectScriptUnload())
elif func == 'project_script_debug':
print(projectScriptDebug())
elif func == 'gogs_edit':
print(gogsEdit())
elif func == 'get_rsa_public':
print(getRsaPublic())
elif func == 'get_total_statistics':
print(getTotalStatistics())
else:
print('fail')
-16
View File
@@ -1,16 +0,0 @@
{
"ps": "Gogs是一款极易搭建的自助Git服务",
"name": "gogs",
"title": "Gogs",
"versions": ["0.11.86","0.12.9"],
"tip": "soft",
"checks": "server/gogs",
"path":"server/gogs",
"author": "midoks",
"date": "2018-04-01",
"home": "https://gogs.io",
"type": "Git服务器",
"shell": "install.sh",
"pid": "3",
"sort": 7
}
-29
View File
@@ -1,29 +0,0 @@
[Unit]
Description=Gogs
After=network.target
[Service]
# Modify these two values and uncomment them if you have
# repos with lots of files and get an HTTP error 500 because
# of that
###
#LimitMEMLOCK=infinity
#LimitNOFILE=65535
Type=simple
User=git
Group=git
WorkingDirectory={$SERVER_PATH}/gogs
ExecStart={$SERVER_PATH}/gogs/gogs web
ExecReload=/bin/kill -USR2 $MAINPID
Restart=always
Environment=USER=git HOME=/home/git
# Some distributions may not support these hardening directives. If you cannot start the service due
# to an unknown option, comment out the ones not supported by your version of systemd.
ProtectSystem=full
PrivateDevices=yes
PrivateTmp=yes
NoNewPrivileges=true
[Install]
WantedBy=multi-user.target
-12
View File
@@ -1,12 +0,0 @@
[Unit]
Description=Gogs
After=network.target
[Service]
Type=forking
ExecStart={$SERVER_PATH}/gogs/init.d/gogs start
ExecStop={$SERVER_PATH}/gogs/init.d/gogs stop
RemainAfterExit=yes
[Install]
WantedBy=multi-user.target
-110
View File
@@ -1,110 +0,0 @@
#!/bin/sh
#
# /etc/rc.d/init.d/gogs
#
# Runs the Gogs
#
#
# chkconfig: - 85 15
#
### BEGIN INIT INFO
# Provides: gogs
# Required-Start: $remote_fs $syslog
# Required-Stop: $remote_fs $syslog
# Should-Start: mysql postgresql
# Should-Stop: mysql postgresql
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
# Short-Description: Start gogs at boot time.
# Description: Control gogs.
### END INIT INFO
# Source function library.
if [ -f /etc/init.d/functions ];then
. /etc/init.d/functions
fi
if [ -f /etc/rc.d/init.d/functions ];then
. /etc/rc.d/init.d/functions
fi
# Default values
export HOME={$HOME_DIR}
export USER={$RUN_USER}
NAME=gogs
GOGS_HOME={$SERVER_PATH}/gogs
GOGS_PATH=${GOGS_HOME}/$NAME
GOGS_USER={$RUN_USER}
SERVICENAME="Gogs"
LOCKFILE=/tmp/gogs.lock
LOGPATH=${GOGS_HOME}/log
LOGFILE=${LOGPATH}/gogs.log
RETVAL=0
[ -r /etc/sysconfig/$NAME ] && . /etc/sysconfig/$NAME
DAEMON_OPTS="--check $NAME"
[ ! -z "$GOGS_USER" ] && DAEMON_OPTS="$DAEMON_OPTS --user=${GOGS_USER}"
status(){
isStart=`ps -ef|grep 'gogs web' |grep -v grep|awk '{print $2}'`
if [ "$isStart" == '' ];then
echo -e "${SERVICENAME} not running"
else
echo -e "${SERVICENAME}(pid $(echo $isStart)) already running"
fi
}
start() {
isStart=`ps -ef|grep 'gogs web' |grep -v grep|awk '{print $2}'`
if [ "$isStart" != '' ];then
echo "${SERVICENAME}(pid $(echo $isStart)) already running"
return $RETVAL
fi
cd ${GOGS_HOME}
echo -e "Starting ${SERVICENAME}: \c"
${GOGS_PATH} web > ${LOGFILE} 2>&1 &
RETVAL=$?
[ $RETVAL = 0 ] && touch ${LOCKFILE} && echo -e "\033[32mdone\033[0m"
return $RETVAL
}
stop() {
pids=`ps -ef|grep 'gogs web' |grep -v grep|awk '{print $2}'`
arr=($pids)
echo -e "Stopping gogs... \c"
for p in ${arr[@]}
do
kill -9 $p
done
echo -e "\033[32mdone\033[0m"
}
case "$1" in
start)
start
;;
stop)
stop
;;
status)
status
;;
restart)
stop
start
;;
reload)
stop
start
;;
*)
echo "Usage: ${NAME} {start|stop|status|restart}"
exit 1
;;
esac
exit $RETVAL
-105
View File
@@ -1,105 +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")
install_tmp=${rootPath}/tmp/mw_install.pl
GOGS_DOWNLOAD='https://dl.gogs.io'
getOs(){
os=`uname`
if [ "Darwin" == "$os" ];then
echo 'darwin'
else
echo 'linux'
fi
return 0
}
getBit(){
echo `getconf LONG_BIT`
}
Install_gogs()
{
mkdir -p $serverPath/source/gogs
echo '正在安装脚本文件...' > $install_tmp
version=$1
os=`getOs`
# if id git &> /dev/null ;then
# echo "git uid is `id -u git`"
# echo "git shell is `grep "^git:" /etc/passwd |cut -d':' -f7 `"
# else
# groupadd git
# useradd -g git git
# fi
git config --global push.default simple
if [ "darwin" == "$os" ];then
file=gogs_${version}_darwin_amd64.zip
else
file=gogs_${version}_linux_amd64.zip
fi
if [ ! -f $serverPath/source/gogs/$file ];then
wget --no-check-certificate -O $serverPath/source/gogs/$file ${GOGS_DOWNLOAD}/${version}/${file}
fi
cd $serverPath/source/gogs && unzip -o $file -d gogs_${version}
mv $serverPath/source/gogs/gogs_${version}/gogs/ $serverPath/gogs
if [ -d $serverPath/gogs ];then
echo $version > $serverPath/gogs/version.pl
cd ${rootPath} && python3 ${rootPath}/plugins/gogs/index.py start
cd ${rootPath} && python3 ${rootPath}/plugins/gogs/index.py initd_install
fi
# if id -u gogs > /dev/null 2>&1; then
# echo "gogs user exists"
# else
# useradd gogs
# cp /etc/sudoers{,.`date +"%Y-%m-%d_%H-%M-%S"`}
# echo "gogs ALL=(ALL) NOPASSWD: ALL" >> /etc/sudoers
# fi
echo 'install success' > $install_tmp
}
Uninstall_gogs()
{
if [ -f /usr/lib/systemd/system/gogs.service ];then
systemctl stop gogs
systemctl disable gogs
rm -rf /usr/lib/systemd/system/gogs.service
systemctl daemon-reload
fi
if [ -f $serverPath/gogs/initd/gogs ];then
$serverPath/gogs/initd/gogs stop
fi
rm -rf $serverPath/gogs
echo 'uninstall success' > $install_tmp
}
action=$1
version=$2
if [ "${1}" == 'install' ];then
Install_gogs $version
else
Uninstall_gogs $version
fi
-352
View File
@@ -1,352 +0,0 @@
function str2Obj(str){
var data = {};
kv = str.split('&');
for(i in kv){
v = kv[i].split('=');
data[v[0]] = v[1];
}
return data;
}
function gogsPost(method,args,callback, title){
var _args = null;
if (typeof(args) == 'string'){
_args = JSON.stringify(str2Obj(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:'gogs', 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 gogsSetConfig(){
gogsPost('get_gogs_conf', '', function(data){
var rrdata = $.parseJSON(data.data);
if (!rrdata.status){
layer.msg(rrdata.msg,{icon:0,time:2000,shade: [0.3, '#000']});
return;
}
var rdata = rrdata.data;
var mlist = '';
for (var i = 0; i < rdata.length; i++) {
var w = '140';
if (rdata[i].name == 'error_reporting') w = '250';
var ibody = '<input style="width: ' + w + 'px;" class="bt-input-text mr5" name="' + rdata[i].name + '" value="' + rdata[i].value + '" type="text" >';
switch (rdata[i].type) {
case 0:
var selected_1 = (rdata[i].value == 1) ? 'selected' : '';
var selected_0 = (rdata[i].value == 0) ? 'selected' : '';
ibody = '<select class="bt-input-text mr5" name="' + rdata[i].name + '" style="width: ' + w + 'px;">\
<option value="1" ' + selected_1 + '>开启</option>\
<option value="0" ' + selected_0 + '>关闭</option>\
</select>';
break;
case 1:
var selected_1 = (rdata[i].value == 'On') ? 'selected' : '';
var selected_0 = (rdata[i].value == 'Off') ? 'selected' : '';
ibody = '<select class="bt-input-text mr5" name="' + rdata[i].name + '" style="width: ' + w + 'px;">\
<option value="On" ' + selected_1 + '>开启</option>\
<option value="Off" ' + selected_0 + '>关闭</option></select>'
break;
case 2:
var selected_1 = (rdata[i].value == 'true') ? 'selected' : '';
var selected_0 = (rdata[i].value == 'false') ? 'selected' : '';
ibody = '<select class="bt-input-text mr5" name="' + rdata[i].name + '" style="width: ' + w + 'px;">\
<option value="true" ' + selected_1 + '>开启</option>\
<option value="false" ' + selected_0 + '>关闭</option></select>'
break;
}
mlist += '<p><span>' + rdata[i].name + '</span>' + ibody + ', <font>' + rdata[i].ps + '</font></p>'
}
var html = '<style>.conf_p p{margin-bottom: 2px}</style><div class="conf_p" style="margin-bottom:0">\
' + mlist + '\
<div style="margin-top:10px; padding-right:15px" class="text-right">\
<button class="btn btn-success btn-sm mr5" onclick="gogsSetConfig()">刷新</button>\
<button class="btn btn-success btn-sm" onclick="submitGogsConf()">保存</button></div>\
</div>';
$(".soft-man-con").html(html);
});
}
//提交PHP配置
function submitGogsConf() {
var data = {
DOMAIN: $("input[name='DOMAIN']").val(),
ROOT_URL: $("input[name='ROOT_URL']").val(),
HTTP_ADDR: $("select[name='HTTP_ADDR']").val(),
HTTP_PORT: $("input[name='HTTP_PORT']").val(),
START_SSH_SERVER: $("select[name='START_SSH_SERVER']").val() || 'false',
SSH_PORT: $("input[name='SSH_PORT']").val(),
REQUIRE_SIGNIN_VIEW: $("select[name='REQUIRE_SIGNIN_VIEW']").val() || 'false',
ENABLE_CAPTCHA: $("select[name='ENABLE_CAPTCHA']").val() || 'true',
DISABLE_REGISTRATION: $("select[name='DISABLE_REGISTRATION']").val() || 'false',
ENABLE_NOTIFY_MAIL: $("select[name='ENABLE_NOTIFY_MAIL']").val() || 'false',
FORCE_PRIVATE: $("select[name='FORCE_PRIVATE']").val() || 'false',
SHOW_FOOTER_BRANDING: $("select[name='SHOW_FOOTER_BRANDING']").val() || 'false',
SHOW_FOOTER_VERSION: $("select[name='SHOW_FOOTER_VERSION']").val() || 'false',
SHOW_FOOTER_TEMPLATE_LOAD_TIME: $("select[name='SHOW_FOOTER_TEMPLATE_LOAD_TIME']").val() || 'false',
};
gogsPost('submit_gogs_conf', data, function(ret_data){
var rdata = $.parseJSON(ret_data.data);
layer.msg(rdata.msg, { icon: rdata.status ? 1 : 2 });
gogsSetConfig();
});
}
function gogsEdit(){
gogsPost('gogs_edit',{} , function(data){
// console.log(data);
var rdata = $.parseJSON(data.data);
var edit = '<p class="status">通用的手动编辑:</p>';
edit +='<div class="sfm-opt">\
<button class="btn btn-default btn-sm" onclick="onlineEditFile(0,\''+rdata['post_receive']+'\');">post-receive.tpl</button>\
<button class="btn btn-default btn-sm" onclick="onlineEditFile(0,\''+rdata['commit']+'\');">commit.tpl</button>\
</div>';
$(".soft-man-con").html(edit);
});
}
function gogsUserList(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;
}
gogsPost('user_list', _data, function(data){
var rdata = $.parseJSON(data.data);
if (!rdata.status){
layer.msg(rdata.msg,{icon:0,time:2000,shade: [0.3, '#000']});
return;
}
content = '<div class="finduser"><input class="bt-input-text mr5 outline_no" type="text" placeholder="查找用户名" id="find_user" style="height: 28px; border-radius: 3px;width: 435px;">';
content += '<button class="btn btn-success btn-sm" onclick="userFind();">查找</button></div>';
content += '<div class="divtable" style="margin-top:5px;"><table class="table table-hover" width="100%" cellspacing="0" cellpadding="0" border="0">';
content += '<thead><tr>';
content += '<th>序号</th>';
content += '<th>用户或组织</th>';
content += '<th>邮件地址</th>';
content += '<th>操作(<a href="'+rdata['data']['root_url']+'" class="btlink" target="_blank">WEB管理</a>)</th>';
content += '</tr></thead>';
content += '<tbody>';
ulist = rdata['data']['data'];
for (i in ulist){
email = ulist[i]["email"] == '' ? '无' : ulist[i]["email"];
content += '<tr><td>'+ulist[i]["id"]+'</td>'+
'<td>'+ulist[i]["name"]+'</td>'+
'<td>'+email+'</td>'+
'<td><a class="btlink" onclick="userProjectList(\''+ulist[i]["name"]+'\')">项目管理</a></td>'+
'</tr>';
}
content += '</tbody>';
content += '</table></div>';
var page = '<div class="dataTables_paginate paging_bootstrap pagination" style="margin-top:0px;"><ul id="softPage" class="page"><div>';
page += rdata['data']['list'];
page += '</div></ul></div>';
content += page;
$(".soft-man-con").html(content);
});
}
function userProjectList(user, search){
var req = {};
if (!isNaN(user)){
req['page'] = user;
req['name'] = user = getCookie('gogsUserSelected');
} else {
req['page'] = 1;
req['name'] = user;
setCookie('gogsUserSelected', user);
}
req['page_size'] = 5;
req['search'] = '';
if(typeof(search) != 'undefined'){
req['search'] = search;
}
$('.layui-layer-close1').click();
gogsPost('user_project_list', req, function(data){
var rdata = [];
try {
rdata = $.parseJSON(data.data);
} catch(e){}
if (!rdata['status']){
layer.msg(rdata['msg'], { icon: 2 });
return;
}
var list = '';
// console.log(rdata);
var project_list = rdata['data']['data'];
for (i in project_list) {
var name = project_list[i]['name'];
list += '<tr><td>'+name+'</td>\
<td>\
<a class="btlink" target="_blank" href="'+rdata['data']['root_url']+user+'/'+name+'">源码</a> | \
<a class="btlink" onclick="projectScript(\''+user+'\',\''+name+'\','+project_list[i]['has_hook']+');">脚本</a>\
</td>\
</tr>';
}
var page = '<div class="dataTables_paginate paging_bootstrap pagination" style="margin-top:0px;"><ul id="softPage" class="page"><div>';
page += rdata['data']['list'];
page += '</div></ul></div>';
var loadOpen = layer.open({
type: 1,
title: '用户('+user+')项目列表',
area: '500px',
content:"<div class='bt-form pd20 c6'>\
<div>\
<div class='divtable' style='margin-top:5px;'>\
<table class='table table-hover'>\
<thead><tr><th>项目</th><th>操作</th></tr></thead>\
<tbody>" + list + "</tbody>\
</table>" +
page +
"</div></div></div>"
});
});
}
function projectScript(user, name,has_hook){
// console.log(user,name,has_hook);
var html = '';
if (has_hook){
html += '<button onclick="projectScriptEdit(\''+user+'\',\''+name+'\')" class="btn btn-default btn-sm">手动编辑</button>';
html += '<button onclick="projectScriptDebug(\''+user+'\',\''+name+'\')" class="btn btn-default btn-sm">调试日志</button>';
html += '<button onclick="projectScriptLoad(\''+user+'\',\''+name+'\')" class="btn btn-default btn-sm">重新加载</button>';
html += '<button onclick="projectScriptUnload(\''+user+'\',\''+name+'\')" class="btn btn-default btn-sm">卸载脚本</button>';
} else {
html += '<button onclick="projectScriptLoad(\''+user+'\',\''+name+'\')" class="btn btn-default btn-sm">加载脚本</button>';
}
var loadOpen = layer.open({
type: 1,
title: '['+user+']['+name+']脚本设置',
area: '240px',
content:'<div class="change-default pd20">'+html+'</div>'
});
}
function projectScriptEdit(user,name){
gogsPost('project_script_edit', {'user':user,'name':name}, function(data){
var rdata = $.parseJSON(data.data);
if (rdata['status']){
onlineEditFile(0, rdata['data']['path']);
} else {
layer.msg(rdata.msg,{icon:1,time:2000,shade: [0.3, '#000']});
}
});
}
function projectScriptLoad(user,name){
gogsPost('project_script_load', {'user':user,'name':name}, function(data){
if (data.data != 'ok'){
layer.msg(data.data,{icon:0,time:2000,shade: [0.3, '#000']});
return;
}
layer.msg('加载成功!',{icon:1,time:2000,shade: [0.3, '#000']});
setTimeout(function(){
userProjectList(1);
}, 2000);
});
}
function projectScriptUnload(user,name){
gogsPost('project_script_unload', {'user':user,'name':name}, function(data){
if (data.data != 'ok'){
layer.msg(data.data,{icon:0,time:2000,shade: [0.3, '#000']});
return;
}
layer.msg('卸载成功!',{icon:1,time:2000,shade: [0.3, '#000']});
setTimeout(function(){
userProjectList(1);
}, 2000);
});
}
function projectScriptDebug(user,name){
gogsPost('project_script_debug', {'user':user,'name':name}, function(data){
var rdata = $.parseJSON(data.data);
if (rdata['status']){
onlineEditFile(0, rdata['path']);
} else {
layer.msg(rdata.msg,{icon:1,time:2000,shade: [0.3, '#000']});
}
});
}
function getRsaPublic(){
gogsPost('get_rsa_public', {}, function(data){
var rdata = $.parseJSON(data.data);
var con = '<div class="tab-con">\
<div class="myKeyCon ptb15">\
<textarea style="margin:0px;width:580px;height:110px;outline:none;" spellcheck="false">'+rdata.mw+'</textarea>\
</div>\
<ul class="help-info-text c7 pull-left"></ul>\
</div>'
layer.open({
type: 1,
area: "600px",
title: '本机公钥',
closeBtn: 2,
shift: 5,
shadeClose: false,
content:con
});
});
}
function gogsRead(){
var readme = '<ul class="help-info-text c7">';
readme += '<li>默认使用MySQL,第一个启动加载各种配置,并修改成正确的数据库配置</li>';
readme += '<li>邮件端口使用456,gogs仅支持使用STARTTLS的SMTP协议</li>';
readme += '<li>如果使用项目中脚本本地同步,<a target="_blank" href="https://github.com/midoks/mdserver-web/wiki/%E6%8F%92%E4%BB%B6%E7%AE%A1%E7%90%86%5Bgogs%E4%BD%BF%E7%94%A8%E8%AF%B4%E6%98%8E%5D#%E5%90%AF%E5%8A%A8gogs%E5%90%8E%E5%A6%82%E6%9E%9C%E8%A6%81%E4%BD%BF%E7%94%A8hook%E8%84%9A%E6%9C%AC%E5%90%8C%E6%AD%A5%E4%BB%A3%E7%A0%81%E9%9C%80%E8%A6%81%E5%BC%80%E5%90%AFssh%E7%AB%AF%E5%8F%A3">点击查看</></li>';
readme += '<li><a href="#" onclick="getRsaPublic();">点击查看本机公钥</></li>';
readme += '</ul>';
$('.soft-man-con').html(readme);
}
-37
View File
@@ -1,37 +0,0 @@
global
daemon
log 127.0.0.1 local0
pidfile /tmp/haproxy.pid
maxconn 4000
user haproxy
group haproxy
defaults
mode http
log global
option httplog
timeout connect 10s
timeout client 15s
timeout server 15s
listen stats
mode http
bind *:10800
stats enable
stats uri /haproxy
stats realm Haproxy\ Statistics
stats auth admin:admin
frontend http-in
bind *:1080
default_backend servers
option forwardfor
option httpclose
backend servers
balance roundrobin
server web1 0.0.0.0:9090 check
Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.5 KiB

-18
View File
@@ -1,18 +0,0 @@
<div class="bt-form">
<div class="bt-w-main">
<div class="bt-w-menu">
<p class="bgw" onclick="pluginService('haproxy');">服务</p>
<p onclick="pluginInitD('haproxy');">自启动</p>
<p onclick="pluginConfigTpl('haproxy');">配置修改</p>
<p onclick="pluginLogs('haproxy','','run_log',30);">运行日志</p>
</div>
<div class="bt-w-con pd15">
<div class="soft-man-con"></div>
</div>
</div>
</div>
<script type="text/javascript">
$.getScript( "/plugins/file?name=haproxy&f=js/haproxy.js", function(){
pluginService('haproxy');
});
</script>
-253
View File
@@ -1,253 +0,0 @@
# coding:utf-8
import sys
import io
import os
import time
import re
import string
import subprocess
sys.path.append(os.getcwd() + "/class/core")
import mw
app_debug = False
if mw.isAppleSystem():
app_debug = True
def getPluginName():
return 'haproxy'
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 getConfTpl():
path = getPluginDir() + "/conf/haproxy.conf"
return path
def getConf():
path = getServerDir() + "/haproxy.conf"
return path
def getInitDTpl():
path = getPluginDir() + "/init.d/" + getPluginName() + ".tpl"
return path
def getArgs():
args = sys.argv[2:]
tmp = {}
args_len = len(args)
if args_len == 1:
t = args[0].strip('{').strip('}')
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 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 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 contentReplace(content):
service_path = mw.getServerDir()
content = content.replace('{$ROOT_PATH}', mw.getRootDir())
content = content.replace('{$SERVER_PATH}', service_path)
content = content.replace('{$SERVER_APP}', service_path + '/haproxy')
return content
def status():
data = mw.execShell(
"ps -ef|grep haproxy |grep -v grep | grep -v python | awk '{print $2}'")
if data[0] == '':
return 'stop'
return 'start'
def initDreplace():
file_tpl = getInitDTpl()
service_path = os.path.dirname(os.getcwd())
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 = contentReplace(content)
mw.writeFile(file_bin, content)
mw.execShell('chmod +x ' + file_bin)
# config replace
conf_bin = getConf()
if not os.path.exists(conf_bin):
conf_content = mw.readFile(getConfTpl())
conf_content = contentReplace(conf_content)
mw.writeFile(getServerDir() + '/haproxy.conf', conf_content)
# systemd
systemDir = mw.systemdCfgDir()
systemService = systemDir + '/haproxy.service'
systemServiceTpl = getPluginDir() + '/init.d/haproxy.service.tpl'
if os.path.exists(systemDir) and not os.path.exists(systemService):
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 haOp(method):
file = initDreplace()
if not mw.isAppleSystem():
data = mw.execShell('systemctl ' + method + ' haproxy')
if data[1] == '':
return 'ok'
return 'fail'
data = mw.execShell(file + ' ' + method)
if data[1] == '':
return 'ok'
return data[1]
def start():
return haOp('start')
def stop():
return haOp('stop')
def restart():
return haOp('restart')
def reload():
return haOp('reload')
def initdStatus():
if mw.isAppleSystem():
return "Apple Computer does not support"
shell_cmd = 'systemctl status haproxy | grep loaded | grep "enabled;"'
data = mw.execShell(shell_cmd)
if data[0] == '':
return 'fail'
return 'ok'
def initdInstall():
if mw.isAppleSystem():
return "Apple Computer does not support"
mw.execShell('systemctl enable haproxy')
return 'ok'
def initdUinstall():
if mw.isAppleSystem():
return "Apple Computer does not support"
mw.execShell('systemctl disable haproxy')
return 'ok'
def runLog():
path = getConf()
content = mw.readFile(path)
rep = 'log\s*=\s*(.*)'
tmp = re.search(rep, content)
return tmp.groups()[0]
def getPort():
path = getConf()
content = mw.readFile(path)
rep = 'listen\s*=\s*(.*)'
tmp = re.search(rep, content)
return tmp.groups()[0]
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 == 'config_tpl':
print(configTpl())
elif func == 'read_config_tpl':
print(readConfigTpl())
elif func == 'run_log':
print(runLog())
elif func == 'query_log':
print(queryLog())
else:
print('error')
-19
View File
@@ -1,19 +0,0 @@
{
"sort": 7,
"ps": "可靠、高性能的TCP/HTTP负载平衡器",
"name": "haproxy",
"title": "haproxy",
"shell": "install.sh",
"versions":["2.6"],
"updates":["2.6"],
"tip": "soft",
"checks": "server/haproxy",
"path": "server/haproxy",
"display": 1,
"author": "midoks",
"date": "2022-08-26",
"home": "https://www.haproxy.org/",
"doc1": "https://www.haproxy.org/",
"type": 0,
"pid": "4"
}
@@ -1,13 +0,0 @@
[Unit]
Description=he Reliable, High Performance TCP/HTTP Load Balancer
After=network.target
[Service]
Type=forking
ExecStartPre={$SERVER_PATH}/haproxy/sbin/haproxy -c -f {$SERVER_PATH}/haproxy/haproxy.conf
ExecStart={$SERVER_PATH}/haproxy/sbin/haproxy -D -f {$SERVER_PATH}/haproxy/haproxy.conf
ExecReload={$SERVER_PATH}/haproxy/sbin/haproxy -f {$SERVER_PATH}/haproxy/haproxy.conf -c -q
Restart=on-failure
[Install]
WantedBy=multi-user.target
-119
View File
@@ -1,119 +0,0 @@
#!/bin/sh
#
# chkconfig: - 85 15
# description: HA-Proxy is a TCP/HTTP reverse proxy which is particularly suited \
# for high availability environments.
# processname: haproxy
# config: /etc/haproxy/haproxy.cfg
# pidfile: /var/run/haproxy.pid
# Script Author: Simon Matter <simon.matter@invoca.ch>
# Version: 2004060600
# Source function library.
if [ -f /etc/init.d/functions ]; then
. /etc/init.d/functions
elif [ -f /etc/rc.d/init.d/functions ] ; then
. /etc/rc.d/init.d/functions
else
echo ".."
fi
# Source networking configuration.
if [ -f /etc/sysconfig/network ];then
. /etc/sysconfig/network
fi
# Check that networking is up.
# [ ${NETWORKING} = "no" ] && exit 0
HAPROXYDIR={$SERVER_PATH}/haproxy
BASENAME=haproxy
# This is our service name
#BASENAME=`basename $0`
#if [ -L $0 ]; then
# BASENAME=`find $0 -name $BASENAME -printf %l`
# BASENAME=`basename $BASENAME`
#fi
[ -f $HAPROXYDIR/etc/$BASENAME.conf ] || exit 1
RETVAL=0
start() {
$HAPROXYDIR/sbin/$BASENAME -c -q -f $HAPROXYDIR/etc/$BASENAME.conf
if [ $? -ne 0 ]; then
echo "Errors found in configuration file, check it with '$BASENAME check'."
return 1
fi
echo -n "Starting $BASENAME: "
daemon $HAPROXYDIR/sbin/$BASENAME -D -f $HAPROXYDIR/etc/$BASENAME.conf
RETVAL=$?
# echo
# [ $RETVAL -eq 0 ] && touch /var/lock/subsys/$BASENAME
return $RETVAL
}
stop() {
echo -n "Shutting down $BASENAME: "
killproc $BASENAME -USR1
RETVAL=$?
echo
[ $RETVAL -eq 0 ] && rm -f /var/lock/subsys/$BASENAME
[ $RETVAL -eq 0 ] && rm -f /var/run/$BASENAME.pid
return $RETVAL
}
restart() {
$HAPROXYDIR/sbin/$BASENAME -c -q -f $HAPROXYDIR/etc/$BASENAME.conf
if [ $? -ne 0 ]; then
echo "Errors found in configuration file, check it with '$BASENAME check'."
return 1
fi
stop
start
}
reload() {
$HAPROXYDIR/sbin/$BASENAME -c -q -f $HAPROXYDIR/etc/$BASENAME.conf
if [ $? -ne 0 ]; then
echo "Errors found in configuration file, check it with '$BASENAME check'."
return 1
fi
$HAPROXYDIR/sbin/$BASENAME -D -f $HAPROXYDIR/$BASENAME.conf -p /var/run/$BASENAME.pid -sf $(cat /var/run/$BASENAME.pid)
}
check() {
$HAPROXYDIR/sbin/$BASENAME -c -q -V -f $HAPROXYDIR/etc/$BASENAME.conf
}
rhstatus() {
status $BASENAME
}
condrestart() {
[ -e /var/lock/subsys/$BASENAME ] && restart || :
}
# See how we were called.
case "$1" in
start) start ;;
stop) stop ;;
restart) restart;;
reload) reload;;
condrestart) condrestart ;;
status) rhstatus ;;
check) check ;;
*)
echo $"Usage: $BASENAME {start|stop|restart|reload|condrestart|status|check}"
exit 1
esac
exit $?
-52
View File
@@ -1,52 +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")
install_tmp=${rootPath}/tmp/mw_install.pl
action=$1
type=$2
echo $action $type
if id haproxy &> /dev/null ;then
echo "haproxy UID is `id -u haproxy`"
echo "haproxy Shell is `grep "^haproxy:" /etc/passwd |cut -d':' -f7 `"
else
groupadd haproxy
useradd -g haproxy haproxy
fi
if [ "${2}" == "" ];then
echo '缺少安装脚本...' > $install_tmp
exit 0
fi
if [ ! -d $curPath/versions/$2 ];then
echo '缺少安装脚本2...' > $install_tmp
exit 0
fi
if [ "${action}" == "uninstall" ];then
if [ -f /usr/lib/systemd/system/haproxy.service ] || [ -f /lib/systemd/system/haproxy.service ];then
systemctl stop haproxy
systemctl disable haproxy
rm -rf /usr/lib/systemd/system/haproxy.service
rm -rf /lib/systemd/system/haproxy.service
systemctl daemon-reload
fi
fi
sh -x $curPath/versions/$2/install.sh $1
if [ "${action}" == "install" ] && [ -d $serverPath/haproxy ];then
#初始化
cd ${rootPath} && python3 ${rootPath}/plugins/haproxy/index.py start ${type}
cd ${rootPath} && python3 ${rootPath}/plugins/haproxy/index.py initd_install ${type}
fi
-53
View File
@@ -1,53 +0,0 @@
function spPostMin(method, args, callback){
var req_data = {};
req_data['name'] = 'haproxy';
req_data['func'] = method;
if (typeof(args) != 'undefined' && args!=''){
req_data['args'] = JSON.stringify(args);
}
$.post('/plugins/run', 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 spPost(method, args, callback){
var loadT = layer.msg('正在获取...', { icon: 16, time: 0, shade: 0.3 });
spPostMin(method,args,function(data){
layer.close(loadT);
if(typeof(callback) == 'function'){
callback(data);
}
});
}
function secToTime(s) {
var t;
if(s > -1){
var hour = Math.floor(s/3600);
var min = Math.floor(s/60) % 60;
var sec = s % 60;
if(hour < 10) {
t = '0'+ hour + ":";
} else {
t = hour + ":";
}
if(min < 10){t += "0";}
t += min + ":";
if(sec < 10){t += "0";}
t += sec.toFixed(2);
}
return t;
}
-70
View File
@@ -1,70 +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")
sysName=`uname`
install_tmp=${rootPath}/tmp/mw_install.pl
bash ${rootPath}/scripts/getos.sh
OSNAME=`cat ${rootPath}/data/osname.pl`
OSNAME_ID=`cat /etc/*-release | grep VERSION_ID | awk -F = '{print $2}' | awk -F "\"" '{print $2}'`
VERSION=2.6.4
MIN_VERSION=2.6
Install_App()
{
echo '正在安装脚本文件...' > $install_tmp
mkdir -p $serverPath/haproxy
APP_DIR=${serverPath}/source/haproxy
mkdir -p $APP_DIR
if [ ! -f ${APP_DIR}/haproxy-${VERSION}.tar.gz ];then
if [ $sysName == 'Darwin' ]; then
wget --no-check-certificate -O ${APP_DIR}/haproxy-${VERSION}.tar.gz https://www.haproxy.org/download/${MIN_VERSION}/src/haproxy-${VERSION}.tar.gz
else
curl -sSLo ${APP_DIR}/haproxy-${VERSION}.tar.gz https://www.haproxy.org/download/${MIN_VERSION}/src/haproxy-${VERSION}.tar.gz
fi
fi
if [ ! -f ${APP_DIR}/haproxy-${VERSION}.tar.gz ];then
curl -sSLo ${APP_DIR}/haproxy-${VERSION}.tar.gz https://www.haproxy.org/download/${MIN_VERSION}/src/haproxy-${VERSION}.tar.gz
fi
cd ${APP_DIR} && tar -zxvf haproxy-${VERSION}.tar.gz
if [ "$OSNAME" == "macos" ];then
cd ${APP_DIR}/haproxy-${VERSION} && make TARGET=osx && make install PREFIX=$serverPath/haproxy
else
cd ${APP_DIR}/haproxy-${VERSION} && make TARGET=linux-glibc && make install PREFIX=$serverPath/haproxy
fi
echo $MIN_VERSION > $serverPath/haproxy/version.pl
echo 'Install_HA' > $install_tmp
}
Uninstall_App()
{
if [ -f $serverPath/haproxy/initd/haproxy ];then
$serverPath/haproxy/initd/haproxy stop
fi
rm -rf $serverPath/haproxy
echo "Uninstall_HA" > $install_tmp
}
action=$1
if [ "${1}" == 'install' ];then
Install_App
else
Uninstall_App
fi
-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 [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
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.
Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.1 KiB

-20
View File
@@ -1,20 +0,0 @@
<div class="bt-form">
<div class="bt-w-main">
<div class="bt-w-menu">
<p class="bgw" onclick="pluginService('imail');">服务</p>
<p onclick="pluginInitD('imail');">自启动</p>
<p onclick="pluginConfig('imail',null, 'conf');">配置文件</p>
<p onclick="pluginLogs('imail',null,'run_log', 50);">运行日志</p>
</div>
<div class="bt-w-con pd15">
<div class="soft-man-con"></div>
</div>
</div>
</div>
<script type="text/javascript">
$.getScript( "/plugins/file?name=imail&f=js/imail.js", function(){
mail.init();
pluginService('imail');
});
</script>
-218
View File
@@ -1,218 +0,0 @@
# coding:utf-8
import sys
import io
import os
import time
import re
import socket
import json
from datetime import datetime
sys.path.append(os.getcwd() + "/class/core")
import mw
app_debug = False
if mw.isAppleSystem():
app_debug = True
class App:
__setupPath = '/www/server/imail'
__SR = ''
def __init__(self):
self.__setupPath = self.getServerDir()
self.__SR = '''#!/bin/bash
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
export PATH
export USER=%s
export HOME=%s && ''' % ( self.getRunUser(), self.getHomeDir())
def getArgs(self):
args = sys.argv[3:]
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 __release_port(self, port):
from collections import namedtuple
try:
import firewall_api
firewall_api.firewall_api().addAcceptPortArgs(port, 'IMail-Server', 'port')
return port
except Exception as e:
return "Release failed {}".format(e)
def openPort(self):
for i in ["25", "110", "143", "465", "995", "993", "587"]:
self.__release_port(i)
return True
def getPluginName(self):
return 'imail'
def getPluginDir(self):
return mw.getPluginDir() + '/' + self.getPluginName()
def getServerDir(self):
return mw.getServerDir() + '/' + self.getPluginName()
def getInitdConfTpl(self):
path = self.getPluginDir() + "/init.d/imail.tpl"
return path
def getHomeDir(self):
if mw.isAppleSystem():
user = mw.execShell(
"who | sed -n '2, 1p' |awk '{print $1}'")[0].strip()
return '/Users/' + user
else:
return '/root'
def getRunUser(self):
if mw.isAppleSystem():
user = mw.execShell(
"who | sed -n '2, 1p' |awk '{print $1}'")[0].strip()
return user
else:
return 'root'
def status(self):
data = mw.execShell(
"ps -ef|grep " + self.getPluginName() + " |grep -v grep | grep -v python | awk '{print $2}'")
if data[0] == '':
return 'stop'
return 'start'
def contentReplace(self, content):
service_path = mw.getServerDir()
content = content.replace('{$ROOT_PATH}', mw.getRootDir())
content = content.replace('{$SERVER_PATH}', service_path)
content = content.replace('{$RUN_USER}', self.getRunUser())
content = content.replace('{$HOME_DIR}', self.getHomeDir())
return content
def initDreplace(self):
file_tpl = self.getInitdConfTpl()
service_path = mw.getServerDir()
initD_path = self.getServerDir() + '/init.d'
if not os.path.exists(initD_path):
os.mkdir(initD_path)
self.openPort()
file_bin = initD_path + '/' + self.getPluginName()
if not os.path.exists(file_bin):
content = mw.readFile(file_tpl)
content = self.contentReplace(content)
mw.writeFile(file_bin, content)
mw.execShell('chmod +x ' + file_bin)
# systemd
systemDir = mw.systemdCfgDir()
systemService = systemDir + '/imail.service'
systemServiceTpl = self.getPluginDir() + '/init.d/imail.service.tpl'
if os.path.exists(systemDir) and not os.path.exists(systemService):
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')
log_path = self.getServerDir() + '/logs'
if not os.path.exists(log_path):
os.mkdir(log_path)
return file_bin
def imOp(self, method):
file = self.initDreplace()
if not mw.isAppleSystem():
cmd = 'systemctl {} {}'.format(method, self.getPluginName())
data = mw.execShell(cmd)
if data[1] == '':
return 'ok'
return 'fail'
data = mw.execShell(self.__SR + file + ' ' + method)
if data[1] == '':
return 'ok'
return data[0]
def start(self):
return self.imOp('start')
def stop(self):
return self.imOp('stop')
def restart(self):
return self.imOp('restart')
def reload(self):
return self.imOp('reload')
def initd_status(self):
if mw.isAppleSystem():
return "Apple Computer does not support"
cmd = 'systemctl status imail | grep loaded | grep "enabled;"'
data = mw.execShell(cmd)
if data[0] == '':
return 'fail'
return 'ok'
def initd_install(self):
if mw.isAppleSystem():
return "Apple Computer does not support"
mw.execShell('systemctl enable imail')
return 'ok'
def initd_uinstall(self):
if mw.isAppleSystem():
return "Apple Computer does not support"
mw.execShell('systemctl disable imail')
return 'ok'
def conf(self):
conf_path = self.getServerDir() + '/custom/conf/app.conf'
if not os.path.exists(conf_path):
return mw.returnJson(False, "请先安装初始化!<br/>默认地址:http://" + mw.getLocalIp() + ":1080")
return self.getServerDir() + '/custom/conf/app.conf'
def run_log(self):
ilog = self.getServerDir() + '/logs/imail.log'
if not os.path.exists(ilog):
return mw.returnJson(False, "请先安装初始化!<br/>默认地址:http://" + mw.getLocalIp() + ":1080")
return self.getServerDir() + '/logs/imail.log'
if __name__ == "__main__":
func = sys.argv[1]
classApp = App()
try:
data = eval("classApp." + func + "()")
print(data)
except Exception as e:
print('error:' + str(e))
-18
View File
@@ -1,18 +0,0 @@
{
"sort": 7,
"ps": "[<b style='color:red;'>DEV</b>]简单邮件服务",
"name": "imail",
"title": "邮件服务",
"shell": "install.sh",
"versions":["0.0.19"],
"updates":["0.0.19"],
"tip": "soft",
"checks": "server/imail",
"path": "server/imail",
"display": 1,
"author": "midoks",
"date": "2022-09-26",
"home": "https://github.com/midoks/mdserver-web",
"type": 0,
"pid": "5"
}
-12
View File
@@ -1,12 +0,0 @@
[Unit]
Description=Imail Simple Mail Server
After=network.target
[Service]
Type=forking
ExecStart={$SERVER_PATH}/imail/init.d/imail start
ExecStop={$SERVER_PATH}/imail/init.d/imail stop
RemainAfterExit=yes
[Install]
WantedBy=multi-user.target
-89
View File
@@ -1,89 +0,0 @@
#!/bin/bash
# chkconfig: 2345 55 25
# description: Imail Service
### BEGIN INIT INFO
# Provides: bt
# Required-Start: $all
# Required-Stop: $all
# Default-Start: 2 3 4 5
# Default-Stop: 0 1 6
# Short-Description: starts Imail
# Description: starts the Imail
### END INIT INFO
PATH=/bin:/sbin:/usr/bin:/usr/sbin:/usr/local/bin:/usr/local/sbin:~/bin
# Source function library.
if [ -f /etc/init.d/functions ];then
. /etc/init.d/functions
fi
if [ -f /etc/rc.d/init.d/functions ];then
. /etc/rc.d/init.d/functions
fi
app_path={$SERVER_PATH}/imail
SERVICENAME="imail"
im_start(){
isStart=`ps -ef|grep 'imail service' |grep -v grep|awk '{print $2}'`
if [ "$isStart" == '' ];then
echo -e "Starting imail... \c"
cd $app_path && ${app_path}/imail service &
isStart=""
while [[ "$isStart" == "" ]];
do
echo -e ".\c"
sleep 0.5
isStart=$(lsof -n -P -i:25|grep LISTEN|grep -v grep|awk '{print $2}'|xargs)
let n+=1
if [ $n -gt 15 ];then
break;
fi
done
if [ "$isStart" == '' ];then
echo -e "\033[31mfailed\033[0m"
echo '------------------------------------------------------'
tail -n 20 ${app_path}/logs/run_away.log
echo '------------------------------------------------------'
echo -e "\033[31mError: ${SERVICENAME} service startup failed.\033[0m"
return;
fi
echo -e "\033[32mdone\033[0m"
else
echo "Starting ${SERVICENAME}(pid $(echo $isStart)) already running"
fi
}
im_stop(){
pids=`ps -ef|grep 'imail service' |grep -v grep|awk '{print $2}'`
arr=($pids)
echo -e "Stopping ${SERVICENAME}... \c"
for p in ${arr[@]}
do
kill -9 $p
done
echo -e "\033[32mdone\033[0m"
}
im_status(){
isStart=`ps -ef|grep 'imail service' |grep -v grep|awk '{print $2}'`
if [ "$isStart" == '' ];then
echo -e "${SERVICENAME} not running"
else
echo -e "${SERVICENAME}(pid $(echo $isStart)) already running"
fi
}
case "$1" in
'start') im_start;;
'stop') im_stop;;
'status') im_status;;
'reload')
im_stop
im_start;;
'restart')
im_stop
im_start;;
esac
-25
View File
@@ -1,25 +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")
install_tmp=${rootPath}/tmp/mw_install.pl
action=$1
type=$2
if [ "${2}" == "" ];then
echo '缺少安装脚本...' > $install_tmp
exit 0
fi
if [ ! -d $curPath/versions/$2 ];then
echo '缺少安装脚本2...' > $install_tmp
exit 0
fi
sh -x $curPath/versions/$2/install.sh $1

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