Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
f6d4609947 | ||
|
|
d2ddcebe3e | ||
|
|
fd2f2c0aa9 | ||
|
|
0aae3b0654 | ||
|
|
e56606ac61 | ||
|
|
54e11700c2 | ||
|
|
fe3b8de490 | ||
|
|
9874069100 | ||
|
|
9419d6d55b | ||
|
|
2f61facbda | ||
|
|
5cd8d7bf18 | ||
|
|
41f016050f | ||
|
|
d931790dd3 | ||
|
|
a81e63e131 | ||
|
|
30717079d4 | ||
|
|
3eb2fbb654 | ||
|
|
cf54a430eb | ||
|
|
04925c0534 | ||
|
|
6473755127 | ||
|
|
35f40fc8fc | ||
|
|
088a4ab969 | ||
|
|
3c00ddad75 | ||
|
|
5edd9baeb6 | ||
|
|
734548d764 | ||
|
|
e47d3a235d | ||
|
|
0a93015720 | ||
|
|
1078d71370 | ||
|
|
b482127044 | ||
|
|
27b6ee135c | ||
|
|
cc2000267d | ||
|
|
c1e3655923 | ||
|
|
b0a6181dca | ||
|
|
33e7efa440 | ||
|
|
6d7a38dc08 | ||
|
|
8a60965069 | ||
|
|
ef9d627095 | ||
|
|
b66acc8c2a | ||
|
|
fb56f6c190 | ||
|
|
af0f55e6ef | ||
|
|
fb95e2ad8d | ||
|
|
138a6d3e66 | ||
|
|
98b9065585 | ||
|
|
36c3fae920 | ||
|
|
70ee376958 | ||
|
|
47b2f1b009 | ||
|
|
f2a35b97b0 | ||
|
|
c26e5dc40d | ||
|
|
893b2d601c | ||
|
|
28964721ec | ||
|
|
5a2028d797 | ||
|
|
fcbe6a87df | ||
|
|
7f5cf75d68 | ||
|
|
c368638181 | ||
|
|
decc9d2b29 | ||
|
|
7da2a90712 | ||
|
|
7dd377460c | ||
|
|
6b6ae63aea | ||
|
|
cc9a4b5536 | ||
|
|
fd44a312f6 | ||
|
|
a5802b4280 | ||
|
|
d8c928dc02 | ||
|
|
339cdf662b | ||
|
|
1e2a174e30 | ||
|
|
2714538ad7 | ||
|
|
8a43f67801 | ||
|
|
f3f7ef4a3d | ||
|
|
3523f8d6c8 | ||
|
|
eb8c2b6ad5 | ||
|
|
438322361d | ||
|
|
c902f48931 | ||
|
|
e67a258e7a | ||
|
|
007993ed9e | ||
|
|
81171c0c7c | ||
|
|
15d07e4234 | ||
|
|
786fac9142 | ||
|
|
5499c8795f | ||
|
|
322698f345 | ||
|
|
f229e03f10 | ||
|
|
6024dbcdb6 | ||
|
|
c874ea44eb | ||
|
|
c70e4d1412 | ||
|
|
235c5e329e | ||
|
|
b77bfab5a8 | ||
|
|
890bbc4214 | ||
|
|
2f86f4d6ff | ||
|
|
c9ced293e8 | ||
|
|
43f081aac1 | ||
|
|
49aa411248 | ||
|
|
d597e15402 | ||
|
|
a74ee3faf4 | ||
|
|
cfe55ca196 | ||
|
|
2edffd36d0 | ||
|
|
c7077ec283 | ||
|
|
cfcd4c2050 | ||
|
|
2387af2880 | ||
|
|
72f825c0c4 | ||
|
|
fbe4857944 | ||
|
|
01fe75e553 | ||
|
|
2bd607edad | ||
|
|
9b9a806dee | ||
|
|
9bc6d392a0 | ||
|
|
26b5dc7cd4 | ||
|
|
80a690c431 | ||
|
|
71840ee1e2 | ||
|
|
fbdbb25a78 | ||
|
|
e546428596 | ||
|
|
a16259f718 | ||
|
|
df0b708462 | ||
|
|
b3c383327d | ||
|
|
8a9c99fb0d | ||
|
|
a22d90198c |
@@ -87,6 +87,7 @@ venv/
|
||||
ENV/
|
||||
env.bak/
|
||||
venv.bak/
|
||||
lib/python*
|
||||
|
||||
# Spyder project settings
|
||||
.spyderproject
|
||||
@@ -98,6 +99,12 @@ venv.bak/
|
||||
# mkdocs documentation
|
||||
/site
|
||||
|
||||
bin
|
||||
pyvenv.cfg
|
||||
include
|
||||
share
|
||||
pip-selfcheck.json
|
||||
|
||||
# mypy
|
||||
.mypy_cache/
|
||||
|
||||
@@ -122,27 +129,17 @@ data/default.pl
|
||||
data/backup.pl
|
||||
data/debug.pl
|
||||
data/default_site.pl
|
||||
|
||||
ssl/input.pl
|
||||
|
||||
lib/python*
|
||||
|
||||
bin
|
||||
pyvenv.cfg
|
||||
include
|
||||
share
|
||||
pip-selfcheck.json
|
||||
data/ssl.pl
|
||||
|
||||
scripts/init.d/mw
|
||||
scripts/mdserver-web
|
||||
|
||||
data/api_login.txt
|
||||
data/sessions
|
||||
data/ssl.pl
|
||||
|
||||
data/port.pl
|
||||
data/ipv6.pl
|
||||
data/restart.pl
|
||||
data/ssl.pl
|
||||
data/edate.pl
|
||||
data/osname.pl
|
||||
data/only_netio_counters.pl
|
||||
@@ -156,6 +153,10 @@ data/bind_domain.pl
|
||||
data/unauthorized_status.pl
|
||||
data/auth_secret.pl
|
||||
|
||||
ssl/local
|
||||
ssl/nginx
|
||||
ssl/choose.pl
|
||||
|
||||
plugins/vip_*
|
||||
plugins/own_*
|
||||
plugins/my_*
|
||||
|
||||
@@ -110,12 +110,9 @@ docker run -itd --name mw-server --privileged=true -p 7200:7200 -p 80:80 -p 443:
|
||||
```
|
||||
|
||||
|
||||
### 版本更新 0.16.9
|
||||
### 版本更新 0.17.0
|
||||
|
||||
- mysql同步优化,享受丝滑般感觉。
|
||||
- 网站统计 - 实时-可调节1-10s。
|
||||
- 网站统计 - 加入大小条件。
|
||||
- Sphinx优化。
|
||||
- 面板SSL调整。
|
||||
|
||||
### JSDelivr安装地址
|
||||
|
||||
|
||||
@@ -28,7 +28,7 @@ from flask import request
|
||||
|
||||
class config_api:
|
||||
|
||||
__version = '0.16.9'
|
||||
__version = '0.17.0'
|
||||
__api_addr = 'data/api.json'
|
||||
|
||||
# 统一默认配置文件
|
||||
@@ -42,7 +42,7 @@ class config_api:
|
||||
'bind_domain' : 'data/bind_domain.pl', # 面板域名绑定
|
||||
'unauth_status' : 'data/unauthorized_status.pl', # URL路径未成功显示状态
|
||||
'auth_secret': 'data/auth_secret.pl', # 二次验证密钥
|
||||
'ssl':'data/ssl.pl', # ssl设置
|
||||
'ssl':'ssl/choose.pl', # ssl设置
|
||||
'hook_database' : 'data/hook_database.json', # 数据库钩子
|
||||
'hook_menu' : 'data/hook_menu.json', # 菜单钩子
|
||||
'hook_global_static' : 'data/hook_global_static.json', # 静态文件钩子
|
||||
@@ -362,19 +362,23 @@ class config_api:
|
||||
return mw.getJson(cert)
|
||||
|
||||
def getPanelSslData(self):
|
||||
cert = {}
|
||||
keyPath = 'ssl/private.pem'
|
||||
certPath = 'ssl/cert.pem'
|
||||
rdata = {}
|
||||
choose_file = self.__file['ssl']
|
||||
rdata['choose'] = mw.readFile(choose_file)
|
||||
|
||||
|
||||
keyPath = 'ssl/local/private.pem'
|
||||
certPath = 'ssl/local/cert.pem'
|
||||
|
||||
if not os.path.exists(certPath):
|
||||
# 不再自动生成证书
|
||||
# mw.createSSL()
|
||||
cert['privateKey'] = ''
|
||||
cert['is_https'] = ''
|
||||
cert['certPem'] = ''
|
||||
cert['rep'] = os.path.exists('ssl/input.pl')
|
||||
cert['info'] = {'endtime': 0, 'subject': '无',
|
||||
'notAfter': '无', 'notBefore': '无', 'issuer': '无'}
|
||||
return cert
|
||||
mw.createLocalSSL()
|
||||
|
||||
cert = {}
|
||||
cert['privateKey'] = mw.readFile(keyPath)
|
||||
cert['is_https'] = ''
|
||||
cert['certPem'] = mw.readFile(certPath)
|
||||
cert['info'] = mw.getCertName(certPath)
|
||||
rdata['local'] = cert
|
||||
|
||||
panel_ssl = mw.getServerDir() + "/web_conf/nginx/vhost/panel.conf"
|
||||
if not os.path.exists(panel_ssl):
|
||||
@@ -384,21 +388,68 @@ class config_api:
|
||||
if ssl_data.find('$server_port !~ 443') != -1:
|
||||
cert['is_https'] = 'checked'
|
||||
|
||||
keyPath = 'ssl/nginx/private.pem'
|
||||
certPath = 'ssl/nginx/cert.pem'
|
||||
|
||||
cert = {}
|
||||
cert['privateKey'] = mw.readFile(keyPath)
|
||||
cert['certPem'] = mw.readFile(certPath)
|
||||
cert['rep'] = os.path.exists('ssl/input.pl')
|
||||
cert['info'] = mw.getCertName(certPath)
|
||||
return cert
|
||||
|
||||
rdata['nginx'] = cert
|
||||
|
||||
return rdata
|
||||
|
||||
# 面板本地SSL设置
|
||||
def setPanelLocalSslApi(self):
|
||||
cert = {}
|
||||
keyPath = 'ssl/local/private.pem'
|
||||
certPath = 'ssl/local/cert.pem'
|
||||
|
||||
if not os.path.exists(certPath):
|
||||
mw.createLocalSSL()
|
||||
|
||||
choose_file = self.__file['ssl']
|
||||
mw.writeFile(choose_file, 'local')
|
||||
|
||||
mw.restartMw()
|
||||
return mw.returnJson(True, '设置成功')
|
||||
|
||||
# 关闭SSL
|
||||
def closePanelSslApi(self):
|
||||
choose_file = self.__file['ssl']
|
||||
if os.path.exists(choose_file):
|
||||
os.remove(choose_file)
|
||||
|
||||
local_ssl = 'ssl/local'
|
||||
if os.path.exists(local_ssl):
|
||||
mw.execShell('rm -rf '+ local_ssl)
|
||||
|
||||
nginx_ssl = 'ssl/nginx'
|
||||
if os.path.exists(nginx_ssl):
|
||||
mw.execShell('rm -rf '+ nginx_ssl)
|
||||
|
||||
mw.restartMw()
|
||||
return mw.returnJson(True, '关闭SSL成功')
|
||||
|
||||
# 保存面板证书
|
||||
def savePanelSslApi(self):
|
||||
keyPath = 'ssl/private.pem'
|
||||
certPath = 'ssl/cert.pem'
|
||||
checkCert = '/tmp/cert.pl'
|
||||
|
||||
choose = request.form.get('choose', '').strip()
|
||||
certPem = request.form.get('certPem', '').strip()
|
||||
privateKey = request.form.get('privateKey', '').strip()
|
||||
|
||||
if not mw.inArray(['local','nginx'], choose):
|
||||
return mw.returnJson(True, '保存错误面板SSL类型!')
|
||||
|
||||
|
||||
keyPath = 'ssl/'+choose+'/private.pem'
|
||||
certPath = 'ssl/'+choose+'/cert.pem'
|
||||
checkCert = '/tmp/cert.pl'
|
||||
|
||||
if not os.path.exists(keyPath):
|
||||
return mw.returnJson(False, '【'+choose+'】SSL类型不存在,先申请!')
|
||||
|
||||
if(privateKey.find('KEY') == -1):
|
||||
return mw.returnJson(False, '秘钥错误,请检查!')
|
||||
if(certPem.find('CERTIFICATE') == -1):
|
||||
@@ -411,258 +462,303 @@ class config_api:
|
||||
mw.writeFile(certPath, certPem)
|
||||
if not mw.checkCert(checkCert):
|
||||
return mw.returnJson(False, '证书错误,请检查!')
|
||||
mw.writeFile('ssl/input.pl', 'True')
|
||||
return mw.returnJson(True, '证书已保存!')
|
||||
|
||||
# 设置面板SSL证书设置
|
||||
def setPanelHttpToHttpsApi(self):
|
||||
# def setPanelHttpToHttpsApi(self):
|
||||
|
||||
bind_domain = self.__file['bind_domain']
|
||||
if not os.path.exists(bind_domain):
|
||||
return mw.returnJson(False, '先要绑定域名!')
|
||||
# bind_domain = self.__file['bind_domain']
|
||||
# if not os.path.exists(bind_domain):
|
||||
# return mw.returnJson(False, '先要绑定域名!')
|
||||
|
||||
keyPath = 'ssl/private.pem'
|
||||
if not os.path.exists(keyPath):
|
||||
return mw.returnJson(False, '未申请SSL证书!')
|
||||
# choose_file = self.__file['ssl']
|
||||
# choose = mw.readFile(choose_file)
|
||||
# if choose == 'local':
|
||||
# return mw.returnJson(False, '本地SSL无法使用!')
|
||||
|
||||
is_https = request.form.get('https', '').strip()
|
||||
# keyPath = 'ssl/nginx/private.pem'
|
||||
# if not os.path.exists(keyPath):
|
||||
# return mw.returnJson(False, '未申请SSL证书!')
|
||||
|
||||
panel_ssl = mw.getServerDir() + "/web_conf/nginx/vhost/panel.conf"
|
||||
if not os.path.exists(panel_ssl):
|
||||
return mw.returnJson(False, '未开启面板SSL!')
|
||||
# is_https = request.form.get('https', '').strip()
|
||||
|
||||
if is_https == 'false':
|
||||
conf = mw.readFile(panel_ssl)
|
||||
if conf:
|
||||
if conf.find('ssl_certificate') == -1:
|
||||
return mw.returnJson(False, '当前未开启SSL')
|
||||
to = "#error_page 404/404.html;\n\
|
||||
#HTTP_TO_HTTPS_START\n\
|
||||
if ($server_port !~ 443){\n\
|
||||
rewrite ^(/.*)$ https://$host$1 permanent;\n\
|
||||
}\n\
|
||||
#HTTP_TO_HTTPS_END"
|
||||
conf = conf.replace('#error_page 404/404.html;', to)
|
||||
mw.writeFile(panel_ssl, conf)
|
||||
else:
|
||||
conf = mw.readFile(panel_ssl)
|
||||
if conf:
|
||||
rep = "\n\\s*#HTTP_TO_HTTPS_START(.|\n){1,300}#HTTP_TO_HTTPS_END"
|
||||
conf = re.sub(rep, '', conf)
|
||||
rep = "\\s+if.+server_port.+\n.+\n\\s+\\s*}"
|
||||
conf = re.sub(rep, '', conf)
|
||||
mw.writeFile(panel_ssl, conf)
|
||||
# panel_ssl = mw.getServerDir() + "/web_conf/nginx/vhost/panel.conf"
|
||||
# if not os.path.exists(panel_ssl):
|
||||
# return mw.returnJson(False, '未开启面板SSL!')
|
||||
|
||||
mw.restartWeb()
|
||||
# if is_https == 'false':
|
||||
# conf = mw.readFile(panel_ssl)
|
||||
# if conf:
|
||||
# if conf.find('ssl_certificate') == -1:
|
||||
# return mw.returnJson(False, '当前未开启SSL')
|
||||
# to = "#error_page 404/404.html;\n\
|
||||
# #HTTP_TO_HTTPS_START\n\
|
||||
# if ($server_port !~ 443){\n\
|
||||
# rewrite ^(/.*)$ https://$host$1 permanent;\n\
|
||||
# }\n\
|
||||
# #HTTP_TO_HTTPS_END"
|
||||
# conf = conf.replace('#error_page 404/404.html;', to)
|
||||
# mw.writeFile(panel_ssl, conf)
|
||||
# else:
|
||||
# conf = mw.readFile(panel_ssl)
|
||||
# if conf:
|
||||
# rep = "\n\\s*#HTTP_TO_HTTPS_START(.|\n){1,300}#HTTP_TO_HTTPS_END"
|
||||
# conf = re.sub(rep, '', conf)
|
||||
# rep = "\\s+if.+server_port.+\n.+\n\\s+\\s*}"
|
||||
# conf = re.sub(rep, '', conf)
|
||||
# mw.writeFile(panel_ssl, conf)
|
||||
|
||||
action = '开启'
|
||||
if is_https == 'true':
|
||||
action = '关闭'
|
||||
return mw.returnJson(True, action + 'HTTPS跳转成功!')
|
||||
# mw.restartNginx()
|
||||
|
||||
# action = '开启'
|
||||
# if is_https == 'true':
|
||||
# action = '关闭'
|
||||
# return mw.returnJson(True, action + 'HTTPS跳转成功!')
|
||||
|
||||
# 删除面板证书
|
||||
def delPanelSslApi(self):
|
||||
bind_domain = self.__file['bind_domain']
|
||||
if not os.path.exists(bind_domain):
|
||||
return mw.returnJson(False, '未绑定域名!')
|
||||
ip = mw.getLocalIp()
|
||||
if mw.isAppleSystem():
|
||||
ip = '127.0.0.1'
|
||||
|
||||
siteName = mw.readFile(bind_domain).strip()
|
||||
port = mw.readFile('data/port.pl').strip()
|
||||
|
||||
src_letpath = mw.getServerDir() + '/web_conf/letsencrypt/' + siteName
|
||||
choose = request.form.get('choose', '').strip()
|
||||
|
||||
dst_letpath = mw.getRunDir() + '/ssl'
|
||||
dst_csrpath = dst_letpath + '/cert.pem'
|
||||
dst_keypath = dst_letpath + '/private.pem'
|
||||
if not mw.inArray(['local','nginx'], choose):
|
||||
return mw.returnJson(True, '删除错误面板SSL类型!')
|
||||
|
||||
if os.path.exists(src_letpath) or os.path.exists(dst_csrpath):
|
||||
if os.path.exists(src_letpath):
|
||||
mw.execShell('rm -rf ' + src_letpath)
|
||||
if os.path.exists(dst_csrpath):
|
||||
mw.execShell('rm -rf ' + dst_csrpath)
|
||||
if os.path.exists(dst_keypath):
|
||||
mw.execShell('rm -rf ' + dst_keypath)
|
||||
# mw.restartWeb()
|
||||
return mw.returnJson(True, '已经删除SSL!')
|
||||
to_panel_url = 'http://'+ip+":"+port+'/config'
|
||||
|
||||
# mw.restartWeb()
|
||||
return mw.returnJson(False, '已经不存在SSL!')
|
||||
if choose == 'local':
|
||||
dst_path = mw.getRunDir() + '/ssl/local'
|
||||
ssl_file = self.__file['ssl']
|
||||
if os.path.exists(dst_path):
|
||||
mw.execShell('rm -rf ' + dst_path)
|
||||
mw.execShell('rm -rf ' + ssl_file)
|
||||
mw.restartMw();
|
||||
return mw.returnJson(True, '删除本地面板SSL成功!',to_panel_url)
|
||||
else:
|
||||
return mw.returnJson(True, '已经删除本地面板SSL!',to_panel_url)
|
||||
|
||||
if choose == 'nginx':
|
||||
|
||||
bind_domain = self.__file['bind_domain']
|
||||
if not os.path.exists(bind_domain):
|
||||
return mw.returnJson(False, '未绑定域名!')
|
||||
|
||||
siteName = mw.readFile(bind_domain).strip()
|
||||
|
||||
src_path = mw.getServerDir() + '/web_conf/letsencrypt/' + siteName
|
||||
|
||||
dst_path = mw.getRunDir() + '/ssl/nginx'
|
||||
dst_csrpath = dst_path + '/cert.pem'
|
||||
dst_keypath = dst_path + '/private.pem'
|
||||
|
||||
if os.path.exists(src_path) or os.path.exists(dst_path):
|
||||
if os.path.exists(src_letpath):
|
||||
mw.execShell('rm -rf ' + src_letpath)
|
||||
if os.path.exists(dst_csrpath):
|
||||
mw.execShell('rm -rf ' + dst_csrpath)
|
||||
if os.path.exists(dst_keypath):
|
||||
mw.execShell('rm -rf ' + dst_keypath)
|
||||
mw.restartNginx()
|
||||
return mw.returnJson(True, '删除面板SSL成功!')
|
||||
|
||||
mw.restartNginx()
|
||||
mw.restartMw()
|
||||
return mw.returnJson(False, '已经删除面板SSL!')
|
||||
return mw.returnJson(False, '未知类型!')
|
||||
|
||||
# 申请面板let证书
|
||||
def applyPanelLetSslApi(self):
|
||||
# def applyPanelAcmeSslApi(self):
|
||||
|
||||
# check domain is bind?
|
||||
bind_domain = self.__file['bind_domain']
|
||||
if not os.path.exists(bind_domain):
|
||||
return mw.returnJson(False, '先要绑定域名!')
|
||||
# bind_domain = self.__file['bind_domain']
|
||||
# if not os.path.exists(bind_domain):
|
||||
# return mw.returnJson(False, '先要绑定域名!')
|
||||
|
||||
siteName = mw.readFile(bind_domain).strip()
|
||||
auth_to = mw.getRunDir() + "/tmp"
|
||||
to_args = {
|
||||
'domains': [siteName],
|
||||
'auth_type': 'http',
|
||||
'auth_to': auth_to,
|
||||
}
|
||||
# # 生成nginx配置
|
||||
# domain = mw.readFile(bind_domain)
|
||||
# panel_tpl = mw.getRunDir() + "/data/tpl/nginx_panel.conf"
|
||||
# dst_panel_path = mw.getServerDir() + "/web_conf/nginx/vhost/panel.conf"
|
||||
# if not os.path.exists(dst_panel_path):
|
||||
# reg = r"^([\w\-\*]{1,100}\.){1,4}(\w{1,10}|\w{1,10}\.\w{1,10})$"
|
||||
# if not re.match(reg, domain):
|
||||
# return mw.returnJson(False, '主域名格式不正确')
|
||||
|
||||
src_letpath = mw.getServerDir() + '/web_conf/letsencrypt/' + siteName
|
||||
src_csrpath = src_letpath + "/fullchain.pem" # 生成证书路径
|
||||
src_keypath = src_letpath + "/privkey.pem" # 密钥文件路径
|
||||
# op_dir = mw.getServerDir() + "/openresty"
|
||||
# if not os.path.exists(op_dir):
|
||||
# return mw.returnJson(False, '依赖OpenResty,先安装启动它!')
|
||||
|
||||
dst_letpath = mw.getRunDir() + '/ssl'
|
||||
dst_csrpath = dst_letpath + '/cert.pem'
|
||||
dst_keypath = dst_letpath + '/private.pem'
|
||||
# content = mw.readFile(panel_tpl)
|
||||
# content = content.replace("{$PORT}", "80")
|
||||
# content = content.replace("{$SERVER_NAME}", domain)
|
||||
# content = content.replace("{$PANAL_PORT}", mw.readFile('data/port.pl'))
|
||||
# content = content.replace("{$LOGPATH}", mw.getRunDir() + '/logs')
|
||||
# content = content.replace("{$PANAL_ADDR}", mw.getRunDir())
|
||||
# mw.writeFile(dst_panel_path, content)
|
||||
# mw.restartNginx()
|
||||
|
||||
is_already_apply = False
|
||||
# siteName = mw.readFile(bind_domain).strip()
|
||||
# auth_to = mw.getRunDir() + "/tmp"
|
||||
# to_args = {
|
||||
# 'domains': [siteName],
|
||||
# 'auth_type': 'http',
|
||||
# 'auth_to': auth_to,
|
||||
# }
|
||||
|
||||
if not os.path.exists(src_letpath):
|
||||
import cert_api
|
||||
data = cert_api.cert_api().applyCertApi(to_args)
|
||||
if not data['status']:
|
||||
msg = data['msg']
|
||||
if type(data['msg']) != str:
|
||||
msg = data['msg'][0]
|
||||
emsg = data['msg'][1]['challenges'][0]['error']
|
||||
msg = msg + '<p><span>响应状态:</span>' + str(emsg['status']) + '</p><p><span>错误类型:</span>' + emsg[
|
||||
'type'] + '</p><p><span>错误代码:</span>' + emsg['detail'] + '</p>'
|
||||
return mw.returnJson(data['status'], msg, data['msg'])
|
||||
else:
|
||||
is_already_apply = True
|
||||
# src_path = mw.getServerDir() + '/web_conf/letsencrypt/' + siteName
|
||||
# src_csrpath = src_path + "/fullchain.pem" # 生成证书路径
|
||||
# src_keypath = src_path + "/privkey.pem" # 密钥文件路径
|
||||
|
||||
mw.buildSoftLink(src_csrpath, dst_csrpath, True)
|
||||
mw.buildSoftLink(src_keypath, dst_keypath, True)
|
||||
mw.execShell('echo "lets" > "' + dst_letpath + '/README"')
|
||||
# dst_path = mw.getRunDir() + '/ssl/nginx'
|
||||
# dst_csrpath = dst_path + '/cert.pem'
|
||||
# dst_keypath = dst_path + '/private.pem'
|
||||
|
||||
data = self.getPanelSslData()
|
||||
# is_already_apply = False
|
||||
|
||||
tmp_well_know = auth_to + '/.well-known'
|
||||
if os.path.exists(tmp_well_know):
|
||||
mw.execShell('rm -rf ' + tmp_well_know)
|
||||
# if not os.path.exists(src_path):
|
||||
# import cert_api
|
||||
# data = cert_api.cert_api().applyCertApi(to_args)
|
||||
# if not data['status']:
|
||||
# msg = data['msg']
|
||||
# if type(data['msg']) != str:
|
||||
# msg = data['msg'][0]
|
||||
# emsg = data['msg'][1]['challenges'][0]['error']
|
||||
# msg = msg + '<p><span>响应状态:</span>' + str(emsg['status']) + '</p><p><span>错误类型:</span>' + emsg[
|
||||
# 'type'] + '</p><p><span>错误代码:</span>' + emsg['detail'] + '</p>'
|
||||
# return mw.returnJson(data['status'], msg, data['msg'])
|
||||
# else:
|
||||
# is_already_apply = True
|
||||
|
||||
if is_already_apply:
|
||||
return mw.returnJson(True, '重复申请!', data)
|
||||
# mw.buildSoftLink(src_csrpath, dst_csrpath, True)
|
||||
# mw.buildSoftLink(src_keypath, dst_keypath, True)
|
||||
# mw.execShell('echo "acme" > "' + dst_path + '/README"')
|
||||
|
||||
return mw.returnJson(True, '申请成功!', data)
|
||||
# tmp_well_know = auth_to + '/.well-known'
|
||||
# if os.path.exists(tmp_well_know):
|
||||
# mw.execShell('rm -rf ' + tmp_well_know)
|
||||
|
||||
# if os.path.exists(dst_path):
|
||||
# choose_file = self.__file['ssl']
|
||||
# mw.writeFile(choose_file, 'nginx')
|
||||
|
||||
# data = self.getPanelSslData()
|
||||
|
||||
# if is_already_apply:
|
||||
# return mw.returnJson(True, '重复申请!', data)
|
||||
# return mw.returnJson(True, '申请成功!', data)
|
||||
|
||||
def setPanelDomainApi(self):
|
||||
domain = request.form.get('domain', '')
|
||||
|
||||
panel_tpl = mw.getRunDir() + "/data/tpl/nginx_panel.conf"
|
||||
dst_panel_path = mw.getServerDir() + "/web_conf/nginx/vhost/panel.conf"
|
||||
|
||||
cfg_domain = self.__file['bind_domain']
|
||||
|
||||
port = mw.readFile('data/port.pl').strip()
|
||||
ip = mw.getLocalIp()
|
||||
|
||||
if domain == '':
|
||||
os.remove(cfg_domain)
|
||||
os.remove(dst_panel_path)
|
||||
mw.restartWeb()
|
||||
return mw.returnJson(True, '清空域名成功!')
|
||||
if os.path.exists(cfg_domain):
|
||||
os.remove(cfg_domain)
|
||||
|
||||
reg = r"^([\w\-\*]{1,100}\.){1,4}(\w{1,10}|\w{1,10}\.\w{1,10})$"
|
||||
if not re.match(reg, domain):
|
||||
return mw.returnJson(False, '主域名格式不正确')
|
||||
|
||||
op_dir = mw.getServerDir() + "/openresty"
|
||||
if not os.path.exists(op_dir):
|
||||
return mw.returnJson(False, '依赖OpenResty,先安装启动它!')
|
||||
|
||||
content = mw.readFile(panel_tpl)
|
||||
content = content.replace("{$PORT}", "80")
|
||||
content = content.replace("{$SERVER_NAME}", domain)
|
||||
content = content.replace("{$PANAL_PORT}", mw.readFile('data/port.pl'))
|
||||
content = content.replace("{$LOGPATH}", mw.getRunDir() + '/logs')
|
||||
content = content.replace("{$PANAL_ADDR}", mw.getRunDir())
|
||||
mw.writeFile(dst_panel_path, content)
|
||||
mw.restartWeb()
|
||||
to_panel_url = 'http://'+ip+":"+port+'/config'
|
||||
|
||||
mw.restartMw()
|
||||
return mw.returnJson(True, '清空域名成功!', to_panel_url)
|
||||
|
||||
mw.writeFile(cfg_domain, domain)
|
||||
return mw.returnJson(True, '设置域名成功!')
|
||||
to_panel_url = 'http://'+domain+":"+port+'/config'
|
||||
mw.restartMw()
|
||||
return mw.returnJson(True, '设置域名成功!',to_panel_url)
|
||||
|
||||
# 设置面板SSL
|
||||
def setPanelSslApi(self):
|
||||
sslConf = mw.getRunDir() + '/' + self.__file['ssl']
|
||||
# def setPanelSslApi(self):
|
||||
# sslConf = mw.getRunDir() + '/' + self.__file['ssl']
|
||||
|
||||
panel_tpl = mw.getRunDir() + "/data/tpl/nginx_panel.conf"
|
||||
dst_panel_path = mw.getServerDir() + "/web_conf/nginx/vhost/panel.conf"
|
||||
if os.path.exists(sslConf):
|
||||
os.system('rm -f ' + sslConf)
|
||||
# panel_tpl = mw.getRunDir() + "/data/tpl/nginx_panel.conf"
|
||||
# dst_panel_path = mw.getServerDir() + "/web_conf/nginx/vhost/panel.conf"
|
||||
# if os.path.exists(sslConf):
|
||||
# os.system('rm -f ' + sslConf)
|
||||
|
||||
conf = mw.readFile(dst_panel_path)
|
||||
if conf:
|
||||
rep = "\\s+ssl_certificate\\s+.+;\\s+ssl_certificate_key\\s+.+;"
|
||||
conf = re.sub(rep, '', conf)
|
||||
rep = "\\s+ssl_protocols\\s+.+;\n"
|
||||
conf = re.sub(rep, '', conf)
|
||||
rep = "\\s+ssl_ciphers\\s+.+;\n"
|
||||
conf = re.sub(rep, '', conf)
|
||||
rep = "\\s+ssl_prefer_server_ciphers\\s+.+;\n"
|
||||
conf = re.sub(rep, '', conf)
|
||||
rep = "\\s+ssl_session_cache\\s+.+;\n"
|
||||
conf = re.sub(rep, '', conf)
|
||||
rep = "\\s+ssl_session_timeout\\s+.+;\n"
|
||||
conf = re.sub(rep, '', conf)
|
||||
rep = "\\s+ssl_ecdh_curve\\s+.+;\n"
|
||||
conf = re.sub(rep, '', conf)
|
||||
rep = "\\s+ssl_session_tickets\\s+.+;\n"
|
||||
conf = re.sub(rep, '', conf)
|
||||
rep = "\\s+ssl_stapling\\s+.+;\n"
|
||||
conf = re.sub(rep, '', conf)
|
||||
rep = "\\s+ssl_stapling_verify\\s+.+;\n"
|
||||
conf = re.sub(rep, '', conf)
|
||||
rep = "\\s+ssl\\s+on;"
|
||||
conf = re.sub(rep, '', conf)
|
||||
rep = "\\s+error_page\\s497.+;"
|
||||
conf = re.sub(rep, '', conf)
|
||||
rep = "\\s+if.+server_port.+\n.+\n\\s+\\s*}"
|
||||
conf = re.sub(rep, '', conf)
|
||||
rep = "\\s+listen\\s+443.*;"
|
||||
conf = re.sub(rep, '', conf)
|
||||
rep = "\\s+listen\\s+\\[\\:\\:\\]\\:443.*;"
|
||||
conf = re.sub(rep, '', conf)
|
||||
mw.writeFile(dst_panel_path, conf)
|
||||
# conf = mw.readFile(dst_panel_path)
|
||||
# if conf:
|
||||
# rep = "\\s+ssl_certificate\\s+.+;\\s+ssl_certificate_key\\s+.+;"
|
||||
# conf = re.sub(rep, '', conf)
|
||||
# rep = "\\s+ssl_protocols\\s+.+;\n"
|
||||
# conf = re.sub(rep, '', conf)
|
||||
# rep = "\\s+ssl_ciphers\\s+.+;\n"
|
||||
# conf = re.sub(rep, '', conf)
|
||||
# rep = "\\s+ssl_prefer_server_ciphers\\s+.+;\n"
|
||||
# conf = re.sub(rep, '', conf)
|
||||
# rep = "\\s+ssl_session_cache\\s+.+;\n"
|
||||
# conf = re.sub(rep, '', conf)
|
||||
# rep = "\\s+ssl_session_timeout\\s+.+;\n"
|
||||
# conf = re.sub(rep, '', conf)
|
||||
# rep = "\\s+ssl_ecdh_curve\\s+.+;\n"
|
||||
# conf = re.sub(rep, '', conf)
|
||||
# rep = "\\s+ssl_session_tickets\\s+.+;\n"
|
||||
# conf = re.sub(rep, '', conf)
|
||||
# rep = "\\s+ssl_stapling\\s+.+;\n"
|
||||
# conf = re.sub(rep, '', conf)
|
||||
# rep = "\\s+ssl_stapling_verify\\s+.+;\n"
|
||||
# conf = re.sub(rep, '', conf)
|
||||
# rep = "\\s+ssl\\s+on;"
|
||||
# conf = re.sub(rep, '', conf)
|
||||
# rep = "\\s+error_page\\s497.+;"
|
||||
# conf = re.sub(rep, '', conf)
|
||||
# rep = "\\s+if.+server_port.+\n.+\n\\s+\\s*}"
|
||||
# conf = re.sub(rep, '', conf)
|
||||
# rep = "\\s+listen\\s+443.*;"
|
||||
# conf = re.sub(rep, '', conf)
|
||||
# rep = "\\s+listen\\s+\\[\\:\\:\\]\\:443.*;"
|
||||
# conf = re.sub(rep, '', conf)
|
||||
# mw.writeFile(dst_panel_path, conf)
|
||||
|
||||
mw.writeLog('面板配置', '面板SSL关闭成功!')
|
||||
mw.restartWeb()
|
||||
return mw.returnJson(True, 'SSL已关闭,请使用http协议访问面板!')
|
||||
else:
|
||||
try:
|
||||
mw.writeFile(sslConf, 'True')
|
||||
# mw.writeLog('面板配置', '面板SSL关闭成功!')
|
||||
# mw.restartWeb()
|
||||
# return mw.returnJson(True, 'SSL已关闭,请使用http协议访问面板!')
|
||||
# else:
|
||||
# try:
|
||||
# mw.writeFile(sslConf, 'True')
|
||||
|
||||
keyPath = mw.getRunDir() + '/ssl/private.pem'
|
||||
certPath = mw.getRunDir() + '/ssl/cert.pem'
|
||||
# keyPath = mw.getRunDir() + '/ssl/private.pem'
|
||||
# certPath = mw.getRunDir() + '/ssl/cert.pem'
|
||||
|
||||
conf = mw.readFile(dst_panel_path)
|
||||
if conf:
|
||||
if conf.find('ssl_certificate') == -1:
|
||||
sslStr = """#error_page 404/404.html;
|
||||
ssl_certificate %s;
|
||||
ssl_certificate_key %s;
|
||||
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
|
||||
ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:HIGH:!aNULL:!MD5:!RC4:!DHE;
|
||||
ssl_prefer_server_ciphers on;
|
||||
ssl_session_cache shared:SSL:10m;
|
||||
ssl_session_timeout 10m;
|
||||
error_page 497 https://$host$request_uri;""" % (certPath, keyPath)
|
||||
if(conf.find('ssl_certificate') != -1):
|
||||
return mw.returnJson(True, 'SSL开启成功!')
|
||||
# conf = mw.readFile(dst_panel_path)
|
||||
# if conf:
|
||||
# if conf.find('ssl_certificate') == -1:
|
||||
# sslStr = """#error_page 404/404.html;
|
||||
# ssl_certificate %s;
|
||||
# ssl_certificate_key %s;
|
||||
# ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
|
||||
# ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:HIGH:!aNULL:!MD5:!RC4:!DHE;
|
||||
# ssl_prefer_server_ciphers on;
|
||||
# ssl_session_cache shared:SSL:10m;
|
||||
# ssl_session_timeout 10m;
|
||||
# error_page 497 https://$host$request_uri;""" % (certPath, keyPath)
|
||||
# if(conf.find('ssl_certificate') != -1):
|
||||
# return mw.returnJson(True, 'SSL开启成功!')
|
||||
|
||||
conf = conf.replace('#error_page 404/404.html;', sslStr)
|
||||
# conf = conf.replace('#error_page 404/404.html;', sslStr)
|
||||
|
||||
rep = "listen\\s+([0-9]+)\\s*[default_server]*;"
|
||||
tmp = re.findall(rep, conf)
|
||||
if not mw.inArray(tmp, '443'):
|
||||
listen = re.search(rep, conf).group()
|
||||
http_ssl = "\n\tlisten 443 ssl http2;"
|
||||
http_ssl = http_ssl + "\n\tlisten [::]:443 ssl http2;"
|
||||
conf = conf.replace(listen, listen + http_ssl)
|
||||
# rep = "listen\\s+([0-9]+)\\s*[default_server]*;"
|
||||
# tmp = re.findall(rep, conf)
|
||||
# if not mw.inArray(tmp, '443'):
|
||||
# listen = re.search(rep, conf).group()
|
||||
# http_ssl = "\n\tlisten 443 ssl http2;"
|
||||
# http_ssl = http_ssl + "\n\tlisten [::]:443 ssl http2;"
|
||||
# conf = conf.replace(listen, listen + http_ssl)
|
||||
|
||||
mw.backFile(dst_panel_path)
|
||||
mw.writeFile(dst_panel_path, conf)
|
||||
isError = mw.checkWebConfig()
|
||||
if(isError != True):
|
||||
mw.restoreFile(dst_panel_path)
|
||||
return mw.returnJson(False, '证书错误: <br><a style="color:red;">' + isError.replace("\n", '<br>') + '</a>')
|
||||
except Exception as ex:
|
||||
return mw.returnJson(False, '开启失败:' + str(ex))
|
||||
mw.restartWeb()
|
||||
return mw.returnJson(True, '开启成功,请使用https协议访问面板!')
|
||||
# mw.backFile(dst_panel_path)
|
||||
# mw.writeFile(dst_panel_path, conf)
|
||||
# isError = mw.checkWebConfig()
|
||||
# if(isError != True):
|
||||
# mw.restoreFile(dst_panel_path)
|
||||
# return mw.returnJson(False, '证书错误: <br><a style="color:red;">' + isError.replace("\n", '<br>') + '</a>')
|
||||
# except Exception as ex:
|
||||
# return mw.returnJson(False, '开启失败:' + str(ex))
|
||||
# mw.restartWeb()
|
||||
# return mw.returnJson(True, '开启成功,请使用https协议访问面板!')
|
||||
|
||||
def getApi(self):
|
||||
data = {}
|
||||
@@ -958,7 +1054,7 @@ class config_api:
|
||||
mw.writeFile(auth, crypt_data)
|
||||
|
||||
ip = mw.getHostAddr()
|
||||
url = pyotp.totp.TOTP(sec).provisioning_uri(name=ip, issuer_name='mdserver-web')
|
||||
url = pyotp.totp.TOTP(sec).provisioning_uri(name=ip, issuer_name=tag)
|
||||
|
||||
rdata = {}
|
||||
rdata['secret'] = sec
|
||||
|
||||
@@ -42,8 +42,13 @@ class Sql():
|
||||
self.__DB_CONN = sqlite3.connect(self.__DB_FILE)
|
||||
self.__DB_CONN.text_factory = str
|
||||
except Exception as ex:
|
||||
# print(mw.getTracebackInfo())
|
||||
return "error: " + str(ex)
|
||||
|
||||
def changeTextFactoryToBytes(self):
|
||||
self.__DB_CONN.text_factory = bytes
|
||||
return True
|
||||
|
||||
def autoTextFactory(self):
|
||||
if sys.version_info[0] == 3:
|
||||
self.__DB_CONN.text_factory = lambda x: str(
|
||||
|
||||
@@ -347,6 +347,9 @@ def restartMw():
|
||||
import system_api
|
||||
system_api.system_api().restartMw()
|
||||
|
||||
def restartNginx(self):
|
||||
writeFile('data/restart_nginx.pl', 'True')
|
||||
return True
|
||||
|
||||
def checkWebConfig():
|
||||
op_dir = getServerDir() + '/openresty/nginx'
|
||||
@@ -1288,25 +1291,46 @@ def checkDomainPanel():
|
||||
domain = readFile('data/bind_domain.pl')
|
||||
port = readFile('data/port.pl').strip()
|
||||
|
||||
npid = getServerDir() + "/openresty/nginx/logs/nginx.pid"
|
||||
if not os.path.exists(npid):
|
||||
scheme = 'http'
|
||||
|
||||
choose_file = getRunDir()+'/ssl/choose.pl'
|
||||
if os.path.exists(choose_file):
|
||||
choose = readFile(choose_file).strip()
|
||||
if not inArray(['local','nginx'], choose):
|
||||
return False
|
||||
else:
|
||||
return False
|
||||
|
||||
nconf = getServerDir() + "/web_conf/nginx/vhost/panel.conf"
|
||||
if os.path.exists(nconf):
|
||||
port = "80"
|
||||
local_ssl = getRunDir()+'/ssl/local'
|
||||
if choose == 'local':
|
||||
scheme = 'https'
|
||||
|
||||
if domain:
|
||||
client_ip = getClientIp()
|
||||
if client_ip in ['127.0.0.1', 'localhost', '::1']:
|
||||
if choose == 'nginx':
|
||||
# print(port)
|
||||
npid = getServerDir() + "/openresty/nginx/logs/nginx.pid"
|
||||
if not os.path.exists(npid):
|
||||
return False
|
||||
if tmp.strip().lower() != domain.strip().lower():
|
||||
from flask import Flask, redirect, request, url_for
|
||||
to = "http://" + domain + ":" + str(port)
|
||||
return redirect(to, code=302)
|
||||
|
||||
nconf = getServerDir() + "/web_conf/nginx/vhost/panel.conf"
|
||||
if os.path.exists(nconf):
|
||||
port = "80"
|
||||
if not domain:
|
||||
return False
|
||||
|
||||
client_ip = getClientIp()
|
||||
if client_ip in ['127.0.0.1', 'localhost', '::1']:
|
||||
return False
|
||||
|
||||
if tmp.strip().lower() != domain.strip().lower():
|
||||
from flask import Flask, redirect, request, url_for
|
||||
to = scheme + "://" + domain + ":" + str(port)
|
||||
# print(to)
|
||||
return redirect(to, code=302)
|
||||
|
||||
return False
|
||||
|
||||
|
||||
|
||||
def createLinuxUser(user, group):
|
||||
execShell("groupadd {}".format(group))
|
||||
execShell('useradd -s /sbin/nologin -g {} {}'.format(user, group))
|
||||
@@ -1811,28 +1835,38 @@ def getCertName(certPath):
|
||||
return None
|
||||
|
||||
|
||||
def createSSL():
|
||||
def createLocalSSL():
|
||||
if not os.path.exists('ssl/local'):
|
||||
execShell('mkdir -p ssl/local')
|
||||
|
||||
|
||||
# 自签证书
|
||||
if os.path.exists('ssl/input.pl'):
|
||||
return True
|
||||
# if os.path.exists('ssl/local/input.pl'):
|
||||
# return True
|
||||
|
||||
client_ip = getClientIp()
|
||||
|
||||
import OpenSSL
|
||||
key = OpenSSL.crypto.PKey()
|
||||
key.generate_key(OpenSSL.crypto.TYPE_RSA, 2048)
|
||||
cert = OpenSSL.crypto.X509()
|
||||
cert.set_serial_number(0)
|
||||
cert.get_subject().CN = getLocalIp()
|
||||
|
||||
if client_ip == '127.0.0.1':
|
||||
cert.get_subject().CN = '127.0.0.1'
|
||||
else:
|
||||
cert.get_subject().CN = getLocalIp()
|
||||
|
||||
cert.set_issuer(cert.get_subject())
|
||||
cert.gmtime_adj_notBefore(0)
|
||||
cert.gmtime_adj_notAfter(86400 * 3650)
|
||||
cert.set_pubkey(key)
|
||||
cert.sign(key, 'md5')
|
||||
cert_ca = OpenSSL.crypto.dump_certificate(
|
||||
OpenSSL.crypto.FILETYPE_PEM, cert)
|
||||
private_key = OpenSSL.crypto.dump_privatekey(
|
||||
OpenSSL.crypto.FILETYPE_PEM, key)
|
||||
cert_ca = OpenSSL.crypto.dump_certificate(OpenSSL.crypto.FILETYPE_PEM, cert)
|
||||
private_key = OpenSSL.crypto.dump_privatekey(OpenSSL.crypto.FILETYPE_PEM, key)
|
||||
if len(cert_ca) > 100 and len(private_key) > 100:
|
||||
writeFile('ssl/cert.pem', cert_ca, 'wb+')
|
||||
writeFile('ssl/private.pem', private_key, 'wb+')
|
||||
writeFile('ssl/local/cert.pem', cert_ca, 'wb+')
|
||||
writeFile('ssl/local/private.pem', private_key, 'wb+')
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
@@ -64,6 +64,11 @@ mw_start_debug2(){
|
||||
gunicorn -b :7200 -k geventwebsocket.gunicorn.workers.GeventWebSocketWorker -w 1 app:app
|
||||
}
|
||||
|
||||
mw_start_debug3(){
|
||||
gunicorn -c setting.py app:app
|
||||
python3 task.py
|
||||
}
|
||||
|
||||
|
||||
mw_stop()
|
||||
{
|
||||
@@ -96,4 +101,8 @@ case "$1" in
|
||||
mw_stop
|
||||
mw_start_debug2
|
||||
;;
|
||||
'debug3')
|
||||
mw_stop
|
||||
mw_start_debug3
|
||||
;;
|
||||
esac
|
||||
@@ -1948,7 +1948,7 @@ def addMasterRepSlaveUser(version=''):
|
||||
if isError != None:
|
||||
return isError
|
||||
|
||||
sql_select = "grant select,lock tables,PROCESS on *.* to " + username + "@'%';"
|
||||
sql_select = "grant select,reload,REPLICATION CLIENT,PROCESS on *.* to " + username + "@'%';"
|
||||
pdb.execute(sql_select)
|
||||
pdb.execute('FLUSH PRIVILEGES;')
|
||||
|
||||
@@ -2583,6 +2583,293 @@ def dumpMysqlData(version=''):
|
||||
return 'ok'
|
||||
return 'fail'
|
||||
|
||||
############### --- 重要 数据补足同步 ---- ###########
|
||||
|
||||
def getSyncMysqlDB(dbname,sign = ''):
|
||||
conn = pSqliteDb('slave_sync_user')
|
||||
if sign != '':
|
||||
data = conn.field('ip,port,user,pass,mode,cmd').where('ip=?', (sign,)).find()
|
||||
else:
|
||||
data = conn.field('ip,port,user,pass,mode,cmd').find()
|
||||
user = data['user']
|
||||
apass = data['pass']
|
||||
port = data['port']
|
||||
ip = data['ip']
|
||||
# 远程数据
|
||||
sync_db = mw.getMyORM()
|
||||
# MySQLdb |
|
||||
sync_db.setPort(port)
|
||||
sync_db.setHost(ip)
|
||||
sync_db.setUser(user)
|
||||
sync_db.setPwd(apass)
|
||||
sync_db.setDbName(dbname)
|
||||
sync_db.setTimeout(60)
|
||||
return sync_db
|
||||
|
||||
def syncDatabaseRepairTempFile():
|
||||
tmp_log = mw.getMWLogs()+ '/mariadb-check.log'
|
||||
return tmp_log
|
||||
|
||||
def syncDatabaseRepairLog(version=''):
|
||||
import subprocess
|
||||
args = getArgs()
|
||||
data = checkArgs(args, ['db','sign','op'])
|
||||
if not data[0]:
|
||||
return data[1]
|
||||
|
||||
sync_args_db = args['db']
|
||||
sync_args_sign = args['sign']
|
||||
op = args['op']
|
||||
tmp_log = syncDatabaseRepairTempFile()
|
||||
cmd = 'cd '+mw.getServerDir()+'/mdserver-web && source bin/activate && python3 plugins/mariadb/index.py sync_database_repair {"db":"'+sync_args_db+'","sign":"'+sync_args_sign+'"}'
|
||||
# print(cmd)
|
||||
|
||||
if op == 'get':
|
||||
log = mw.getLastLine(tmp_log, 15)
|
||||
return mw.returnJson(True, log)
|
||||
|
||||
if op == 'cmd':
|
||||
return mw.returnJson(True, 'ok', cmd)
|
||||
|
||||
if op == 'do':
|
||||
os.system(' echo "开始执行" > '+ tmp_log)
|
||||
os.system(cmd +' >> '+ tmp_log +' &')
|
||||
# time.sleep(10)
|
||||
# mw.execShell('rm -rf '+tmp_log)
|
||||
return mw.returnJson(True, 'ok')
|
||||
|
||||
return mw.returnJson(False, '无效请求!')
|
||||
|
||||
|
||||
def syncDatabaseRepair(version=''):
|
||||
time_stats_s = time.time()
|
||||
tmp_log = syncDatabaseRepairTempFile()
|
||||
|
||||
from pymysql.converters import escape_string
|
||||
args = getArgs()
|
||||
data = checkArgs(args, ['db','sign'])
|
||||
if not data[0]:
|
||||
return data[1]
|
||||
|
||||
sync_args_db = args['db']
|
||||
sync_args_sign = args['sign']
|
||||
|
||||
# 本地数据
|
||||
local_db = pMysqlDb()
|
||||
# 远程数据
|
||||
sync_db = getSyncMysqlDB(sync_args_db,sync_args_sign)
|
||||
|
||||
tables = local_db.query('show tables from `%s`' % sync_args_db)
|
||||
table_key = "Tables_in_" + sync_args_db
|
||||
inconsistent_table = []
|
||||
|
||||
tmp_dir = '/tmp/sync_db_repair'
|
||||
mw.execShell('mkdir -p '+tmp_dir)
|
||||
|
||||
for tb in tables:
|
||||
|
||||
table_name = sync_args_db+'.'+tb[table_key]
|
||||
table_check_file = tmp_dir+'/'+table_name+'.txt'
|
||||
|
||||
if os.path.exists(table_check_file):
|
||||
# print(table_name+', 已检查OK')
|
||||
continue
|
||||
|
||||
primary_key_sql = "SHOW INDEX FROM "+table_name+" WHERE Key_name = 'PRIMARY';";
|
||||
primary_key_data = local_db.query(primary_key_sql)
|
||||
# print(primary_key_sql,primary_key_data)
|
||||
pkey_name = '*'
|
||||
if len(primary_key_data) == 1:
|
||||
pkey_name = primary_key_data[0]['Column_name']
|
||||
# print(pkey_name)
|
||||
if pkey_name != '*' :
|
||||
# 智能校验(由于服务器同步可能会慢,比较总数总是对不上)
|
||||
cmd_local_newpk_sql = 'select ' + pkey_name + ' from ' + table_name + " order by " + pkey_name + " desc limit 1"
|
||||
cmd_local_newpk_data = local_db.query(cmd_local_newpk_sql)
|
||||
# print(cmd_local_newpk_data)
|
||||
if len(cmd_local_newpk_data) == 1:
|
||||
# 比较总数
|
||||
cmd_count_sql = 'select count('+pkey_name+') as num from '+table_name + ' where '+pkey_name + ' <= '+ str(cmd_local_newpk_data[0][pkey_name])
|
||||
local_count_data = local_db.query(cmd_count_sql)
|
||||
sync_count_data = sync_db.query(cmd_count_sql)
|
||||
|
||||
if local_count_data != sync_count_data:
|
||||
print(cmd_count_sql)
|
||||
print("all data compare: ",local_count_data, sync_count_data)
|
||||
else:
|
||||
print(table_name+' smart compare check ok.')
|
||||
mw.writeFile(tmp_log, table_name+' smart compare check ok.\n','a+')
|
||||
mw.execShell("echo 'ok' > "+table_check_file)
|
||||
continue
|
||||
|
||||
|
||||
|
||||
# 比较总数
|
||||
cmd_count_sql = 'select count('+pkey_name+') as num from '+table_name
|
||||
local_count_data = local_db.query(cmd_count_sql)
|
||||
sync_count_data = sync_db.query(cmd_count_sql)
|
||||
|
||||
if local_count_data != sync_count_data:
|
||||
print("all data compare: ",local_count_data, sync_count_data)
|
||||
inconsistent_table.append(table_name)
|
||||
diff = sync_count_data[0]['num'] - local_count_data[0]['num']
|
||||
print(table_name+', need sync. diff,'+str(diff))
|
||||
mw.writeFile(tmp_log, table_name+', need sync. diff,'+str(diff)+'\n','a+')
|
||||
else:
|
||||
print(table_name+' check ok.')
|
||||
mw.writeFile(tmp_log, table_name+' check ok.\n','a+')
|
||||
mw.execShell("echo 'ok' > "+table_check_file)
|
||||
|
||||
|
||||
# inconsistent_table = ['xx.xx']
|
||||
# 数据对齐
|
||||
for table_name in inconsistent_table:
|
||||
is_break = False
|
||||
while not is_break:
|
||||
local_db.ping()
|
||||
# 远程数据
|
||||
sync_db.ping()
|
||||
|
||||
print("check table:"+table_name)
|
||||
mw.writeFile(tmp_log, "check table:"+table_name+'\n','a+')
|
||||
table_name_pos = 0
|
||||
table_name_pos_file = tmp_dir+'/'+table_name+'.pos.txt'
|
||||
primary_key_sql = "SHOW INDEX FROM "+table_name+" WHERE Key_name = 'PRIMARY';";
|
||||
primary_key_data = local_db.query(primary_key_sql)
|
||||
pkey_name = primary_key_data[0]['Column_name']
|
||||
|
||||
if os.path.exists(table_name_pos_file):
|
||||
table_name_pos = mw.readFile(table_name_pos_file)
|
||||
|
||||
|
||||
data_select_sql = 'select * from '+table_name + ' where '+pkey_name+' > '+str(table_name_pos)+' limit 10000'
|
||||
print(data_select_sql)
|
||||
local_select_data = local_db.query(data_select_sql)
|
||||
|
||||
time_s = time.time()
|
||||
sync_select_data = sync_db.query(data_select_sql)
|
||||
print(f'sync query cos:{time.time() - time_s:.4f}s')
|
||||
mw.writeFile(tmp_log, f'sync query cos:{time.time() - time_s:.4f}s\n','a+')
|
||||
|
||||
# print(local_select_data)
|
||||
# print(sync_select_data)
|
||||
|
||||
# print(len(local_select_data))
|
||||
# print(len(sync_select_data))
|
||||
print('pos:',str(table_name_pos),'local compare sync,',local_select_data == sync_select_data)
|
||||
|
||||
|
||||
cmd_count_sql = 'select count('+pkey_name+') as num from '+table_name
|
||||
local_count_data = local_db.query(cmd_count_sql)
|
||||
time_s = time.time()
|
||||
sync_count_data = sync_db.query(cmd_count_sql)
|
||||
print(f'sync count data cos:{time.time() - time_s:.4f}s')
|
||||
print(local_count_data,sync_count_data)
|
||||
# 数据同步有延迟,相等即任务数据补足完成
|
||||
if local_count_data[0]['num'] == sync_count_data[0]['num']:
|
||||
is_break = True
|
||||
break
|
||||
|
||||
diff = sync_count_data[0]['num'] - local_count_data[0]['num']
|
||||
print("diff," + str(diff)+' line data!')
|
||||
|
||||
if local_select_data == sync_select_data:
|
||||
data_count = len(local_select_data)
|
||||
if data_count == 0:
|
||||
# mw.writeFile(table_name_pos_file, '0')
|
||||
print(table_name+",data is equal ok..")
|
||||
is_break = True
|
||||
break
|
||||
|
||||
# print(table_name,data_count)
|
||||
pos = local_select_data[data_count-1][pkey_name]
|
||||
print('pos',pos)
|
||||
progress = pos/sync_count_data[0]['num']
|
||||
print('progress,%.2f' % progress+'%')
|
||||
mw.writeFile(table_name_pos_file, str(pos))
|
||||
else:
|
||||
sync_select_data_len = len(sync_select_data)
|
||||
skip_idx = 0
|
||||
# 主库PK -> 查询本地 | 保证一致
|
||||
if sync_select_data_len > 0:
|
||||
for idx in range(sync_select_data_len):
|
||||
sync_idx_data = sync_select_data[idx]
|
||||
local_idx_data = None
|
||||
if idx in local_select_data:
|
||||
local_idx_data = local_select_data[idx]
|
||||
if sync_select_data[idx] == local_idx_data:
|
||||
skip_idx = idx
|
||||
pos = local_select_data[idx][pkey_name]
|
||||
mw.writeFile(table_name_pos_file, str(pos))
|
||||
|
||||
# print(insert_data)
|
||||
local_inquery_sql = 'select * from ' + table_name+ ' where ' +pkey_name+' = '+ str(sync_idx_data[pkey_name])
|
||||
# print(local_inquery_sql)
|
||||
ldata = local_db.query(local_inquery_sql)
|
||||
# print('ldata:',ldata)
|
||||
if len(ldata) == 0:
|
||||
print("id:"+ str(sync_idx_data[pkey_name])+ " no exists, insert")
|
||||
insert_sql = 'insert into ' + table_name
|
||||
field_str = ''
|
||||
value_str = ''
|
||||
for field in sync_idx_data:
|
||||
field_str += '`'+field+'`,'
|
||||
value_str += '\''+escape_string(str(sync_idx_data[field]))+'\','
|
||||
field_str = '(' +field_str.strip(',')+')'
|
||||
value_str = '(' +value_str.strip(',')+')'
|
||||
insert_sql = insert_sql+' '+field_str+' values'+value_str+';'
|
||||
print(insert_sql)
|
||||
r = local_db.execute(insert_sql)
|
||||
print(r)
|
||||
else:
|
||||
# print('compare sync->local:',sync_idx_data == ldata[0] )
|
||||
if ldata[0] == sync_idx_data:
|
||||
continue
|
||||
|
||||
print("id:"+ str(sync_idx_data[pkey_name])+ " data is not equal, update")
|
||||
update_sql = 'update ' + table_name
|
||||
field_str = ''
|
||||
value_str = ''
|
||||
for field in sync_idx_data:
|
||||
if field == pkey_name:
|
||||
continue
|
||||
field_str += '`'+field+'`=\''+escape_string(str(sync_idx_data[field]))+'\','
|
||||
field_str = field_str.strip(',')
|
||||
update_sql = update_sql+' set '+field_str+' where '+pkey_name+'=\''+str(sync_idx_data[pkey_name])+'\';'
|
||||
print(update_sql)
|
||||
r = local_db.execute(update_sql)
|
||||
print(r)
|
||||
|
||||
# 本地PK -> 查询主库 | 保证一致
|
||||
# local_select_data_len = len(local_select_data)
|
||||
# if local_select_data_len > 0:
|
||||
# for idx in range(local_select_data_len):
|
||||
# if idx < skip_idx:
|
||||
# continue
|
||||
# local_idx_data = local_select_data[idx]
|
||||
# print('local idx check', idx, skip_idx)
|
||||
# local_inquery_sql = 'select * from ' + table_name+ ' where ' +pkey_name+' = '+ str(local_idx_data[pkey_name])
|
||||
# print(local_inquery_sql)
|
||||
# sdata = sync_db.query(local_inquery_sql)
|
||||
# sdata_len = len(sdata)
|
||||
# print('sdata:',sdata,sdata_len)
|
||||
# if sdata_len == 0:
|
||||
# delete_sql = 'delete from ' + table_name + ' where ' +pkey_name+' = '+ str(local_idx_data[pkey_name])
|
||||
# print(delete_sql)
|
||||
# r = local_db.execute(delete_sql)
|
||||
# print(r)
|
||||
# break
|
||||
|
||||
|
||||
if is_break:
|
||||
print("break all")
|
||||
break
|
||||
time.sleep(3)
|
||||
print(f'data check cos:{time.time() - time_stats_s:.4f}s')
|
||||
print("data supplementation completed")
|
||||
mw.execShell('rm -rf '+tmp_dir)
|
||||
return 'ok'
|
||||
|
||||
|
||||
############### --- 重要 同步---- ###########
|
||||
|
||||
@@ -2647,11 +2934,18 @@ def doFullSync(version=''):
|
||||
|
||||
|
||||
def doFullSyncUser(version=''):
|
||||
which_pv = mw.execShell('which pv')
|
||||
is_exist_pv = False
|
||||
if not os.path.exists(which_pv[0]):
|
||||
is_exist_pv = True
|
||||
|
||||
|
||||
args = getArgs()
|
||||
data = checkArgs(args, ['db', 'sign'])
|
||||
if not data[0]:
|
||||
return data[1]
|
||||
|
||||
time_all_s = time.time()
|
||||
sync_db = args['db']
|
||||
sync_sign = args['sign']
|
||||
|
||||
@@ -2675,23 +2969,67 @@ def doFullSyncUser(version=''):
|
||||
os.system("rm -rf " + bak_file)
|
||||
|
||||
writeDbSyncStatus({'code': 0, 'msg': '开始同步...', 'progress': 0})
|
||||
|
||||
dmp_option = ''
|
||||
mode = recognizeDbMode()
|
||||
# if mode == 'gtid':
|
||||
# dmp_option = ' --set-gtid-purged=off '
|
||||
|
||||
writeDbSyncStatus({'code': 1, 'msg': '远程导出数据...', 'progress': 10})
|
||||
|
||||
find_run_dump = mw.execShell('ps -ef | grep mariadb-dump | grep -v grep')
|
||||
if find_run_dump[0] != "":
|
||||
print("正在远程导出数据中,别着急...")
|
||||
writeDbSyncStatus({'code': 3.1, 'msg': '正在远程导出数据中,别着急...', 'progress': 39})
|
||||
return False
|
||||
|
||||
time_s = time.time()
|
||||
if not os.path.exists(bak_file):
|
||||
dmp_option += " --master-data=1 --apply-slave-statements --include-master-host-port "
|
||||
|
||||
# https://mariadb.com/kb/zh-cn/mariadb-dump/
|
||||
dump_sql_data = getServerDir() + "/bin/mariadb-dump -f --opt --default-character-set=utf8 --single-transaction -h" + ip + " -P" + \
|
||||
dump_sql_data = getServerDir() + "/bin/mariadb-dump " + dmp_option + " -f --default-character-set=utf8 --single-transaction --compress -q -h" + ip + " -P" + \
|
||||
port + " -u" + user + " -p'" + apass + "' " + sync_db + ">" + bak_file
|
||||
print(dump_sql_data)
|
||||
mw.execShell(dump_sql_data)
|
||||
|
||||
writeDbSyncStatus({'code': 2, 'msg': '本地导入数据...', 'progress': 40})
|
||||
time_e = time.time()
|
||||
export_cos = time_e - time_s
|
||||
print("export cos:", export_cos)
|
||||
writeDbSyncStatus({'code': 3, 'msg': '导出耗时:'+str(int(export_cos))+'秒,正在到本地导入数据中...', 'progress': 40})
|
||||
|
||||
|
||||
find_run_import = mw.execShell('ps -ef | grep mariadb| grep '+ bak_file +' | grep -v grep')
|
||||
if find_run_import[0] != "":
|
||||
print("正在导入数据中,别着急...")
|
||||
writeDbSyncStatus({'code': 4.1, 'msg': '正在导入数据中,别着急...', 'progress': 59})
|
||||
return False
|
||||
|
||||
# if os.path.exists(bak_file):
|
||||
# db.execute('reset master')
|
||||
|
||||
time_s = time.time()
|
||||
if os.path.exists(bak_file):
|
||||
pwd = pSqliteDb('config').where('id=?', (1,)).getField('mysql_root')
|
||||
sock = getSocketFile()
|
||||
my_import_cmd = getServerDir() + '/bin/mariadb -S ' + sock + " -uroot -p'" + pwd + \
|
||||
"' " + sync_db + '<' + bak_file
|
||||
print(my_import_cmd)
|
||||
mw.execShell(my_import_cmd)
|
||||
|
||||
if is_exist_pv:
|
||||
my_import_cmd = getServerDir() + '/bin/mariadb -S ' + sock + " -uroot -p'" + pwd + "' " + sync_db
|
||||
my_import_cmd = "pv -t -p " + bak_file + '|' + my_import_cmd
|
||||
print(my_import_cmd)
|
||||
os.system(my_import_cmd)
|
||||
else:
|
||||
my_import_cmd = getServerDir() + '/bin/mariadb -S ' + sock + " -uroot -p'" + pwd + \
|
||||
"' " + sync_db + '<' + bak_file
|
||||
print(my_import_cmd)
|
||||
mw.execShell(my_import_cmd)
|
||||
|
||||
time_e = time.time()
|
||||
import_cos = time_e - time_s
|
||||
print("import cos:", import_cos)
|
||||
writeDbSyncStatus({'code': 4, 'msg': '导入耗时:'+str(int(import_cos))+'秒', 'progress': 60})
|
||||
|
||||
time.sleep(3)
|
||||
|
||||
pinfo = parseSlaveSyncCmd(data['cmd'])
|
||||
# print(pinfo)
|
||||
@@ -3026,5 +3364,9 @@ if __name__ == "__main__":
|
||||
print(doFullSync(version))
|
||||
elif func == 'dump_mysql_data':
|
||||
print(dumpMysqlData(version))
|
||||
elif func == 'sync_database_repair':
|
||||
print(syncDatabaseRepair())
|
||||
elif func == 'sync_database_repair_log':
|
||||
print(syncDatabaseRepairLog())
|
||||
else:
|
||||
print('error')
|
||||
|
||||
@@ -7,6 +7,12 @@ rootPath=$(dirname "$curPath")
|
||||
rootPath=$(dirname "$rootPath")
|
||||
serverPath=$(dirname "$rootPath")
|
||||
|
||||
# cd /www/server/mdserver-web/plugins/mariadb && bash install.sh install 8.2
|
||||
# cd /www/server/mdserver-web && source bin/activate && python3 plugins/mariadb/index.py try_slave_sync_bugfix {}
|
||||
# cd /www/server/mdserver-web && source bin/activate && python3 plugins/mariadb/index.py do_full_sync {"db":"xxx","sign":"","begin":1}
|
||||
# cd /www/server/mdserver-web && source bin/activate && python3 plugins/mariadb/index.py sync_database_repair {"db":"xxx","sign":""}
|
||||
# cd /www/server/mdserver-web && source bin/activate && python3 plugins/mariadb/index.py init_slave_status
|
||||
# cd /www/server/mdserver-web && source bin/activate && python3 plugins/mariadb/index.py install_pre_inspection
|
||||
|
||||
install_tmp=${rootPath}/tmp/mw_install.pl
|
||||
|
||||
|
||||
@@ -874,7 +874,7 @@ function openPhpmyadmin(name,username,password){
|
||||
}
|
||||
|
||||
if (rdata.data['cfg']['choose'] != 'mariadb'){
|
||||
layer.msg('当前为['+rdata.choose+']模式,若要使用请修改phpMyAdmin访问切换.',{icon:2,shade: [0.3, '#000']});
|
||||
layer.msg('当前为['+rdata.data['cfg']['choose']+']模式,若要使用请修改phpMyAdmin访问切换.',{icon:2,shade: [0.3, '#000']});
|
||||
return;
|
||||
}
|
||||
var home_page = rdata.data['home_page'];
|
||||
@@ -1978,6 +1978,84 @@ function getFullSyncStatus(db){
|
||||
|
||||
}
|
||||
|
||||
function dataSyncVerify(db){
|
||||
var reqTimer = null;
|
||||
|
||||
function requestLogs(layerIndex){
|
||||
myPostN('sync_database_repair_log', {db:db, sign:'',op:'get'}, function(rdata){
|
||||
var rdata = $.parseJSON(rdata.data);
|
||||
|
||||
if(!rdata.status) {
|
||||
layer.close(layerIndex);
|
||||
layer.msg(rdata.msg,{icon:2, time:2000});
|
||||
clearInterval(reqTimer);
|
||||
return;
|
||||
};
|
||||
|
||||
if (rdata.msg == ''){
|
||||
rdata.msg = '暂无数据!';
|
||||
}
|
||||
|
||||
$("#data_verify_log").html(rdata.msg);
|
||||
//滚动到最低
|
||||
var ob = document.getElementById('data_verify_log');
|
||||
ob.scrollTop = ob.scrollHeight;
|
||||
});
|
||||
}
|
||||
|
||||
layer.open({
|
||||
type: 1,
|
||||
title: '同步数据库['+db+']数据校验',
|
||||
area: '500px',
|
||||
btn:[ "开始","取消","手动"],
|
||||
content:"<div class='bt-form'>\
|
||||
"+'<pre id="data_verify_log" style="overflow: auto; border: 0px none; line-height:23px;padding: 5px; margin: 0px; white-space: pre-wrap; height: 395px; background-color: rgb(51,51,51);color:#f1f1f1;border-radius:0px;font-family:"></pre>'+"\
|
||||
</div>",
|
||||
cancel: function(){
|
||||
if (reqTimer){
|
||||
clearInterval(reqTimer);
|
||||
}
|
||||
},
|
||||
yes:function(index,layer_index){
|
||||
myPostN('sync_database_repair_log', {db:db, sign:'',op:'do'}, function(data){});
|
||||
layer.msg("执行成功");
|
||||
|
||||
requestLogs(layer_index);
|
||||
reqTimer = setInterval(function(){
|
||||
requestLogs(layer_index);
|
||||
},3000);
|
||||
},
|
||||
success:function(){
|
||||
},
|
||||
btn3: function(){
|
||||
myPostN('sync_database_repair_log', {db:db, sign:'',op:'cmd'}, function(rdata){
|
||||
var rdata = $.parseJSON(rdata.data);
|
||||
layer.open({
|
||||
title: "手动执行命令CMD",
|
||||
area: ['600px', '180px'],
|
||||
type:1,
|
||||
closeBtn: 1,
|
||||
shadeClose: false,
|
||||
btn:["复制","取消"],
|
||||
content: '<div class="pd15">\
|
||||
<div class="divtable">\
|
||||
<pre class="layui-code">'+rdata.data+'</pre>\
|
||||
</div>\
|
||||
</div>',
|
||||
success:function(){
|
||||
copyText(rdata.data);
|
||||
},
|
||||
yes:function(){
|
||||
copyText(rdata.data);
|
||||
}
|
||||
});
|
||||
});
|
||||
return false;
|
||||
}
|
||||
|
||||
});
|
||||
}
|
||||
|
||||
function addSlaveSSH(ip=''){
|
||||
|
||||
myPost('get_slave_ssh_by_ip', {ip:ip}, function(rdata){
|
||||
@@ -2633,7 +2711,8 @@ function masterOrSlaveConf(version=''){
|
||||
list += '<td>' + rdata.data[i]['name'] +'</td>';
|
||||
list += '<td style="text-align:right">' +
|
||||
'<a href="javascript:;" class="btlink" onclick="setDbSlave(\''+rdata.data[i]['name']+'\')" title="加入|退出">'+(rdata.data[i]['slave']?'退出':'加入')+'</a> | ' +
|
||||
'<a href="javascript:;" class="btlink" onclick="getFullSyncStatus(\''+rdata.data[i]['name']+'\')" title="同步">同步</a>' +
|
||||
'<a href="javascript:;" class="btlink" onclick="getFullSyncStatus(\''+rdata.data[i]['name']+'\')" title="同步">同步</a> | ' +
|
||||
'<a href="javascript:;" class="btlink" onclick="dataSyncVerify(\''+rdata.data[i]['name']+'\')" title="数据校验">数据校验</a>' +
|
||||
'</td>';
|
||||
list += '</tr>';
|
||||
}
|
||||
|
||||
@@ -65,7 +65,7 @@ class backupTools:
|
||||
# mw.execShell(db_path + "/bin/mysqldump --defaults-file=" + my_conf_path + " --skip-lock-tables --default-character-set=utf8 " +
|
||||
# name + " | gzip > " + filename)
|
||||
|
||||
cmd = db_path + "/bin/mysqldump --defaults-file=" + my_conf_path + " --single-transaction --quick --default-character-set=utf8 " + \
|
||||
cmd = db_path + "/bin/mariadb-dump --defaults-file=" + my_conf_path + " --single-transaction --quick --default-character-set=utf8 " + \
|
||||
name + " | gzip > " + filename
|
||||
mw.execShell(cmd)
|
||||
|
||||
|
||||
@@ -3075,9 +3075,7 @@ def syncDatabaseRepairLog(version=''):
|
||||
|
||||
if op == 'do':
|
||||
os.system(' echo "开始执行" > '+ tmp_log)
|
||||
subprocess.Popen(cmd +' >> '+ tmp_log +' &')
|
||||
# time.sleep(10)
|
||||
# mw.execShell('rm -rf '+tmp_log)
|
||||
os.system(cmd +' >> '+ tmp_log +' &')
|
||||
return mw.returnJson(True, 'ok')
|
||||
|
||||
return mw.returnJson(False, '无效请求!')
|
||||
|
||||
@@ -871,7 +871,7 @@ function openPhpmyadmin(name,username,password){
|
||||
}
|
||||
|
||||
if (rdata.data['cfg']['choose'] != 'mysql'){
|
||||
layer.msg('当前为['+rdata.choose+']模式,若要使用请修改phpMyAdmin访问切换.',{icon:2,shade: [0.3, '#000']});
|
||||
layer.msg('当前为['+rdata.data['cfg']['choose'] + ']模式,若要使用请修改phpMyAdmin访问切换.',{icon:2,shade: [0.3, '#000']});
|
||||
return;
|
||||
}
|
||||
var home_page = rdata.data['home_page'];
|
||||
|
||||
@@ -210,13 +210,17 @@ def __delete_port(port):
|
||||
|
||||
|
||||
def openPort():
|
||||
for i in ["888"]:
|
||||
conf = getCfg()
|
||||
port = conf['port']
|
||||
for i in [port]:
|
||||
__release_port(i)
|
||||
return True
|
||||
|
||||
|
||||
def delPort():
|
||||
for i in ["888"]:
|
||||
conf = getCfg()
|
||||
port = conf['port']
|
||||
for i in [port]:
|
||||
__delete_port(i)
|
||||
return True
|
||||
|
||||
|
||||
@@ -111,6 +111,7 @@ function autoMakeConf(){
|
||||
</div>\
|
||||
<ul class='help-info-text c7'>\
|
||||
<li style='color:red;'>具体配置,仍须手动修改!!!</li>'\
|
||||
<li style='color:red;'>增量索引,需要有更新权限,主从分离时,需要主库配置</li>'\
|
||||
</ul>\
|
||||
</form>\
|
||||
",
|
||||
@@ -286,7 +287,6 @@ function readme(){
|
||||
|
||||
con += '<li style="color:red;">如果数据量比较大,第一次启动会失败!(可通过手动建立索引)</li>';
|
||||
con += '<li style="color:red;">以下内容,需手动加入计划任务。</li>';
|
||||
|
||||
|
||||
con += '<li>全量:' + rdata['data']['cmd'] + ' --all --rotate</li>';
|
||||
|
||||
@@ -294,11 +294,13 @@ function readme(){
|
||||
for (var i = 0; i < rdata['data']['index'].length; i++) {
|
||||
var index_kv = rdata['data']['index'][i];
|
||||
var index = index_kv['index'];
|
||||
var delta = index_kv['delta'];
|
||||
// console.log(index,delta);
|
||||
// console.log(index);
|
||||
con += '<li>主索引 :' + rdata['data']['cmd'] + ' '+ index +' --rotate</li>';
|
||||
con += '<li>增量索引 :' + rdata['data']['cmd'] + ' '+ delta +' --rotate</li>';
|
||||
con += '<li>合并索引 :' + rdata['data']['cmd'] + ' --merge '+ index + ' ' + delta +' --rotate</li>';
|
||||
if (typeof(index_kv['delta']) != 'undefined'){
|
||||
var delta = index_kv['delta'];
|
||||
con += '<li>增量索引 :' + rdata['data']['cmd'] + ' '+ delta +' --rotate</li>';
|
||||
con += '<li>合并索引 :' + rdata['data']['cmd'] + ' --merge '+ index + ' ' + delta +' --rotate</li>';
|
||||
}
|
||||
}
|
||||
con += '</ul>';
|
||||
|
||||
|
||||
@@ -31,6 +31,7 @@ CREATE INDEX method_idx ON web_logs (`method`);
|
||||
CREATE INDEX status_code_idx ON web_logs (`status_code`);
|
||||
CREATE INDEX request_time_idx ON web_logs (`request_time`);
|
||||
CREATE INDEX is_spider_idx ON web_logs (`is_spider`);
|
||||
CREATE INDEX body_length_idx ON web_logs (`body_length`);
|
||||
CREATE INDEX all_union_idx ON web_logs (`time`,`ip`,`method`,`status_code`,`request_time`,`is_spider`);
|
||||
|
||||
|
||||
@@ -84,6 +85,7 @@ CREATE TABLE `request_stat`(
|
||||
`status_402` INTEGER DEFAULT 0,
|
||||
`status_403` INTEGER DEFAULT 0,
|
||||
`status_404` INTEGER DEFAULT 0,
|
||||
`status_499` INTEGER DEFAULT 0,
|
||||
`http_get` INTEGER DEFAULT 0,
|
||||
`http_post` INTEGER DEFAULT 0,
|
||||
`http_put` INTEGER DEFAULT 0,
|
||||
|
||||
@@ -791,7 +791,23 @@ def getLogsList():
|
||||
|
||||
attacHistoryLogHack(conn, domain, query_date)
|
||||
|
||||
conn.changeTextFactoryToBytes()
|
||||
clist = conn.limit(limit).order('time desc').inquiry()
|
||||
|
||||
for x in range(len(clist)):
|
||||
req_line = clist[x]
|
||||
for cx in req_line:
|
||||
v = req_line[cx]
|
||||
if type(v) == bytes:
|
||||
try:
|
||||
clist[x][cx] = v.decode('utf-8')
|
||||
except Exception as e:
|
||||
v = str(v)
|
||||
v = v.replace("b'",'').strip("'")
|
||||
clist[x][cx] = v
|
||||
else:
|
||||
clist[x][cx] = v
|
||||
|
||||
# print(clist)
|
||||
count_key = "count(*) as num"
|
||||
count = conn.field(count_key).limit('').order('').inquiry()
|
||||
|
||||
@@ -2257,11 +2257,13 @@ function wsTableLogRequest(page){
|
||||
}
|
||||
|
||||
$('#logs_search').attr('req','start');
|
||||
// wsPost('get_logs_list', '' ,args, function(rdata){
|
||||
wsPostCallbak('get_logs_list', '' ,args, function(rdata){
|
||||
$('#logs_search').attr('req','end');
|
||||
var rdata = $.parseJSON(rdata.data);
|
||||
var list = '';
|
||||
var data = rdata.data.data;
|
||||
// console.log(data);
|
||||
|
||||
if (data.length > 0){
|
||||
for(i in data){
|
||||
@@ -2346,20 +2348,23 @@ function wsTableLogRequest(page){
|
||||
var index = $(this).attr('data-id');
|
||||
var res = data[index];
|
||||
var request_headers = res.request_headers;
|
||||
var req_data = $.parseJSON(request_headers);
|
||||
|
||||
var req_data_html = res.method +' ' + res.uri + '<br/>';
|
||||
for (var d in req_data) {
|
||||
|
||||
if (d == 'payload'){
|
||||
req_data_html += '<b style="color:red;">'+d +"</b>:"+req_data[d]+"<br/>";
|
||||
} else{
|
||||
req_data_html += d+":"+req_data[d]+"<br/>";
|
||||
try {
|
||||
var req_data = $.parseJSON(request_headers);
|
||||
for (var d in req_data) {
|
||||
if (d == 'payload'){
|
||||
req_data_html += '<b style="color:red;">'+d +"</b>:"+req_data[d]+"<br/>";
|
||||
} else{
|
||||
req_data_html += d+":"+req_data[d]+"<br/>";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
} catch (error) {
|
||||
req_data_html += request_headers;
|
||||
}
|
||||
|
||||
|
||||
layer.open({
|
||||
type: 1,
|
||||
title: "【"+res.domain + "】HTTP详情",
|
||||
|
||||
@@ -616,7 +616,32 @@ function _M.statistics_request(self, ip, is_spider, body_length)
|
||||
-- 计算pv uv
|
||||
local pvc = 0
|
||||
local uvc = 0
|
||||
local request_header = ngx.req.get_headers()
|
||||
if not is_spider and ngx.status == 200 and body_length > 0 then
|
||||
local ua = ''
|
||||
if request_header['user-agent'] then
|
||||
ua = string.lower(request_header['user-agent'])
|
||||
end
|
||||
|
||||
pvc = 1
|
||||
if ua then
|
||||
local today = ngx.today()
|
||||
local uv_token = ngx.md5(ip .. ua .. today)
|
||||
if not cache:get(uv_token) then
|
||||
uvc = 1
|
||||
cache:set(uv_token,1, self:get_end_time())
|
||||
end
|
||||
end
|
||||
end
|
||||
return pvc, uvc
|
||||
end
|
||||
|
||||
-- 仅计算GET/HTML
|
||||
function _M.statistics_request_old(self, ip, is_spider, body_length)
|
||||
-- 计算pv uv
|
||||
local pvc = 0
|
||||
local uvc = 0
|
||||
local method = ngx.req.get_method()
|
||||
if not is_spider and method == 'GET' and ngx.status == 200 and body_length > 512 then
|
||||
local ua = ''
|
||||
if request_header['user-agent'] then
|
||||
|
||||
@@ -260,7 +260,7 @@ log_by_lua_block {
|
||||
end
|
||||
end
|
||||
|
||||
if ngx.re.find("500,501,502,503,504,505,506,507,509,510,400,401,402,403,404,405,406,407,408,409,410,411,412,413,414,415,416,417,418,421,422,423,424,425,426,449,451", tostring(status_code), "jo") then
|
||||
if ngx.re.find("500,501,502,503,504,505,506,507,509,510,400,401,402,403,404,405,406,407,408,409,410,411,412,413,414,415,416,417,418,421,422,423,424,425,426,449,451,499", tostring(status_code), "jo") then
|
||||
local field = "status_"..status_code
|
||||
request_stat_fields = request_stat_fields .. ","..field.."="..field.."+1"
|
||||
end
|
||||
@@ -392,7 +392,7 @@ log_by_lua_block {
|
||||
end
|
||||
end
|
||||
|
||||
if ngx.re.find("500,501,502,503,504,505,506,507,509,510,400,401,402,403,404,405,406,407,408,409,410,411,412,413,414,415,416,417,418,421,422,423,424,425,426,449,451", tostring(status_code), "jo") then
|
||||
if ngx.re.find("500,501,502,503,504,505,506,507,509,510,400,401,402,403,404,405,406,407,408,409,410,411,412,413,414,415,416,417,418,421,422,423,424,425,426,449,451,499", tostring(status_code), "jo") then
|
||||
local field = "status_"..status_code
|
||||
request_stat_fields[field] = 1
|
||||
end
|
||||
|
||||
@@ -791,8 +791,22 @@ def get_logs_list(args):
|
||||
|
||||
attacHistoryLogHack(conn, domain, query_date)
|
||||
|
||||
conn.changeTextFactoryToBytes()
|
||||
clist = conn.limit(limit).order('time desc').inquiry()
|
||||
# print(clist)
|
||||
for x in range(len(clist)):
|
||||
req_line = clist[x]
|
||||
for cx in req_line:
|
||||
v = req_line[cx]
|
||||
if type(v) == bytes:
|
||||
try:
|
||||
clist[x][cx] = v.decode('utf-8')
|
||||
except Exception as e:
|
||||
v = str(v)
|
||||
v = v.replace("b'",'').strip("'")
|
||||
clist[x][cx] = v
|
||||
else:
|
||||
clist[x][cx] = v
|
||||
|
||||
count_key = "count(*) as num"
|
||||
count = conn.field(count_key).limit('').order('').inquiry()
|
||||
# print(count)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
setuptools>=33.1.1
|
||||
Werkzeug>=1.0.1,<3.0.0
|
||||
wheel>=0.37.1
|
||||
gunicorn==22.0.0
|
||||
gunicorn>=21.2.0
|
||||
requests>=2.27.1
|
||||
urllib3>=1.21.1
|
||||
flask>=2.0.3,<3.0.0
|
||||
@@ -29,7 +29,7 @@ pymemcache
|
||||
redis
|
||||
pillow
|
||||
Jinja2>=2.11.2
|
||||
PyMySQL==1.1.1
|
||||
PyMySQL>=1.0.2
|
||||
whitenoise==5.3.0
|
||||
pyotp
|
||||
pytz
|
||||
|
||||
@@ -60,8 +60,7 @@ try:
|
||||
sdb.create_all()
|
||||
except:
|
||||
app.config['SESSION_TYPE'] = 'filesystem'
|
||||
app.config['SESSION_FILE_DIR'] = '/tmp/py_mw_session_' + \
|
||||
str(sys.version_info[0])
|
||||
app.config['SESSION_FILE_DIR'] = '/tmp/py_mw_session_' + str(sys.version_info[0])
|
||||
app.config['SESSION_FILE_THRESHOLD'] = 1024
|
||||
app.config['SESSION_FILE_MODE'] = 384
|
||||
|
||||
@@ -296,9 +295,6 @@ def code():
|
||||
|
||||
out = io.BytesIO()
|
||||
codeImage[0].save(out, "png")
|
||||
|
||||
# print(codeImage[1])
|
||||
|
||||
session['code'] = mw.md5(''.join(codeImage[1]).lower())
|
||||
|
||||
img = Response(out.getvalue(), headers={'Content-Type': 'image/png'})
|
||||
|
||||
@@ -75,16 +75,106 @@ $('input[name="bind_domain"]').change(function(){
|
||||
$('.btn_bind_domain').removeAttr('disabled');
|
||||
$('.btn_bind_domain').unbind().click(function(){
|
||||
$.post('/config/set_panel_domain','domain='+domain, function(rdata){
|
||||
showMsg(rdata.msg,function(){window.location.reload();},{icon:rdata.status?1:2},2000);
|
||||
showMsg(rdata.msg,function(){
|
||||
window.location.href = rdata.data;
|
||||
},{icon:rdata.status?1:2},5000);
|
||||
},'json');
|
||||
});
|
||||
});
|
||||
|
||||
$('input[name="bind_ssl"]').click(function(){
|
||||
var open_ssl = $(this).prop("checked");
|
||||
$.post('/config/set_panel_ssl',{}, function(rdata){
|
||||
showMsg(rdata.msg,function(){window.location.reload();},{icon:rdata.status?1:2},2000);
|
||||
},'json');
|
||||
var panel_ssl = $(this).prop("checked");
|
||||
$(this).prop("checked",!panel_ssl);
|
||||
|
||||
//开启证书
|
||||
if (panel_ssl){
|
||||
// <option value="1">ACME</option>
|
||||
layer.open({
|
||||
type:1,
|
||||
closeBtn: 1,
|
||||
title:"开启SSL证书",
|
||||
area: ['600px','440px'],
|
||||
btn: ["开启SSL证书访问"],
|
||||
maxmin:false,
|
||||
shadeClose: true,
|
||||
content: '<div class="bt-form" style="padding: 25px 40px;">\
|
||||
<div style="text-align: center;">\
|
||||
<h3 style="font-size: 20px;color: #333;margin-left: 5px;">【开启SSL证书】保护面板访问安全</h3>\
|
||||
</div>\
|
||||
<ul class="help-info-text c7 pd15" style="color: #333;font-size: 14px;background: #F5F7FA;margin-top: 24px;padding: 10px 20px 10px 20px;border-radius: 2px;">\
|
||||
<li>自签证书访问步骤:</li>\
|
||||
<li>1. 部署SSL证书</li>\
|
||||
<li>2. 浏览器地址栏修改为https://访问</li>\
|
||||
<li>3. 如提醒风险(正常现象)点击【高级】或【详情】</li>\
|
||||
<li>4.【继续访问】或【接收风险并继续】</li>\
|
||||
</ul>\
|
||||
<div class="pt10" style="margin-top: 20px;">\
|
||||
<div class="line" style="font-size: 14px;">\
|
||||
<span class="tname" style="width: 78px;">类型</span>\
|
||||
<div class="info-r" style="margin-left: 78px;">\
|
||||
<select class="bt-input-text mr5" name="cert_type" style="width: 440px;">\
|
||||
<option value="0">自签证书 (推荐,浏览器会提示不安全。可忽略,请放心开启)</option>\
|
||||
</select>\
|
||||
</div>\
|
||||
</div>\
|
||||
<ul class="help-info-text c7 sslSafeTips">\
|
||||
<li><span>开启后导致面板不能访问,可以点击查看</span></li>\
|
||||
<li>自签证书不被浏览器信任,显示不安全是正常现象</li>\
|
||||
</ul>\
|
||||
</div>\
|
||||
</div>',
|
||||
yes: function(){
|
||||
|
||||
var cert_type = $('select[name=cert_type]').val();
|
||||
$.post('/config/set_panel_local_ssl',{'cert_type':cert_type}, function(rdata){
|
||||
// console.log(rdata);
|
||||
var to_https = window.location.href.replace('http','https');
|
||||
showMsg(rdata.msg,function(){
|
||||
if (rdata.status){
|
||||
window.location.href = to_https;
|
||||
}
|
||||
},{icon:rdata.status?1:2},5000);
|
||||
},'json');
|
||||
|
||||
}
|
||||
});
|
||||
} else {
|
||||
//关闭SSL
|
||||
layer.open({
|
||||
type:1,
|
||||
closeBtn: 1,
|
||||
title:"关闭SSL证书",
|
||||
area: ['480px','280px'],
|
||||
btn: ["确定","取消"],
|
||||
shadeClose: true,
|
||||
content: '<div class="bt-form" style="padding: 25px 40px;">\
|
||||
<div class="hint_title" style="font-size: 15px;color: #111;text-align:center;">\
|
||||
<div class="hint_con">关闭SSL极易被抓包攻击导致账号密码泄露,请勿关闭</div>\
|
||||
</div>\
|
||||
<div class="confirm-info-box" style="background-color: #f0f0f0;clear: both;font-size: 14px;height: 105px;line-height: 26px;padding: 20px 20px;margin-top: 20px;">\
|
||||
<div>请手动输入【<span style="color: #fc6d26;">我要关闭</span>】,完成验证</div>\
|
||||
<input onpaste="return false;" id="prompt_input_box" style="height: 30px;line-height: 30px;margin-top: 5px;width: 360px;color: #444;outline: none;border: 1px solid #ccc;padding: 0 5px;" type="text" value="" autocomplete="off">\
|
||||
</div>\
|
||||
</div>',
|
||||
yes: function(index){
|
||||
var val = $('#prompt_input_box').val();
|
||||
if (val != '我要关闭'){
|
||||
layer.msg("关闭SSL失败!");
|
||||
return;
|
||||
}
|
||||
|
||||
$.post('/config/close_panel_ssl',{}, function(rdata){
|
||||
var to_http = window.location.href.replace('https','http');
|
||||
showMsg(rdata.msg,function(){
|
||||
if (rdata.status){
|
||||
window.location.href = to_http;
|
||||
}
|
||||
},{icon:rdata.status?1:2},5000);
|
||||
},'json');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
});
|
||||
|
||||
/** op **/
|
||||
@@ -714,8 +804,30 @@ function getPanelSSL(){
|
||||
$.post('/config/get_panel_ssl',{},function(cert){
|
||||
layer.close(loadT);
|
||||
|
||||
// console.log(cert);
|
||||
var choose = cert['choose'];
|
||||
var choose_local = '';
|
||||
var choose_nginx = '';
|
||||
|
||||
if (choose == 'local'){
|
||||
cert = cert['local'];
|
||||
choose_local = 'selected="selected"';
|
||||
} else if (choose == 'nginx') {
|
||||
cert = cert['nginx'];
|
||||
choose_nginx = 'selected="selected"';
|
||||
} else {
|
||||
cert = cert['local'];
|
||||
}
|
||||
|
||||
var cert_data = '';
|
||||
|
||||
// <div class='state_item'>\
|
||||
// <span>强制HTTPS:</span>\
|
||||
// <span class='switch'>\
|
||||
// <input class='btswitch btswitch-ios' id='toHttps' type='checkbox' "+cert['is_https']+">\
|
||||
// <label class='btswitch-btn set_panel_http_to_https' for='toHttps'></label>\
|
||||
// </span>\
|
||||
// </div>\
|
||||
if (cert['info']){
|
||||
cert_data = "<div class='ssl_state_info'><div class='state_info_flex'>\
|
||||
<div class='state_item'><span>证书品牌:</span>\
|
||||
@@ -726,16 +838,11 @@ function getPanelSSL(){
|
||||
<div class='state_info_flex'>\
|
||||
<div class='state_item'><span>认证域名:</span>\
|
||||
<span class='ellipsis_text ssl_subject'>"+cert['info']['subject']+"</span></div>\
|
||||
<div class='state_item'>\
|
||||
<span>强制HTTPS:</span>\
|
||||
<span class='switch'>\
|
||||
<input class='btswitch btswitch-ios' id='toHttps' type='checkbox' "+cert['is_https']+">\
|
||||
<label class='btswitch-btn set_panel_http_to_https' for='toHttps'></label>\
|
||||
</span>\
|
||||
</div>\
|
||||
</div></div>";
|
||||
}
|
||||
|
||||
// <button class="btn btn-success btn-sm apply-lets-ssl">申请ACME证书</button>\
|
||||
// <option value="nginx" '+choose_nginx+'>OpenResty</option>\
|
||||
var certBody = '<div class="tab-con">\
|
||||
<div class="myKeyCon ptb15">\
|
||||
'+cert_data+'\
|
||||
@@ -750,7 +857,9 @@ function getPanelSSL(){
|
||||
<div class="ssl-btn pull-left mtb15" style="width:100%">\
|
||||
<button class="btn btn-success btn-sm save-panel-ssl">保存</button>\
|
||||
<button class="btn btn-success btn-sm del-panel-ssl">删除</button>\
|
||||
<button class="btn btn-success btn-sm apply-lets-ssl">申请Lets证书</button>\
|
||||
<select class="bt-input-text" name="choose" style="width:100px;">\
|
||||
<option value="local" '+choose_local+'>本地</option>\
|
||||
</select>\
|
||||
</div>\
|
||||
</div>\
|
||||
<ul class="help-info-text c7 pull-left">\
|
||||
@@ -768,65 +877,120 @@ function getPanelSSL(){
|
||||
content:certBody,
|
||||
success:function(layero, layer_id){
|
||||
|
||||
|
||||
//保存SSL
|
||||
$('.save-panel-ssl').click(function(){
|
||||
var data = {
|
||||
privateKey:$("#key").val(),
|
||||
certPem:$("#csr").val()
|
||||
}
|
||||
var loadT = layer.msg('正在安装并设置SSL组件,这需要几分钟时间...',{icon:16,time:0,shade: [0.3, '#000']});
|
||||
$.post('/config/save_panel_ssl',data,function(rdata){
|
||||
layer.close(loadT);
|
||||
if(rdata.status){
|
||||
layer.closeAll();
|
||||
}
|
||||
layer.msg(rdata.msg,{icon:rdata.status?1:2});
|
||||
},'json');
|
||||
|
||||
layer.confirm('选择保存面板SSL方式?',
|
||||
{
|
||||
title:'提示',
|
||||
shade:0.001,
|
||||
btn: ['本地SSL', '取消'],//'OpenResty'
|
||||
btn3:function(){
|
||||
data['choose'] = 'nginx';
|
||||
var loadT = layer.msg('正在安装并设置SSL组件,这需要几分钟时间...',{icon:16,time:0,shade: [0.3, '#000']});
|
||||
$.post('/config/save_panel_ssl',data,function(rdata){
|
||||
layer.close(loadT);
|
||||
if(rdata.status){
|
||||
layer.closeAll();
|
||||
}
|
||||
layer.msg(rdata.msg,{icon:rdata.status?1:2});
|
||||
},'json');
|
||||
},
|
||||
},
|
||||
function(index) {
|
||||
data['choose'] = 'local';
|
||||
var loadT = layer.msg('正在安装并设置SSL组件,这需要几分钟时间...',{icon:16,time:0,shade: [0.3, '#000']});
|
||||
$.post('/config/save_panel_ssl',data,function(rdata){
|
||||
layer.close(loadT);
|
||||
if(rdata.status){
|
||||
layer.closeAll();
|
||||
}
|
||||
layer.msg(rdata.msg,{icon:rdata.status?1:2});
|
||||
},'json');
|
||||
},
|
||||
function(index) {
|
||||
layer.close(index);
|
||||
});
|
||||
});
|
||||
|
||||
//删除SSL
|
||||
$('.del-panel-ssl').click(function(){
|
||||
var loadT = layer.msg('正在删除SSL...',{icon:16,time:0,shade: [0.3, '#000']});
|
||||
$.post('/config/del_panel_ssl',data,function(rdata){
|
||||
layer.close(loadT);
|
||||
if(rdata.status){
|
||||
layer.closeAll();
|
||||
}
|
||||
layer.msg(rdata.msg,{icon:rdata.status?1:2});
|
||||
},'json');
|
||||
});
|
||||
|
||||
// 设置面板SSL的Http
|
||||
$('.set_panel_http_to_https').click(function(){
|
||||
var https = $('#toHttps').prop('checked');
|
||||
$.post('/config/set_panel_http_to_https',{'https':https},function(rdata){
|
||||
layer.close(loadT);
|
||||
if(rdata.status){
|
||||
layer.closeAll();
|
||||
}
|
||||
layer.msg(rdata.msg,{icon:rdata.status?1:2});
|
||||
},'json');
|
||||
});
|
||||
|
||||
//申请Lets证书
|
||||
$('.apply-lets-ssl').click(function(){
|
||||
showSpeedWindow('正在申请...', 'site.get_let_logs', function(layers,index){
|
||||
$.post('/config/apply_panel_let_ssl',{},function(rdata){
|
||||
layer.confirm('选择删除面板SSL方式?',
|
||||
{
|
||||
title:'提示',
|
||||
shade:0.001,
|
||||
btn: ['本地SSL', '取消'],//, 'OpenResty'
|
||||
btn3:function(){
|
||||
var data = {};
|
||||
data['choose'] = 'nginx';
|
||||
var loadT = layer.msg('正在删除面板SSL【nginx】...',{icon:16,time:0,shade: [0.3, '#000']});
|
||||
$.post('/config/del_panel_ssl',data,function(rdata){
|
||||
layer.close(loadT);
|
||||
if(rdata.status){
|
||||
layer.closeAll();
|
||||
}
|
||||
layer.msg(rdata.msg,{icon:rdata.status?1:2});
|
||||
},'json');
|
||||
},
|
||||
},
|
||||
function(index) {
|
||||
var data = {};
|
||||
data['choose'] = 'local';
|
||||
var loadT = layer.msg('正在删除面板SSL【本地】...',{icon:16,time:0,shade: [0.3, '#000']});
|
||||
$.post('/config/del_panel_ssl',data,function(rdata){
|
||||
console.log(rdata);
|
||||
layer.close(loadT);
|
||||
if(rdata.status){
|
||||
layer.close(index);
|
||||
var tdata = rdata['data'];
|
||||
$('.ssl_issue').text(tdata['info']['issuer']);
|
||||
$('.ssl_endtime').text("剩余"+tdata['info']['endtime']+"天到期");
|
||||
$('.ssl_subject').text(tdata['info']['subject']);
|
||||
|
||||
$('textarea[name="key"]').val(tdata['info']['privateKey']);
|
||||
$('textarea[name="csr"]').val(tdata['info']['certPem']);
|
||||
}
|
||||
layer.msg(rdata.msg,{icon:rdata.status?1:2});
|
||||
showMsg(rdata.msg, function(){
|
||||
if(rdata.status){
|
||||
location.href = rdata.data;
|
||||
}
|
||||
},{icon:rdata.status?1:2},3000);
|
||||
},'json');
|
||||
});
|
||||
},
|
||||
function(index) {
|
||||
layer.close(index);
|
||||
});
|
||||
|
||||
|
||||
});
|
||||
|
||||
// // 设置面板SSL的Http
|
||||
// $('.set_panel_http_to_https').click(function(){
|
||||
// var https = $('#toHttps').prop('checked');
|
||||
// $.post('/config/set_panel_http_to_https',{'https':https},function(rdata){
|
||||
// layer.close(loadT);
|
||||
// if(rdata.status){
|
||||
// layer.closeAll();
|
||||
// }
|
||||
// layer.msg(rdata.msg,{icon:rdata.status?1:2});
|
||||
// },'json');
|
||||
// });
|
||||
|
||||
// //申请Lets证书
|
||||
// $('.apply-lets-ssl').click(function(){
|
||||
// showSpeedWindow('正在申请...', 'site.get_let_logs', function(layers,index){
|
||||
// $.post('/config/apply_panel_acme_ssl',{},function(rdata){
|
||||
// layer.close(loadT);
|
||||
// if(rdata.status){
|
||||
// layer.close(index);
|
||||
// var tdata = rdata['data'];
|
||||
// $('.ssl_issue').text(tdata['info']['issuer']);
|
||||
// $('.ssl_endtime').text("剩余"+tdata['info']['endtime']+"天到期");
|
||||
// $('.ssl_subject').text(tdata['info']['subject']);
|
||||
|
||||
// $('textarea[name="key"]').val(tdata['info']['privateKey']);
|
||||
// $('textarea[name="csr"]').val(tdata['info']['certPem']);
|
||||
// }
|
||||
// layer.msg(rdata.msg,{icon:rdata.status?1:2});
|
||||
// },'json');
|
||||
// });
|
||||
// });
|
||||
}
|
||||
});
|
||||
},'json');
|
||||
|
||||
@@ -294,7 +294,7 @@ function openFilename(obj){
|
||||
onlineEditFile(0, path);
|
||||
}
|
||||
|
||||
if (inArray(ext,['png','jpeg','gif','jpg','ico'])){
|
||||
if (inArray(ext,['png','jpeg','jpg','gif','webp','bmp','ico'])){
|
||||
getImage(path);
|
||||
}
|
||||
|
||||
|
||||
@@ -1000,7 +1000,7 @@ function setSelectChecked(c, d) {
|
||||
|
||||
function jump() {
|
||||
layer.closeAll();
|
||||
window.location.href = "/soft"
|
||||
window.location.href = "/soft";
|
||||
}
|
||||
|
||||
function installTips() {
|
||||
@@ -1134,15 +1134,15 @@ function getPanelList(){
|
||||
var user = $(this).attr("data-user");
|
||||
var pw = $(this).attr("data-pw");
|
||||
layer.open({
|
||||
type: 2,
|
||||
title: false,
|
||||
closeBtn: 0, //不显示关闭按钮
|
||||
shade: [0],
|
||||
area: ['340px', '215px'],
|
||||
offset: 'rb', //右下角弹出
|
||||
time: 5, //2秒后自动关闭
|
||||
anim: 2,
|
||||
content: [murl+'/login', 'no']
|
||||
type: 2,
|
||||
title: false,
|
||||
closeBtn: 0, //不显示关闭按钮
|
||||
shade: [0],
|
||||
area: ['340px', '215px'],
|
||||
offset: 'rb', //右下角弹出
|
||||
time: 5, //2秒后自动关闭
|
||||
anim: 2,
|
||||
content: [murl+'/login', 'no']
|
||||
});
|
||||
var loginForm ='<div id="btpanelform" style="display:none"><form id="toBtpanel" action="'+murl+'/do_login" method="post" target="btpfrom">\
|
||||
<input name="username" id="btp_username" value="'+user+'" type="text">\
|
||||
@@ -1375,7 +1375,7 @@ function remind(a){
|
||||
<td>\
|
||||
<div class="titlename c3">'+g.data[d].name+'</span>\
|
||||
<span class="rs-status">【'+lan.bt.task_ok+'】<span>\
|
||||
<span class="rs-time">耗时['+ getSFM(g.data[d].end - g.data[d].start) +']</span>\
|
||||
<span class="rs-time">安装等待中...</span>\
|
||||
</div>\
|
||||
</td>\
|
||||
<td class="text-right c3">'+g.data[d].addtime+'</td>\
|
||||
|
||||
|
Before Width: | Height: | Size: 2.0 KiB |
|
Before Width: | Height: | Size: 1.3 KiB |
|
Before Width: | Height: | Size: 1.3 KiB |
|
Before Width: | Height: | Size: 1.3 KiB |
|
Before Width: | Height: | Size: 1.2 KiB |
|
Before Width: | Height: | Size: 1.6 KiB |
|
Before Width: | Height: | Size: 1.8 KiB |
@@ -54,7 +54,7 @@
|
||||
|
||||
<p class="mtb15">
|
||||
<span class="set-tit text-right">面板端口</span>
|
||||
<input data-port="{{data['port']}}" id="banport" name="port" class="inputtxt bt-input-text" type="numner" value="{{data['port']}}" maxlength="5">
|
||||
<input data-port="{{data['port']}}" id="banport" name="port" class="inputtxt bt-input-text" type="number" value="{{data['port']}}" maxlength="5">
|
||||
<button type="button" class="btn btn-success btn-sm ml5 btn_port" disabled>保存</button>
|
||||
<span class="set-info c7">建议端口范围7200 - 65535</span>
|
||||
</p>
|
||||
|
||||
@@ -209,6 +209,21 @@ mw_unbind_domain()
|
||||
fi
|
||||
}
|
||||
|
||||
mw_unbind_ssl()
|
||||
{
|
||||
if [ -f $mw_path/local ];then
|
||||
rm -rf $mw_path/local
|
||||
fi
|
||||
|
||||
if [ -f $mw_path/nginx ];then
|
||||
rm -rf $mw_path/nginx
|
||||
fi
|
||||
|
||||
if [ -f $mw_path/ssl/choose.pl ];then
|
||||
rm -rf $mw_path/ssl/choose.pl
|
||||
fi
|
||||
}
|
||||
|
||||
error_logs()
|
||||
{
|
||||
tail -n 100 $mw_path/logs/error.log
|
||||
@@ -445,6 +460,7 @@ case "$1" in
|
||||
'install_app') mw_install_app;;
|
||||
'close_admin_path') mw_close_admin_path;;
|
||||
'unbind_domain') mw_unbind_domain;;
|
||||
'unbind_ssl') mw_unbind_domain;;
|
||||
'debug') mw_debug;;
|
||||
'mirror') mw_mirror;;
|
||||
'db') mw_connect_mysql;;
|
||||
@@ -454,6 +470,11 @@ case "$1" in
|
||||
'default')
|
||||
cd $mw_path
|
||||
port=7200
|
||||
scheme=http
|
||||
|
||||
if [ -f $mw_path/ssl/choose.pl ];then
|
||||
scheme=https
|
||||
fi
|
||||
|
||||
if [ -f $mw_path/data/port.pl ];then
|
||||
port=$(cat $mw_path/data/port.pl)
|
||||
@@ -484,9 +505,9 @@ case "$1" in
|
||||
mw_start
|
||||
fi
|
||||
|
||||
address="MW-Panel-Url-Ipv4: http://$v4:$port$auth_path \nMW-Panel-Url-Ipv6: http://[$v6]:$port$auth_path"
|
||||
address="MW-Panel-Url-Ipv4: ${scheme}://$v4:$port$auth_path \nMW-Panel-Url-Ipv6: ${scheme}://[$v6]:$port$auth_path"
|
||||
elif [ "$v4" != "" ]; then
|
||||
address="MW-Panel-Url: http://$v4:$port$auth_path"
|
||||
address="MW-Panel-Url: ${scheme}://$v4:$port$auth_path"
|
||||
elif [ "$v6" != "" ]; then
|
||||
|
||||
if [ ! -f $mw_path/data/ipv6.pl ];then
|
||||
@@ -495,12 +516,12 @@ case "$1" in
|
||||
mw_stop
|
||||
mw_start
|
||||
fi
|
||||
address="MW-Panel-Url: http://[$v6]:$port$auth_path"
|
||||
address="MW-Panel-Url: ${scheme}://[$v6]:$port$auth_path"
|
||||
else
|
||||
address="MW-Panel-Url: http://you-network-ip:$port$auth_path"
|
||||
address="MW-Panel-Url: ${scheme}://you-network-ip:$port$auth_path"
|
||||
fi
|
||||
else
|
||||
address="MW-Panel-Url: http://$address:$port$auth_path"
|
||||
address="MW-Panel-Url: ${scheme}://$address:$port$auth_path"
|
||||
fi
|
||||
|
||||
show_panel_ip="$port|"
|
||||
|
||||
@@ -59,11 +59,17 @@ if os.path.exists('data/ipv6.pl'):
|
||||
else:
|
||||
bind.append('0.0.0.0:%s' % mw_port)
|
||||
|
||||
# if os.path.exists('data/ssl.pl'):
|
||||
# certfile = 'ssl/certificate.pem'
|
||||
# keyfile = 'ssl/privateKey.pem'
|
||||
# ciphers = 'TLSv1 TLSv1.1 TLSv1.2 TLSv1.3'
|
||||
# ssl_version = 2
|
||||
ssl_choose_file = 'ssl/choose.pl'
|
||||
if os.path.exists(ssl_choose_file):
|
||||
ssl_choose = mw.readFile(ssl_choose_file).strip()
|
||||
if mw.inArray(['local','nginx'],ssl_choose):
|
||||
tmp_cert = 'ssl/'+ssl_choose+'/cert.pem'
|
||||
tmp_private = 'ssl/'+ssl_choose+'/private.pem'
|
||||
if os.path.exists(tmp_cert) and os.path.exists(tmp_private):
|
||||
certfile = tmp_cert
|
||||
keyfile = tmp_private
|
||||
ciphers = 'TLSv1 TLSv1.1 TLSv1.2 TLSv1.3'
|
||||
ssl_version = 2
|
||||
|
||||
# 初始安装时,自动生成安全路径
|
||||
if not os.path.exists('data/admin_path.pl'):
|
||||
|
||||
@@ -64,6 +64,29 @@ def service_cmd(method):
|
||||
return
|
||||
|
||||
|
||||
def openresty_cmd(method = 'reload'):
|
||||
# 检查是否安装
|
||||
odir = mw.getServerDir() + '/openresty'
|
||||
if not os.path.exists(odir):
|
||||
return False
|
||||
|
||||
# systemd
|
||||
systemd = mw.systemdCfgDir()+'/openresty.service'
|
||||
if os.path.exists(systemd):
|
||||
execShell('systemctl ' + method + ' openresty')
|
||||
return True
|
||||
|
||||
sys_initd = '/etc/init.d/openresty'
|
||||
if os.path.exists(install_initd):
|
||||
os.system(sys_initd + ' ' + method)
|
||||
return True
|
||||
|
||||
install_initd = mw.getServerDir()+'/openresty/init.d/openresty'
|
||||
if os.path.exists(install_initd):
|
||||
os.system(install_initd + ' ' + method)
|
||||
return True
|
||||
return False
|
||||
|
||||
def mw_async(f):
|
||||
def wrapper(*args, **kwargs):
|
||||
thr = threading.Thread(target=f, args=args, kwargs=kwargs)
|
||||
@@ -539,13 +562,7 @@ def openrestyAutoRestart():
|
||||
time.sleep(86400)
|
||||
continue
|
||||
|
||||
# systemd
|
||||
systemd = mw.systemdCfgDir()+'/openresty.service'
|
||||
initd = '/etc/init.d/openresty'
|
||||
if os.path.exists(systemd):
|
||||
execShell('systemctl reload openresty')
|
||||
elif os.path.exists(initd):
|
||||
os.system(initd + ' reload')
|
||||
openresty_cmd('reload')
|
||||
time.sleep(86400)
|
||||
except Exception as e:
|
||||
print(str(e))
|
||||
@@ -553,6 +570,18 @@ def openrestyAutoRestart():
|
||||
|
||||
# --------------------------------------OpenResty Auto Restart End --------------------------------------------- #
|
||||
|
||||
# ------------------------------------ OpenResty Restart At Once Start ------------------------------------------ #
|
||||
|
||||
|
||||
def openrestyRestartAtOnce():
|
||||
restart_nginx_tip = 'data/restart_nginx.pl'
|
||||
while True:
|
||||
if os.path.exists(restart_nginx_tip):
|
||||
os.remove(restart_nginx_tip)
|
||||
openresty_cmd('reload')
|
||||
time.sleep(1)
|
||||
# ----------------------------------- OpenResty Restart At Once End ------------------------------------------ #
|
||||
|
||||
|
||||
# --------------------------------------Panel Restart Start --------------------------------------------- #
|
||||
def restartPanelService():
|
||||
@@ -584,11 +613,18 @@ if __name__ == "__main__":
|
||||
php502 = setDaemon(php502)
|
||||
php502.start()
|
||||
|
||||
# OpenResty Restart At Once Start
|
||||
oraos = threading.Thread(target=openrestyRestartAtOnce)
|
||||
oraos = setDaemon(oraos)
|
||||
oraos.start()
|
||||
|
||||
|
||||
# OpenResty Auto Restart Start
|
||||
oar = threading.Thread(target=openrestyAutoRestart)
|
||||
oar = setDaemon(oar)
|
||||
oar.start()
|
||||
|
||||
|
||||
# Panel Restart Start
|
||||
rps = threading.Thread(target=restartPanelService)
|
||||
rps = setDaemon(rps)
|
||||
|
||||
@@ -58,11 +58,12 @@ def mwcli(mw_input=0):
|
||||
print("(13) 显示面板错误日志")
|
||||
print("(20) 关闭BasicAuth认证")
|
||||
print("(21) 解除域名绑定")
|
||||
print("(22) 开启IPV6支持")
|
||||
print("(23) 关闭IPV6支持")
|
||||
print("(24) 开启防火墙SSH端口")
|
||||
print("(25) 关闭二次验证")
|
||||
print("(26) 查看防火墙信息")
|
||||
print("(22) 解除面板SSL绑定")
|
||||
print("(23) 开启IPV6支持")
|
||||
print("(24) 关闭IPV6支持")
|
||||
print("(25) 开启防火墙SSH端口")
|
||||
print("(26) 关闭二次验证")
|
||||
print("(27) 查看防火墙信息")
|
||||
print("(100) 开启PHP52显示")
|
||||
print("(101) 关闭PHP52显示")
|
||||
print("(200) 切换Linux系统软件源")
|
||||
@@ -77,8 +78,8 @@ def mwcli(mw_input=0):
|
||||
mw_input = 0
|
||||
|
||||
nums = [
|
||||
1, 2, 3, 4, 5, 10, 11, 12, 13,
|
||||
20, 21, 22, 23, 24, 25, 26,
|
||||
1, 2, 3, 4, 5, 10, 11, 12, 13,
|
||||
20, 21, 22, 23, 24, 25, 26, 27,
|
||||
100, 101,
|
||||
200, 201
|
||||
]
|
||||
@@ -134,6 +135,12 @@ def mwcli(mw_input=0):
|
||||
os.system(INIT_CMD + " unbind_domain")
|
||||
print("|-解除域名绑定成功")
|
||||
elif mw_input == 22:
|
||||
ssl_choose = 'ssl/choose.pl'
|
||||
if os.path.exists(ssl_choose):
|
||||
os.remove(ssl_choose)
|
||||
os.system(INIT_CMD + " unbind_ssl")
|
||||
print("|-解除面板SSL绑定成功")
|
||||
elif mw_input == 23:
|
||||
listen_ipv6 = 'data/ipv6.pl'
|
||||
if not os.path.exists(listen_ipv6):
|
||||
mw.writeFile(listen_ipv6,'True')
|
||||
@@ -141,7 +148,7 @@ def mwcli(mw_input=0):
|
||||
print("|-开启IPv6支持了")
|
||||
else:
|
||||
print("|-已开启IPv6支持!")
|
||||
elif mw_input == 23:
|
||||
elif mw_input == 24:
|
||||
listen_ipv6 = 'data/ipv6.pl'
|
||||
if not os.path.exists(listen_ipv6):
|
||||
print("|-已关闭IPv6支持!")
|
||||
@@ -149,17 +156,17 @@ def mwcli(mw_input=0):
|
||||
os.remove(listen_ipv6)
|
||||
os.system(INIT_CMD + " restart")
|
||||
print("|-关闭IPv6支持了")
|
||||
elif mw_input == 24:
|
||||
elif mw_input == 25:
|
||||
open_ssh_port()
|
||||
print("|-已开启!")
|
||||
elif mw_input == 25:
|
||||
elif mw_input == 26:
|
||||
auth_secret = 'data/auth_secret.pl'
|
||||
if os.path.exists(auth_secret):
|
||||
os.remove(auth_secret)
|
||||
print("|-关闭二次验证成功!")
|
||||
else:
|
||||
print("|-二次验证已关闭!")
|
||||
elif mw_input == 26:
|
||||
elif mw_input == 27:
|
||||
cmd = 'which ufw'
|
||||
run_cmd = False
|
||||
find_cmd = mw.execShell(cmd)
|
||||
|
||||