diff --git a/BTPanel/__init__.py b/BTPanel/__init__.py
index bfac2365..d25c74ea 100644
--- a/BTPanel/__init__.py
+++ b/BTPanel/__init__.py
@@ -183,6 +183,8 @@ def login():
if request.method == method_post[0]:
result = userlogin.userlogin().request_post(get)
+ if result == "1":
+ return result
return is_login(result)
if request.method == method_get[0]:
@@ -460,7 +462,7 @@ def config(pdata = None):
if public.is_local(): data['is_local'] = 'checked'
return render_template( 'config.html',data=data)
import config
- defs = ('get_php_session_path','set_php_session_path','get_cert_source','set_local','set_debug','get_panel_error_logs','clean_panel_error_logs','get_basic_auth_stat','set_basic_auth','get_cli_php_version','get_tmp_token','set_cli_php_version','DelOldSession', 'GetSessionCount', 'SetSessionConf', 'GetSessionConf','get_ipv6_listen','set_ipv6_status','GetApacheValue','SetApacheValue','GetNginxValue','SetNginxValue','get_token','set_token','set_admin_path','is_pro','get_php_config','get_config','SavePanelSSL','GetPanelSSL','GetPHPConf','SetPHPConf','GetPanelList','AddPanelInfo','SetPanelInfo','DelPanelInfo','ClickPanelInfo','SetPanelSSL','SetTemplates','Set502','setPassword','setUsername','setPanel','setPathInfo','setPHPMaxSize','getFpmConfig','setFpmConfig','setPHPMaxTime','syncDate','setPHPDisable','SetControl','ClosePanel','AutoUpdatePanel','SetPanelLock')
+ defs = ('get_qrcode_data','check_two_step','set_two_step_auth','get_key','get_php_session_path','set_php_session_path','get_cert_source','set_local','set_debug','get_panel_error_logs','clean_panel_error_logs','get_basic_auth_stat','set_basic_auth','get_cli_php_version','get_tmp_token','set_cli_php_version','DelOldSession', 'GetSessionCount', 'SetSessionConf', 'GetSessionConf','get_ipv6_listen','set_ipv6_status','GetApacheValue','SetApacheValue','GetNginxValue','SetNginxValue','get_token','set_token','set_admin_path','is_pro','get_php_config','get_config','SavePanelSSL','GetPanelSSL','GetPHPConf','SetPHPConf','GetPanelList','AddPanelInfo','SetPanelInfo','DelPanelInfo','ClickPanelInfo','SetPanelSSL','SetTemplates','Set502','setPassword','setUsername','setPanel','setPathInfo','setPHPMaxSize','getFpmConfig','setFpmConfig','setPHPMaxTime','syncDate','setPHPDisable','SetControl','ClosePanel','AutoUpdatePanel','SetPanelLock')
return publicObject(config.config(),defs,None,pdata);
@app.route('/ajax',methods=method_all)
@@ -503,7 +505,11 @@ def panel_data(pdata = None):
@app.route('/code')
def code():
- import vilidate,time
+ try:
+ import vilidate,time
+ except:
+ os.system("pip install Pillow==5.4.1 -I")
+ return "Pillow not install!"
code_time = cache.get('codeOut')
if code_time: return u'Error: Don\'t request validation codes frequently';
vie = vilidate.vieCode();
@@ -710,7 +716,9 @@ def panel_other(name=None,fun = None,stype=None):
if r_type == dict: return public.returnJson(False,public.GetMsg("RETURN_TYPE_ERR") + '[%s]' % r_type), json_header
return data
except:
- return public.get_error_info().replace('\n',' \n')
+ error_info = public.get_error_info()
+ public.submit_error(error_info)
+ return error_info.replace('\n',' \n')
@app.route('/wxapp',methods=method_all)
@@ -991,10 +999,13 @@ def publicObject(toObject,defs,action=None,get = None):
if hasattr(get,'path'):
get.path = get.path.replace('//','/').replace('\\','/');
- if get.path.find('..') != -1: return public.ReturnJson(False,public.GetMsg("UNSAFE_PATH")),json_header
+ if get.path.find('./') != -1: return public.ReturnJson(False,public.GetMsg("UNSAFE_PATH")),json_header
if get.path.find('->') != -1:
get.path = get.path.split('->')[0].strip();
+ if hasattr(toObject,'site_path_check'):
+ if not toObject.site_path_check(get): return public.ReturnJson(False,'Excessive operation!'),json_header
+
for key in defs:
if key == get.action:
fun = 'toObject.'+key+'(get)'
diff --git a/BTPanel/static/css/login.css b/BTPanel/static/css/login.css
index 107df87a..65ec9530 100644
--- a/BTPanel/static/css/login.css
+++ b/BTPanel/static/css/login.css
@@ -1 +1,725 @@
-body{background-color:#444;font-family:华文黑体,"Microsoft YaHei",微软雅黑,STHei,"Helvetica Neue",Helvetica,Arial,sans-serif}input{-webkit-appearance:none}.main .login{background-color:#fff;border-radius:4px;height:290px;left:50%;margin-left:-220px;margin-top:-180px;padding:35px 40px 50px;position:absolute;top:50%;width:360px}.main .login .rlogo{text-align:center;font-size:26px;color:#444;cursor:pointer;height:40px;margin-bottom:40px;overflow:hidden}.main .login .title{color:#444;font-family:"微软雅黑";font-size:28px;margin:-10px 0 46px;text-align:center}.main .login .line{height:42px;margin-bottom:20px;line-height:42px;position:relative}.main .login .line span{width:70px;float:left;color:#757584}.main .login .line .inputtxt{background:transparent;float:left;width:338px;height:30px;line-height:30px;border:#d1d1d1 1px solid;padding:5px 10px;position:relative;z-index:1;box-shadow:0 3px 5px -4px rgba(0,0,0,0.4) inset,-1px 0 3px -2px rgba(0,0,0,0.1) inset;border-radius:3px}.main .login .line .code{width:230px}.main .login .line .getcode,.main .login .line .picgetcode{width:100px;text-align:center;height:38px;line-height:38px;border:#cacad9 1px solid;background-color:#dbdbea;color:#757584;font-size:14px;float:left;margin-left:17px;cursor:pointer}.main .login .line .getcode.no-drop{cursor:no-drop}.main .login .line .getcode.pointer{border:#47acff 1px solid;background-color:#47acff;color:#fff}.main .login .line .picgetcode img{width:100%;height:100%}.main .login .line .codemsg{background:#e53451;border-radius:3px;color:#fff;font-size:12px;height:21px;line-height:22px;overflow:hidden;padding:0 6px;position:absolute;right:127px;text-align:right;top:5px;z-index:0}.main .login .line .inputtxt:focus{border:#70c3ec 1px solid;box-shadow:0 0 5px #eee}.main .login .yzm .inputtxt{width:64%}.login_btn input{width:100%;height:44px;background:#20a53a;background:linear-gradient(#20a53a,#189d32);box-shadow:inset 0 1px 2px #30ad42;color:#fff;text-shadow:#00851a 0 -1px 0;border:1px solid #20a53a;text-align:center;font-size:16px;color:#fff;border-radius:3px;cursor:pointer}.login_btn input:hover{background:#10952a;background:linear-gradient(#10952a,#088d22);border:1px solid #10952a}.login-link{text-align:right;margin-top:22px}.login-link span{color:#cacad9;padding:0 8px;text-align:center}.login-link a{color:#757584}.copyright{color:#9d9d9d;text-align:center;margin-top:-50px;position:relative;z-index:3}.copyright a{color:#9d9d9d;text-decoration:none}.pwinfo{bottom:19px;color:#999;position:absolute}.resetpw{bottom:20px;color:#20a53a;position:absolute;right:40px}.Validform_checktip{color:#999;font-size:12px;height:24px;line-height:24px;overflow:hidden;padding:0 6px;position:absolute;right:10px;text-align:right;top:14px;z-index:1}.Validform_wrong{background:#e53451;border-radius:3px;color:#fff;float:right}.Validform_right{padding:0 10px;color:#71b83d;background:url(../images/right.png) no-repeat right center/16px auto}.main .login .line .Validform_error{border:#e53451 1px solid;box-shadow:0 0 2px #ea5d74}.main .login .yzm .Validform_wrong{background:#e53451;border-radius:3px;color:#fff;font-size:12px;height:24px;line-height:24px;overflow:hidden;padding:0 6px;position:absolute;right:127px;text-align:right;top:14px;z-index:1}.main .login .yzm .Validform_right{color:#71b83d;padding:0 10px;right:130px}.main .login .line .inputcode{width:208px}.zun-form{background:#fff none repeat scroll 0 0;border-radius:5px;color:#888;font:12px "Helvetica Neue",Helvetica,Arial,sans-serif;margin-left:auto;margin-right:auto;max-width:90%;padding:20px 10px;text-shadow:1px 1px 1px #fff}.zun-form-new{background:#FFF;padding:20px 0 0;font:12px "Helvetica Neue",Helvetica,Arial,sans-serif;color:#888}.zun-form-new .line{padding:5px 20px}.zun-form-new .line:first-child{padding-top:0}.zun-form-new .line:after{display:table;content:"";clear:both}.zun-form-new h1{font:25px "Helvetica Neue",Helvetica,Arial,sans-serif;padding:0 0 10px 40px;display:block;border-bottom:1px solid #dadada;margin:-10px -30px 30px -30px;color:#888}.zun-form-new h1>span{display:block;font-size:11px}.zun-form-new label{display:block;float:left;width:80px}.zun-form-new .info-r{margin-bottom:5px;margin-left:80px;position:relative;color:#666}.zun-form-new .info-r label{margin-bottom:0;padding-top:5px}.zun-form-new .info-r label input{margin-right:5px}.zun-form-new .info-r p{margin-top:10px;color:#999}.zun-form-new label>span{float:left;text-align:right;padding-right:10px;margin-top:8px;color:#565656;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-weight:bold}.zun-form-new input[type="text"],.zun-form-new input[type="email"],.zun-form-new input[type="password"],.zun-form-new input[type="number"],.zun-form-new textarea,.zun-form-new select,.webEdit-box input,.webEdit-box textarea,.webEdit-box select{border:1px solid #ccc;color:#888;height:30px;line-height:30px;margin-right:6px;margin-top:2px;outline:0 none;padding-left:5px;width:100%;border-radius:2px;-webkit-border-radius:2px;-moz-border-radius:2px;-webkit-transition:border-color ease-in-out .15s,-webkit-box-shadow ease-in-out .15s;-o-transition:border-color ease-in-out .15s,box-shadow ease-in-out .15s;transition:border-color ease-in-out .15s,box-shadow ease-in-out .15s}.zun-form-new input:focus,.zun-form-new input:active,.zun-form-new select:focus,.zun-form-new select:active,.dataTables_paginate select:focus,.dataTables_paginate select:active,textarea:focus,textarea:active,.webEdit-box input:focus,.webEdit-box select:focus,.webEdit-box input:active,.webEdit-box select:active,.dlt-text:focus,.sp-text:focus{border:#66afe9 1px solid;-webkit-box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 8px rgba(102,175,233,.6);box-shadow:inset 0 1px 1px rgba(0,0,0,.075),0 0 8px rgba(102,175,233,.6)}.zun-form-new select{height:32px;padding:0\9;margin:0\9}.zun-form-new textarea{height:100px;padding:5px 0 0 5px;width:80%}.zun-form-new .submit-btn,.getfile-btn{text-align:right;padding:7px 20px 10px;background:#f6f8f8;border-top:1px solid #edf1f2;margin-top:10px;clear:both}.zun-form-new .submit-btn button:first-child,.getfile-btn button:first-child{margin-right:4px}.zun-form-new .button{background:#FFF;border:1px solid #CCC;padding:10px 25px 10px 25px;color:#333;border-radius:0}.zun-form-new .button:hover{color:#333;background-color:#ebebeb;border-color:#adadad}.btn{display:inline-block;padding:5px 12px;margin-bottom:0;font-size:14px;font-weight:400;line-height:1.42857143;text-align:center;white-space:nowrap;vertical-align:middle;-ms-touch-action:manipulation;touch-action:manipulation;cursor:pointer;-webkit-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none;background-image:none;border:1px solid transparent;border-radius:3px}.btn-sm{border-radius:3px;font-size:12px;line-height:1.5;padding:4px 10px}.btn-danger{background-color:#cbcbcb;border-color:#cbcbcb;color:#fff}.btn-danger:focus,.btn-danger.focus{color:#fff;background-color:#c9302c;border-color:#761c19}.btn-danger:hover{color:#fff;background-color:#c9302c;border-color:#ac2925}.btn-danger:active,.btn-danger.active,.open>.dropdown-toggle.btn-danger{color:#fff;background-color:#c9302c;border-color:#ac2925}.btn-success{color:#fff;background-color:#5cb85c;border-color:#4cae4c}.btn-success:focus,.btn-success.focus{color:#fff;background-color:#449d44;border-color:#255625}.btn-success:hover{color:#fff;background-color:#449d44;border-color:#398439}.btn-success:active,.btn-success.active,.open>.dropdown-toggle.btn-success{color:#fff;background-color:#449d44;border-color:#398439}@media screen and (max-width:640px){body{background-color:#eee}.main .login .rlogo{background-color:rgba(0,0,0,0.8);color:#fff;font-size:20px;height:2.8em;line-height:2.8em;margin:0 -3.3% 25px -3.3%}.main .login{background-color:#eee;border-radius:0;height:330px;left:0;margin-left:0;margin-top:0;padding:0 3%;position:absolute;top:0;width:94%}.main .login .line .inputtxt{background-color:#fff;border:1px solid #d1d1d1;width:93.6%;padding:5px 3%;font-size:1.2em}.main .login .yzm .inputtxt{width:57%}}
\ No newline at end of file
+body {
+ background-color: #444;
+ font-family: 华文黑体, "Microsoft YaHei", 微软雅黑, STHei, "Helvetica Neue", Helvetica, Arial, sans-serif
+}
+
+input {
+ -webkit-appearance: none
+}
+
+.main .login {
+ background-color: #fff;
+ border-radius: 4px;
+ height: 290px;
+ left: 50%;
+ margin-left: -220px;
+ margin-top: -187.5px;
+ padding: 35px 40px 50px;
+ position: absolute;
+ top: 50%;
+ width: 360px;
+}
+
+.main .login .rlogo {
+ text-align: center;
+ font-size: 26px;
+ color: #444;
+ cursor: pointer;
+ height: 40px;
+ margin-bottom: 40px;
+ overflow: hidden
+}
+
+.main .login .title {
+ color: #444;
+ font-family: "微软雅黑";
+ font-size: 28px;
+ margin: -10px 0 46px;
+ text-align: center
+}
+
+.main .login .line {
+ height: 42px;
+ margin-bottom: 20px;
+ line-height: 42px;
+ position: relative
+}
+
+.main .login .line span {
+ width: 70px;
+ float: left;
+ color: #757584
+}
+
+.main .login .line .inputtxt {
+ background: transparent;
+ float: left;
+ width: 338px;
+ height: 30px;
+ line-height: 30px;
+ border: #d1d1d1 1px solid;
+ padding: 5px 10px;
+ position: relative;
+ z-index: 1;
+ box-shadow: 0 3px 5px -4px rgba(0, 0, 0, 0.4) inset, -1px 0 3px -2px rgba(0, 0, 0, 0.1) inset;
+ border-radius: 3px
+}
+
+.main .login .line .code {
+ width: 230px
+}
+
+.main .login .line .getcode,
+.main .login .line .picgetcode {
+ width: 100px;
+ text-align: center;
+ height: 38px;
+ line-height: 38px;
+ border: #cacad9 1px solid;
+ background-color: #dbdbea;
+ color: #757584;
+ font-size: 14px;
+ float: left;
+ margin-left: 17px;
+ cursor: pointer
+}
+
+.main .login .line .getcode.no-drop {
+ cursor: no-drop
+}
+
+.main .login .line .getcode.pointer {
+ border: #47acff 1px solid;
+ background-color: #47acff;
+ color: #fff
+}
+
+.main .login .line .picgetcode img {
+ width: 100%;
+ height: 100%
+}
+
+.main .login .line .codemsg {
+ background: #e53451;
+ border-radius: 3px;
+ color: #fff;
+ font-size: 12px;
+ height: 21px;
+ line-height: 22px;
+ overflow: hidden;
+ padding: 0 6px;
+ position: absolute;
+ right: 127px;
+ text-align: right;
+ top: 5px;
+ z-index: 0
+}
+
+.main .login .line .inputtxt:focus {
+ border: #70c3ec 1px solid;
+ box-shadow: 0 0 5px #eee
+}
+
+.main .login .yzm .inputtxt {
+ width: 64%
+}
+
+.login_btn input {
+ width: 100%;
+ height: 44px;
+ background: #20a53a;
+ background: linear-gradient(#20a53a, #189d32);
+ box-shadow: inset 0 1px 2px #30ad42;
+ color: #fff;
+ text-shadow: #00851a 0 -1px 0;
+ border: 1px solid #20a53a;
+ text-align: center;
+ font-size: 16px;
+ color: #fff;
+ border-radius: 3px;
+ cursor: pointer
+}
+
+.login_btn input:hover {
+ background: #10952a;
+ background: linear-gradient(#10952a, #088d22);
+ border: 1px solid #10952a
+}
+
+.login-link {
+ text-align: right;
+ margin-top: 22px
+}
+
+.login-link span {
+ color: #cacad9;
+ padding: 0 8px;
+ text-align: center
+}
+
+.login-link a {
+ color: #757584
+}
+
+.copyright {
+ color: #9d9d9d;
+ text-align: center;
+ margin-top: -50px;
+ position: relative;
+ z-index: 3
+}
+
+.copyright a {
+ color: #9d9d9d;
+ text-decoration: none
+}
+
+.pwinfo {
+ bottom: 19px;
+ color: #999;
+ position: absolute
+}
+
+.resetpw {
+ bottom: 20px;
+ color: #20a53a;
+ position: absolute;
+ right: 40px
+}
+
+.Validform_checktip {
+ color: #999;
+ font-size: 12px;
+ height: 24px;
+ line-height: 24px;
+ overflow: hidden;
+ padding: 0 6px;
+ position: absolute;
+ right: 10px;
+ text-align: right;
+ top: 14px;
+ z-index: 1
+}
+
+.Validform_wrong {
+ background: #e53451;
+ border-radius: 3px;
+ color: #fff;
+ float: right
+}
+
+.Validform_right {
+ padding: 0 10px;
+ color: #71b83d;
+ background: url(../images/right.png) no-repeat right center/16px auto
+}
+
+.main .login .line .Validform_error {
+ border: #e53451 1px solid;
+ box-shadow: 0 0 2px #ea5d74
+}
+
+.main .login .yzm .Validform_wrong {
+ background: #e53451;
+ border-radius: 3px;
+ color: #fff;
+ font-size: 12px;
+ height: 24px;
+ line-height: 24px;
+ overflow: hidden;
+ padding: 0 6px;
+ position: absolute;
+ right: 127px;
+ text-align: right;
+ top: 14px;
+ z-index: 1
+}
+
+.main .login .yzm .Validform_right {
+ color: #71b83d;
+ padding: 0 10px;
+ right: 130px
+}
+
+.main .login .line .inputcode {
+ width: 208px
+}
+
+.zun-form {
+ background: #fff none repeat scroll 0 0;
+ border-radius: 5px;
+ color: #888;
+ font: 12px "Helvetica Neue", Helvetica, Arial, sans-serif;
+ margin-left: auto;
+ margin-right: auto;
+ max-width: 90%;
+ padding: 20px 10px;
+ text-shadow: 1px 1px 1px #fff
+}
+
+.zun-form-new {
+ background: #FFF;
+ padding: 20px 0 0;
+ font: 12px "Helvetica Neue", Helvetica, Arial, sans-serif;
+ color: #888
+}
+
+.zun-form-new .line {
+ padding: 5px 20px
+}
+
+.zun-form-new .line:first-child {
+ padding-top: 0
+}
+
+.zun-form-new .line:after {
+ display: table;
+ content: "";
+ clear: both
+}
+
+.zun-form-new h1 {
+ font: 25px "Helvetica Neue", Helvetica, Arial, sans-serif;
+ padding: 0 0 10px 40px;
+ display: block;
+ border-bottom: 1px solid #dadada;
+ margin: -10px -30px 30px -30px;
+ color: #888
+}
+
+.zun-form-new h1>span {
+ display: block;
+ font-size: 11px
+}
+
+.zun-form-new label {
+ display: block;
+ float: left;
+ width: 80px
+}
+
+.zun-form-new .info-r {
+ margin-bottom: 5px;
+ margin-left: 80px;
+ position: relative;
+ color: #666
+}
+
+.zun-form-new .info-r label {
+ margin-bottom: 0;
+ padding-top: 5px
+}
+
+.zun-form-new .info-r label input {
+ margin-right: 5px
+}
+
+.zun-form-new .info-r p {
+ margin-top: 10px;
+ color: #999
+}
+
+.zun-form-new label>span {
+ float: left;
+ text-align: right;
+ padding-right: 10px;
+ margin-top: 8px;
+ color: #565656;
+ font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
+ font-weight: bold
+}
+
+.zun-form-new input[type="text"],
+.zun-form-new input[type="email"],
+.zun-form-new input[type="password"],
+.zun-form-new input[type="number"],
+.zun-form-new textarea,
+.zun-form-new select,
+.webEdit-box input,
+.webEdit-box textarea,
+.webEdit-box select {
+ border: 1px solid #ccc;
+ color: #888;
+ height: 30px;
+ line-height: 30px;
+ margin-right: 6px;
+ margin-top: 2px;
+ outline: 0 none;
+ padding-left: 5px;
+ width: 100%;
+ border-radius: 2px;
+ -webkit-border-radius: 2px;
+ -moz-border-radius: 2px;
+ -webkit-transition: border-color ease-in-out .15s, -webkit-box-shadow ease-in-out .15s;
+ -o-transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s;
+ transition: border-color ease-in-out .15s, box-shadow ease-in-out .15s
+}
+
+.zun-form-new input:focus,
+.zun-form-new input:active,
+.zun-form-new select:focus,
+.zun-form-new select:active,
+.dataTables_paginate select:focus,
+.dataTables_paginate select:active,
+textarea:focus,
+textarea:active,
+.webEdit-box input:focus,
+.webEdit-box select:focus,
+.webEdit-box input:active,
+.webEdit-box select:active,
+.dlt-text:focus,
+.sp-text:focus {
+ border: #66afe9 1px solid;
+ -webkit-box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 8px rgba(102, 175, 233, .6);
+ box-shadow: inset 0 1px 1px rgba(0, 0, 0, .075), 0 0 8px rgba(102, 175, 233, .6)
+}
+
+.zun-form-new select {
+ height: 32px;
+ padding: 0\9;
+ margin: 0\9
+}
+
+.zun-form-new textarea {
+ height: 100px;
+ padding: 5px 0 0 5px;
+ width: 80%
+}
+
+.zun-form-new .submit-btn,
+.getfile-btn {
+ text-align: right;
+ padding: 7px 20px 10px;
+ background: #f6f8f8;
+ border-top: 1px solid #edf1f2;
+ margin-top: 10px;
+ clear: both
+}
+
+.zun-form-new .submit-btn button:first-child,
+.getfile-btn button:first-child {
+ margin-right: 4px
+}
+
+.zun-form-new .button {
+ background: #FFF;
+ border: 1px solid #CCC;
+ padding: 10px 25px 10px 25px;
+ color: #333;
+ border-radius: 0
+}
+
+.zun-form-new .button:hover {
+ color: #333;
+ background-color: #ebebeb;
+ border-color: #adadad
+}
+
+.btn {
+ display: inline-block;
+ padding: 5px 12px;
+ margin-bottom: 0;
+ font-size: 14px;
+ font-weight: 400;
+ line-height: 1.42857143;
+ text-align: center;
+ white-space: nowrap;
+ vertical-align: middle;
+ -ms-touch-action: manipulation;
+ touch-action: manipulation;
+ cursor: pointer;
+ -webkit-user-select: none;
+ -moz-user-select: none;
+ -ms-user-select: none;
+ user-select: none;
+ background-image: none;
+ border: 1px solid transparent;
+ border-radius: 3px
+}
+
+.btn-sm {
+ border-radius: 3px;
+ font-size: 12px;
+ line-height: 1.5;
+ padding: 4px 10px
+}
+
+.btn-danger {
+ background-color: #cbcbcb;
+ border-color: #cbcbcb;
+ color: #fff
+}
+
+.btn-danger:focus,
+.btn-danger.focus {
+ color: #fff;
+ background-color: #c9302c;
+ border-color: #761c19
+}
+
+.btn-danger:hover {
+ color: #fff;
+ background-color: #c9302c;
+ border-color: #ac2925
+}
+
+.btn-danger:active,
+.btn-danger.active,
+.open>.dropdown-toggle.btn-danger {
+ color: #fff;
+ background-color: #c9302c;
+ border-color: #ac2925
+}
+
+.btn-success {
+ color: #fff;
+ background-color: #5cb85c;
+ border-color: #4cae4c
+}
+
+.btn-success:focus,
+.btn-success.focus {
+ color: #fff;
+ background-color: #449d44;
+ border-color: #255625
+}
+
+.btn-success:hover {
+ color: #fff;
+ background-color: #449d44;
+ border-color: #398439
+}
+
+.btn-success:active,
+.btn-success.active,
+.open>.dropdown-toggle.btn-success {
+ color: #fff;
+ background-color: #449d44;
+ border-color: #398439
+}
+
+@media screen and (max-width:640px) {
+ body {
+ background-color: #eee
+ }
+
+ .main .login .rlogo {
+ background-color: rgba(0, 0, 0, 0.8);
+ color: #fff;
+ font-size: 20px;
+ height: 2.8em;
+ line-height: 2.8em;
+ margin: 0 -3.3% 25px -3.3%
+ }
+
+ .main .login {
+ background-color: #eee;
+ border-radius: 0;
+ height: 330px;
+ left: 0;
+ margin-left: 0;
+ margin-top: 0;
+ padding: 0 3%;
+ position: absolute;
+ top: 0;
+ width: 94%
+ }
+
+ .main .login .line .inputtxt {
+ background-color: #fff;
+ border: 1px solid #d1d1d1;
+ width: 93.6%;
+ padding: 5px 3%;
+ font-size: 1.2em
+ }
+
+ .main .login .yzm .inputtxt {
+ width: 57%
+ }
+}
+
+@media screen and (max-width:640px) {
+ .scanCode {
+ display: none
+ }
+
+ .account {
+ display: block !important
+ }
+
+ .bg_img {
+ display: none
+ }
+}
+
+.bg_img {
+ position: absolute;
+ top: 5px;
+ right: 5px;
+ width: 60px;
+ height: 60px;
+ background-image: url(/static/img/qrCode.png);
+ background-position: 0 0;
+ background-size: 120px 120px;
+ background-repeat: no-repeat
+}
+
+.bg_img:hover {
+ background-position: -60px 0;
+ cursor: pointer
+}
+
+.bg_img.pc {
+ background-position: 0 -60px
+}
+
+.bg_img.pc:hover {
+ background-position: -60px -60px
+}
+
+.qrCode {
+ padding-top: 20px;
+ text-align: center
+}
+
+.scanCode .titles {
+ margin-top: 25px;
+ color: #444;
+ text-align: center;
+ font-size: 23px
+}
+
+.list_scan {
+ margin: 0 auto;
+ margin-top: 15px;
+ width: 150px
+}
+
+.list_scan>img {
+ float: left;
+ margin-right: 15px;
+ width: 40px;
+ height: 40px
+}
+
+.list_scan span {
+ display: inline-block;
+ margin-bottom: 5px;
+ font-size: 13px
+}
+
+.list_scan a:hover .weChatSamll {
+ display: block
+}
+
+.list_scan .weChatSamll {
+ position: absolute;
+ right: 80px;
+ bottom: 105px;
+ display: none;
+ padding: 20px;
+ width: 150px;
+ height: 150px;
+ border: 1px solid #ececec;
+ border-radius: 5px;
+ background-color: #fff
+}
+
+.list_scan .weChatSamll img {
+ width: 100%
+}
+
+.list_scan .weChatSamll em {
+ position: absolute;
+ right: 87px;
+ bottom: -14px;
+ margin-left: -6px;
+ width: 0;
+ height: 0;
+ border: 7px solid #ececec;
+ border-color: #ececec transparent transparent transparent
+}
+
+.tips {
+ position: absolute;
+ top: 10px;
+ right: 65px;
+ padding: 5px 10px;
+ width: 115px;
+ border-radius: 4px;
+ background: #dff0d8;
+ color: #20a53a;
+ text-align: center
+}
+
+.tips em {
+ position: absolute;
+ top: 8px;
+ right: -11px;
+ margin-left: -6px;
+ width: 0;
+ height: 0;
+ border: 6px solid #dff0d8;
+ border-color: transparent transparent transparent #dff0d8
+}
+
+.tips img {
+ margin-top: -1px;
+ margin-right: 4px;
+ width: 16px;
+ height: 16px;
+ vertical-align: middle
+}
+
+.main .login .rlogo {
+ margin-top: 15px;
+ margin-bottom: 25px
+}
+
+.verification_view {
+ padding: 30px 40px;
+}
+.verification_view .v_title{
+ text-align: center;
+ font-size: 21px;
+ color: #444;
+ height: 40px;
+ margin-bottom: 15px;
+ overflow: hidden;
+}
+
+.verification_view .v_btn input{
+ width: 100%;
+ height: 40px;
+ font-size: 15px;
+ background: #20a53a;
+ background: linear-gradient(#20a53a,#189d32);
+ box-shadow: inset 0 1px 2px #30ad42;
+ color: #fff;
+ text-shadow: #00851a 0 -1px 0;
+ border: 1px solid #20a53a;
+ text-align: center;
+ color: #fff;
+ border-radius: 3px;
+ cursor: pointer;
+}
+.verification_view .v_input input{
+ width: 100%;
+ height: 40px;
+ line-height: 30px;
+ border: #d1d1d1 1px solid;
+ padding: 4px 10px;
+ z-index: 1;
+ box-shadow: 0 3px 5px -4px rgba(0, 0, 0, 0.4) inset, -1px 0 3px -2px rgba(0, 0, 0, 0.1) inset;
+ border-radius: 3px;
+ outline: none;
+ box-sizing: border-box;
+}
+.verification_view .v_input{
+ margin-bottom: 15px;
+}
+.verification_view .v_tips{
+ color: #777;
+ font-size: 13px;
+ margin-top: 18px;
+}
\ No newline at end of file
diff --git a/BTPanel/static/css/site.css b/BTPanel/static/css/site.css
index 114192af..baf1caf1 100644
--- a/BTPanel/static/css/site.css
+++ b/BTPanel/static/css/site.css
@@ -5233,6 +5233,13 @@ select[disabled]{
margin-bottom:0;
}
+.aceEditors{
+ border: 1px solid rgb(208, 207, 207);
+ overflow: hidden;
+}
+#ace_conter.chrome{
+ background: #ececec;
+}
#ace_conter{
background: #444;
height: 100%;
diff --git a/BTPanel/static/js/config.js b/BTPanel/static/js/config.js
index 50b5f990..a261d354 100644
--- a/BTPanel/static/js/config.js
+++ b/BTPanel/static/js/config.js
@@ -22,9 +22,175 @@ function SetPanelAutoUpload(){
}
+
+
+$('#panel_verification').click(function(){
+ var _checked = $(this).prop('checked');
+ if(_checked){
+ layer.open({
+ type: 1,
+ area: ['600px','420px'],
+ title: 'Google authentication binding',
+ closeBtn: 2,
+ shift: 5,
+ shadeClose: false,
+ content: '
',
+ success:function(layers,index){
+ $('.submit_verify').click(function(e){
+ var check_verification = $('#check_verification').prop('checked');
+ if(!check_verification){
+ layer.msg('Please check the consent risk first.',{icon:0});
+ return false;
+ }
+ var loadT = layer.msg('Opening Google authentication, please wait...', { icon: 16, time: 0, shade: [0.3, '#000'] });
+ set_two_step_auth({act:_checked},function(rdata){
+ layer.close(loadT);
+ if (rdata.status) layer.closeAll();
+ layer.msg(rdata.msg, { icon: rdata.status ? 1 : 2 });
+ if(rdata.status && _checked){
+ $('.open_two_verify_view').click();
+ }
+ });
+ });
+ $('.close_verify').click(function(){
+ layer.closeAll();
+ $('#panel_verification').prop('checked',!_checked);
+ });
+ },cancel:function () {
+ layer.closeAll();
+ $('#panel_verification').prop('checked',!_checked);
+ }
+ });
+ }else{
+ bt.confirm({
+ title: 'Google authentication',
+ msg: 'Turn off Google authentication, do you want to continue?',
+ cancel: function () {
+ $('#panel_verification').prop('checked',!_checked);
+ }}, function () {
+ var loadT = layer.msg('Google authentication is being turned off, please wait...', { icon: 16, time: 0, shade: [0.3, '#000'] });
+ set_two_step_auth({act:_checked},function(rdata){
+ layer.close(loadT);
+ if (rdata.status) layer.closeAll();
+ layer.msg(rdata.msg, { icon: rdata.status ? 1 : 2 });
+ if(rdata.status && _checked){
+ $('.open_two_verify_view').click();
+ }
+ });
+ },function () {
+ $('#panel_verification').prop('checked',!_checked);
+ });
+ }
+
+ // console.log(_data);
+
+});
+
+$('.open_two_verify_view').click(function(){
+ var _checked = $('#panel_verification').prop('checked');
+ if(!_checked){
+ layer.msg('Please turn on Google authentication first.',{icon:0});
+ return false;
+ }
+ layer.open({
+ type: 1,
+ area: ['600px','670px'],
+ title: 'Google authentication binding',
+ closeBtn: 2,
+ shift: 5,
+ shadeClose: false,
+ content: '',
+ success:function(e){
+ get_two_verify(function(res){
+ $('.verify_box_line .username').html(res.username);
+ $('.verify_box_line .userkey').html(res.key);
+ });
+ get_qrcode_data(function(res){
+ jQuery('#verify_qrcode').qrcode({
+ render: "canvas",
+ text: res,
+ height:150,
+ width:150
+ });
+ });
+ }
+ });
+});
+
+(function(){
+ check_two_step(function(res){
+ $('#panel_verification').prop('checked',res.status);
+ });
+})()
+function get_qrcode_data(callback){
+ $.post('/config?action=get_qrcode_data',function(res){
+ if(callback) callback(res);
+ });
+}
+
+function check_two_step(callback){
+ $.post('/config?action=check_two_step',function(res){
+ if(callback) callback(res);
+ });
+}
+
+
+
+function get_two_verify(callback){
+ $.post('/config?action=get_key',function(res){
+ if(callback) callback(res);
+ });
+}
+function set_two_step_auth(obj,callback){
+ $.post('/config?action=set_two_step_auth',{act:obj.act?1:0},function(res){
+ if(callback) callback(res);
+ });
+}
+
$(".set-submit").click(function(){
var data = $("#set-Config").serialize();
- console.log(data)
layer.msg(lan.config.config_save,{icon:16,time:0,shade: [0.3, '#000']});
$.post('/config?action=setPanel',data,function(rdata){
layer.closeAll();
@@ -35,7 +201,7 @@ $(".set-submit").click(function(){
},1500);
}
});
-
+
});
@@ -62,8 +228,8 @@ function modify_auth_path() {
})
-
-
+
+
}
@@ -100,7 +266,7 @@ function Set502(){
$.post('/config?action=Set502','',function(rdata){
layer.close(loadT);
layer.msg(rdata.msg,{icon:rdata.status?1:2});
- });
+ });
}
//绑定修改宝塔账号
@@ -153,7 +319,7 @@ function apiSetup(){
var loadT = layer.msg(lan.config.token_get,{icon:16,time:0,shade: [0.3, '#000']});
$.get('/api?action=GetToken',function(rdata){
layer.close(loadT);
-
+
});
}
@@ -496,7 +662,7 @@ var weChat = {
});
},
}
-
+
function open_wxapp(){
var rhtml = '\
\
@@ -515,7 +681,7 @@ function open_wxapp(){
\
\
'
-
+
layer.open({
type: 1,
title: lan.config.bind_wechat,
@@ -524,7 +690,7 @@ function open_wxapp(){
shadeClose: false,
content:rhtml
});
-
+
weChat.init();
}
@@ -615,7 +781,7 @@ function SetPanelApi(t_type) {
return;
}
}
-
+
layer.msg(rdata.msg, { icon: rdata.status ? 1 : 2 });
if (rdata.msg == lan.config.open_successfully) {
GetPanelApi();
@@ -657,19 +823,19 @@ function modify_basic_auth() {
if (rdata.open) {
show_basic_auth(rdata);
} else {
- m_html = ''
- + '
Danger! This feature does not know how to open! '
- + '
'
+ var loadT = layer.confirm(m_html, { title: "Risk reminder", area: "600px",closeBtn:2 }, function () {
if (!$("#check_basic").prop("checked")) {
layer.msg("Please read the precautions carefully and check to agree to take risks!");
setTimeout(function () { modify_basic_auth();},3000)
@@ -683,7 +849,6 @@ function modify_basic_auth() {
});
}
-
function show_basic_auth(rdata) {
layer.open({
type: 1,
diff --git a/BTPanel/static/js/crontab.js b/BTPanel/static/js/crontab.js
index 733c31b1..9b450fd1 100644
--- a/BTPanel/static/js/crontab.js
+++ b/BTPanel/static/js/crontab.js
@@ -64,7 +64,7 @@ function getCronData(){
"+s_status+" \
"+rdata[i].type+" \
"+rdata[i].cycle+" \
- "+(rdata[i].save?rdata[i].save+'份':'-')+" \
+ "+(rdata[i].save?rdata[i].save:'-')+" \
"+optName+" \
"+rdata[i].addtime+" \
\
diff --git a/BTPanel/static/js/database.js b/BTPanel/static/js/database.js
index e45ccb64..c0c17810 100644
--- a/BTPanel/static/js/database.js
+++ b/BTPanel/static/js/database.js
@@ -419,5 +419,15 @@ var database = {
});
}, 100)
})
+ },
+ remove_input_file: function (fileName,name) {
+ layer.confirm(lan.get('recycle_bin_confirm', [fileName]), { title: lan.files.del_file, closeBtn: 2, icon: 3 }, function (index) {
+ layer.msg(lan.public.the, { icon: 16, time: 0, shade: [0.3, '#000'] });
+ $.post('/files?action=DeleteFile', 'path=' + encodeURIComponent(fileName), function (rdata) {
+ layer.close(index);
+ layer.msg(rdata.msg, { icon: rdata.status ? 1 : 2 });
+ database.input_database(name);
+ });
+ });
}
}
\ No newline at end of file
diff --git a/BTPanel/static/js/public_backup.js b/BTPanel/static/js/public_backup.js
index a5c0d262..9b6be90d 100644
--- a/BTPanel/static/js/public_backup.js
+++ b/BTPanel/static/js/public_backup.js
@@ -3808,10 +3808,17 @@ bt.soft = {
if(type == undefined) type = 0;
if(search == undefined) search = '';
var force = bt.get_cookie('force');
- if(force == undefined) force = 0;
- var loading = bt.load(lan.public.the,1);
- bt.send('get_soft_list','plugin/get_soft_list',{p:p,type:type,tojs:'soft.get_list',force:force,query:search},function(rdata){
- loading.close();
+ if (force == undefined) force = 0;
+ p = p + ''
+ if (p.indexOf('not_load') == -1) {
+ var loading = bt.load(lan.public.the, 1);
+ } else {
+ var loading = null;
+ p = p.split("not_load")[0];
+ }
+
+ bt.send('get_soft_list', 'plugin/get_soft_list', { p: p, type: type, tojs: 'soft.get_list', force: force, query: search }, function (rdata) {
+ if (loading) loading.close();
bt.set_cookie('force',0);
if(callback) callback(rdata);
})
diff --git a/BTPanel/static/js/soft.js b/BTPanel/static/js/soft.js
index 58c4edb4..b36c352b 100644
--- a/BTPanel/static/js/soft.js
+++ b/BTPanel/static/js/soft.js
@@ -1,5 +1,6 @@
var soft = {
- get_list: function(page, type, search) {
+ is_install: false,
+ get_list: function (page, type, search) {
if (page == undefined) page = 0;
if (type == undefined) type = 0;
@@ -14,9 +15,8 @@ var soft = {
soft.get_dep_list(1)
return;
}
-
+ soft.is_install = false;
bt.soft.get_soft_list(page, type, search, function (rdata) {
-
if (rdata.pro >= 0) {
$("#updata_pro_info").html('');
} else if (rdata.pro === -2) {
@@ -228,7 +228,7 @@ var soft = {
}
}
else {
- if (item.setup) {
+ if (item.setup && item.task == '1') {
if (pay_opt == '') {
if (item.versions.length > 1) {
for (var i = 0; i < item.versions.length; i++) {
@@ -258,9 +258,15 @@ var soft = {
}
else if (item.task == '-1') {
option = '' + lan.soft.installing + ' ';
+ soft.is_install = true;
}
else if (item.task == '0') {
option = '' + lan.soft.wait_install + ' ';
+ soft.is_install = true;
+ }
+ else if (item.task == '-2') {
+ option = 'Updating ';
+ soft.is_install = true;
}
else {
if (pay_opt) {
@@ -277,6 +283,14 @@ var soft = {
],
data: data
})
+ bt.set_cookie('load_page', (page+'').split('not_load')[0])
+ bt.set_cookie('load_type', type)
+ bt.set_cookie('load_search', search)
+ if (soft.is_install) {
+ setTimeout(function () {
+ soft.get_list(page + 'not_load', type, search);
+ }, 3000);
+ }
})
},
get_dep_list: function (p) {
diff --git a/BTPanel/static/language/Simplified_Chinese/template.json b/BTPanel/static/language/Simplified_Chinese/template.json
index 214472cb..cfb99f3f 100644
--- a/BTPanel/static/language/Simplified_Chinese/template.json
+++ b/BTPanel/static/language/Simplified_Chinese/template.json
@@ -346,7 +346,7 @@
"N3":"Account",
"N4":"Password",
"N5":"Password",
- "N6":"Passowrd",
+ "N6":"Password",
"N7":"Verification Code",
"N8":"Verification Code Error",
"N9":"Verification Error",
diff --git a/BTPanel/static/layer/layer.js b/BTPanel/static/layer/layer.js
index 2755658e..22d36376 100644
--- a/BTPanel/static/layer/layer.js
+++ b/BTPanel/static/layer/layer.js
@@ -1,2 +1,2 @@
/*! layer-v3.0.1 Web弹层组件 MIT License http://layer.layui.com/ By 贤心 */
- ;!function(e,t){"use strict";var i,n,a=e.layui&&layui.define,o={getPath:function(){var e=document.scripts,t=e[e.length-1],i=t.src;if(!t.getAttribute("merge"))return i.substring(0,i.lastIndexOf("/")+1)}(),config:{},end:{},minIndex:0,minLeft:[],btn:["OK","Canel"],type:["dialog","page","iframe","loading","tips"]},r={v:"3.0.1",ie:function(){var t=navigator.userAgent.toLowerCase();return!!(e.ActiveXObject||"ActiveXObject"in e)&&((t.match(/msie\s(\d+)/)||[])[1]||"11")}(),index:e.layer&&e.layer.v?1e5:0,path:o.getPath,config:function(e,t){return e=e||{},r.cache=o.config=i.extend({},o.config,e),r.path=o.config.path||r.path,"string"==typeof e.extend&&(e.extend=[e.extend]),o.config.path&&r.ready(),e.extend?(a?layui.addcss("modules/layer/"+e.extend):r.link("skin/"+e.extend),this):this},link:function(t,n,a){if(r.path){var o=i("head")[0],l=document.createElement("link");"string"==typeof n&&(a=n);var s=(a||t).replace(/\.|\//g,""),f="layuicss-"+s,c=0;l.rel="stylesheet",l.href=r.path+t,l.id=f,i("#"+f)[0]||o.appendChild(l),"function"==typeof n&&!function d(){return++c>80?e.console&&console.error("layer.css: Invalid"):void(1989===parseInt(i("#"+f).css("width"))?n():setTimeout(d,100))}()}},ready:function(e){var t="skinlayercss",i="1110";return a?layui.addcss("modules/layer/default/layer.css?v="+r.v+i,e,t):r.link("skin/default/layer.css?v="+r.v+i,e,t),this},alert:function(e,t,n){var a="function"==typeof t;return a&&(n=t),r.open(i.extend({content:e,yes:n},a?{}:t))},confirm:function(e,t,n,a){var l="function"==typeof t;return l&&(a=n,n=t),r.open(i.extend({content:e,btn:o.btn,yes:n,btn2:a},l?{}:t))},msg:function(e,n,a){var l="function"==typeof n,f=o.config.skin,c=(f?f+" "+f+"-msg":"")||"layui-layer-msg",d=s.anim.length-1;return l&&(a=n),r.open(i.extend({content:e,time:3e3,shade:!1,skin:c,title:!1,closeBtn:!1,btn:!1,resize:!1,end:a},l&&!o.config.skin?{skin:c+" layui-layer-hui",anim:d}:function(){return n=n||{},(n.icon===-1||n.icon===t&&!o.config.skin)&&(n.skin=c+" "+(n.skin||"layui-layer-hui")),n}()))},load:function(e,t){return r.open(i.extend({type:3,icon:e||0,resize:!1,shade:.01},t))},tips:function(e,t,n){return r.open(i.extend({type:4,content:[e,t],closeBtn:!1,time:3e3,shade:!1,resize:!1,fixed:!1,maxWidth:210},n))}},l=function(e){var t=this;t.index=++r.index,t.config=i.extend({},t.config,o.config,e),document.body?t.creat():setTimeout(function(){t.creat()},50)};l.pt=l.prototype;var s=["layui-layer",".layui-layer-title",".layui-layer-main",".layui-layer-dialog","layui-layer-iframe","layui-layer-content","layui-layer-btn","layui-layer-close"];s.anim=["layer-anim","layer-anim-01","layer-anim-02","layer-anim-03","layer-anim-04","layer-anim-05","layer-anim-06"],l.pt.config={type:0,shade:.3,fixed:!0,move:s[1],title:"信息",offset:"auto",area:"auto",closeBtn:1,time:0,zIndex:19891014,maxWidth:360,anim:0,icon:-1,moveType:1,resize:!0,scrollbar:!0,tips:2},l.pt.vessel=function(e,t){var n=this,a=n.index,r=n.config,l=r.zIndex+a,f="object"==typeof r.title,c=r.maxmin&&(1===r.type||2===r.type),d=r.title?''+(f?r.title[0]:r.title)+"
":"";return r.zIndex=l,t([r.shade?'
':"",''+(e&&2!=r.type?"":d)+'
'+(0==r.type&&r.icon!==-1?' ':"")+(1==r.type&&e?"":r.content||"")+'
'+function(){var e=c?' ':"";return r.closeBtn&&(e+=' '),e}()+" "+(r.btn?function(){var e="";"string"==typeof r.btn&&(r.btn=[r.btn]);for(var t=0,i=r.btn.length;t
'+r.btn[t]+"";return' '+e+"
"}():"")+(r.resize?' ':"")+" "],d,i('
')),n},l.pt.creat=function(){var e=this,t=e.config,a=e.index,l=t.content,f="object"==typeof l,c=i("body");if(!i("#"+t.id)[0]){switch("string"==typeof t.area&&(t.area="auto"===t.area?["",""]:[t.area,""]),t.shift&&(t.anim=t.shift),6==r.ie&&(t.fixed=!1),t.type){case 0:t.btn="btn"in t?t.btn:o.btn[0],r.closeAll("dialog");break;case 2:var l=t.content=f?t.content:[t.content||"http://layer.layui.com","auto"];t.content='';break;case 3:delete t.title,delete t.closeBtn,t.icon===-1&&0===t.icon,r.closeAll("loading");break;case 4:f||(t.content=[t.content,"body"]),t.follow=t.content[1],t.content=t.content[0]+' ',delete t.title,t.tips="object"==typeof t.tips?t.tips:[t.tips,!0],t.tipsMore||r.closeAll("tips")}e.vessel(f,function(n,r,d){c.append(n[0]),f?function(){2==t.type||4==t.type?function(){i("body").append(n[1])}():function(){l.parents("."+s[0])[0]||(l.data("display",l.css("display")).show().addClass("layui-layer-wrap").wrap(n[1]),i("#"+s[0]+a).find("."+s[5]).before(r))}()}():c.append(n[1]),i(".layui-layer-move")[0]||c.append(o.moveElem=d),e.layero=i("#"+s[0]+a),t.scrollbar||s.html.css("overflow","hidden").attr("layer-full",a)}).auto(a),2==t.type&&6==r.ie&&e.layero.find("iframe").attr("src",l[0]),4==t.type?e.tips():e.offset(),t.fixed&&n.on("resize",function(){e.offset(),(/^\d+%$/.test(t.area[0])||/^\d+%$/.test(t.area[1]))&&e.auto(a),4==t.type&&e.tips()}),t.time<=0||setTimeout(function(){r.close(e.index)},t.time),e.move().callback(),s.anim[t.anim]&&e.layero.addClass(s.anim[t.anim]).data("anim",!0)}},l.pt.auto=function(e){function t(e){e=l.find(e),e.height(f[1]-c-d-2*(0|parseFloat(e.css("padding"))))}var a=this,o=a.config,l=i("#"+s[0]+e);""===o.area[0]&&o.maxWidth>0&&(r.ie&&r.ie<8&&o.btn&&l.width(l.innerWidth()),l.outerWidth()>o.maxWidth&&l.width(o.maxWidth));var f=[l.innerWidth(),l.innerHeight()],c=l.find(s[1]).outerHeight()||0,d=l.find("."+s[6]).outerHeight()||0;switch(o.type){case 2:t("iframe");break;default:""===o.area[1]?o.fixed&&f[1]>=n.height()&&(f[1]=n.height(),t("."+s[5])):t("."+s[5])}return a},l.pt.offset=function(){var e=this,t=e.config,i=e.layero,a=[i.outerWidth(),i.outerHeight()],o="object"==typeof t.offset;e.offsetTop=(n.height()-a[1])/2,e.offsetLeft=(n.width()-a[0])/2,o?(e.offsetTop=t.offset[0],e.offsetLeft=t.offset[1]||e.offsetLeft):"auto"!==t.offset&&("t"===t.offset?e.offsetTop=0:"r"===t.offset?e.offsetLeft=n.width()-a[0]:"b"===t.offset?e.offsetTop=n.height()-a[1]:"l"===t.offset?e.offsetLeft=0:"lt"===t.offset?(e.offsetTop=0,e.offsetLeft=0):"lb"===t.offset?(e.offsetTop=n.height()-a[1],e.offsetLeft=0):"rt"===t.offset?(e.offsetTop=0,e.offsetLeft=n.width()-a[0]):"rb"===t.offset?(e.offsetTop=n.height()-a[1],e.offsetLeft=n.width()-a[0]):e.offsetTop=t.offset),t.fixed||(e.offsetTop=/%$/.test(e.offsetTop)?n.height()*parseFloat(e.offsetTop)/100:parseFloat(e.offsetTop),e.offsetLeft=/%$/.test(e.offsetLeft)?n.width()*parseFloat(e.offsetLeft)/100:parseFloat(e.offsetLeft),e.offsetTop+=n.scrollTop(),e.offsetLeft+=n.scrollLeft()),i.attr("minLeft")&&(e.offsetTop=n.height()-(i.find(s[1]).outerHeight()||0),e.offsetLeft=i.css("left")),i.css({top:e.offsetTop,left:e.offsetLeft})},l.pt.tips=function(){var e=this,t=e.config,a=e.layero,o=[a.outerWidth(),a.outerHeight()],r=i(t.follow);r[0]||(r=i("body"));var l={width:r.outerWidth(),height:r.outerHeight(),top:r.offset().top,left:r.offset().left},f=a.find(".layui-layer-TipsG"),c=t.tips[0];t.tips[1]||f.remove(),l.autoLeft=function(){l.left+o[0]-n.width()>0?(l.tipLeft=l.left+l.width-o[0],f.css({right:12,left:"auto"})):l.tipLeft=l.left},l.where=[function(){l.autoLeft(),l.tipTop=l.top-o[1]-10,f.removeClass("layui-layer-TipsB").addClass("layui-layer-TipsT").css("border-right-color",t.tips[1])},function(){l.tipLeft=l.left+l.width+10,l.tipTop=l.top,f.removeClass("layui-layer-TipsL").addClass("layui-layer-TipsR").css("border-bottom-color",t.tips[1])},function(){l.autoLeft(),l.tipTop=l.top+l.height+10,f.removeClass("layui-layer-TipsT").addClass("layui-layer-TipsB").css("border-right-color",t.tips[1])},function(){l.tipLeft=l.left-o[0]-10,l.tipTop=l.top,f.removeClass("layui-layer-TipsR").addClass("layui-layer-TipsL").css("border-bottom-color",t.tips[1])}],l.where[c-1](),1===c?l.top-(n.scrollTop()+o[1]+16)<0&&l.where[2]():2===c?n.width()-(l.left+l.width+o[0]+16)>0||l.where[3]():3===c?l.top-n.scrollTop()+l.height+o[1]+16-n.height()>0&&l.where[0]():4===c&&o[0]+16-l.left>0&&l.where[1](),a.find("."+s[5]).css({"background-color":t.tips[1],"padding-right":t.closeBtn?"30px":""}),a.css({left:l.tipLeft-(t.fixed?n.scrollLeft():0),top:l.tipTop-(t.fixed?n.scrollTop():0)})},l.pt.move=function(){var e=this,t=e.config,a=i(document),l=e.layero,s=l.find(t.move),f=l.find(".layui-layer-resize"),c={};return t.move&&s.css("cursor","move"),s.on("mousedown",function(e){e.preventDefault(),t.move&&(c.moveStart=!0,c.offset=[e.clientX-parseFloat(l.css("left")),e.clientY-parseFloat(l.css("top"))],o.moveElem.css("cursor","move").show())}),f.on("mousedown",function(e){e.preventDefault(),c.resizeStart=!0,c.offset=[e.clientX,e.clientY],c.area=[l.outerWidth(),l.outerHeight()],o.moveElem.css("cursor","se-resize").show()}),a.on("mousemove",function(i){if(c.moveStart){var a=i.clientX-c.offset[0],o=i.clientY-c.offset[1],s="fixed"===l.css("position");if(i.preventDefault(),c.stX=s?0:n.scrollLeft(),c.stY=s?0:n.scrollTop(),!t.moveOut){var f=n.width()-l.outerWidth()+c.stX,d=n.height()-l.outerHeight()+c.stY;af&&(a=f),od&&(o=d)}l.css({left:a,top:o})}if(t.resize&&c.resizeStart){var a=i.clientX-c.offset[0],o=i.clientY-c.offset[1];i.preventDefault(),r.style(e.index,{width:c.area[0]+a,height:c.area[1]+o}),c.isResize=!0}}).on("mouseup",function(e){c.moveStart&&(delete c.moveStart,o.moveElem.hide(),t.moveEnd&&t.moveEnd()),c.resizeStart&&(delete c.resizeStart,o.moveElem.hide())}),e},l.pt.callback=function(){function e(){var e=a.cancel&&a.cancel(t.index,n);e===!1||r.close(t.index)}var t=this,n=t.layero,a=t.config;t.openLayer(),a.success&&(2==a.type?n.find("iframe").on("load",function(){a.success(n,t.index)}):a.success(n,t.index)),6==r.ie&&t.IE6(n),n.find("."+s[6]).children("a").on("click",function(){var e=i(this).index();if(0===e)a.yes?a.yes(t.index,n):a.btn1?a.btn1(t.index,n):r.close(t.index);else{var o=a["btn"+(e+1)]&&a["btn"+(e+1)](t.index,n);o===!1||r.close(t.index)}}),n.find("."+s[7]).on("click",e),a.shadeClose&&i("#layui-layer-shade"+t.index).on("click",function(){r.close(t.index)}),n.find(".layui-layer-min").on("click",function(){var e=a.min&&a.min(n);e===!1||r.min(t.index,a)}),n.find(".layui-layer-max").on("click",function(){i(this).hasClass("layui-layer-maxmin")?(r.restore(t.index),a.restore&&a.restore(n)):(r.full(t.index,a),setTimeout(function(){a.full&&a.full(n)},100))}),a.end&&(o.end[t.index]=a.end)},o.reselect=function(){i.each(i("select"),function(e,t){var n=i(this);n.parents("."+s[0])[0]||1==n.attr("layer")&&i("."+s[0]).length<1&&n.removeAttr("layer").show(),n=null})},l.pt.IE6=function(e){i("select").each(function(e,t){var n=i(this);n.parents("."+s[0])[0]||"none"===n.css("display")||n.attr({layer:"1"}).hide(),n=null})},l.pt.openLayer=function(){var e=this;r.zIndex=e.config.zIndex,r.setTop=function(e){var t=function(){r.zIndex++,e.css("z-index",r.zIndex+1)};return r.zIndex=parseInt(e[0].style.zIndex),e.on("mousedown",t),r.zIndex}},o.record=function(e){var t=[e.width(),e.height(),e.position().top,e.position().left+parseFloat(e.css("margin-left"))];e.find(".layui-layer-max").addClass("layui-layer-maxmin"),e.attr({area:t})},o.rescollbar=function(e){s.html.attr("layer-full")==e&&(s.html[0].style.removeProperty?s.html[0].style.removeProperty("overflow"):s.html[0].style.removeAttribute("overflow"),s.html.removeAttr("layer-full"))},e.layer=r,r.getChildFrame=function(e,t){return t=t||i("."+s[4]).attr("times"),i("#"+s[0]+t).find("iframe").contents().find(e)},r.getFrameIndex=function(e){return i("#"+e).parents("."+s[4]).attr("times")},r.iframeAuto=function(e){if(e){var t=r.getChildFrame("html",e).outerHeight(),n=i("#"+s[0]+e),a=n.find(s[1]).outerHeight()||0,o=n.find("."+s[6]).outerHeight()||0;n.css({height:t+a+o}),n.find("iframe").css({height:t})}},r.iframeSrc=function(e,t){i("#"+s[0]+e).find("iframe").attr("src",t)},r.style=function(e,t,n){var a=i("#"+s[0]+e),r=a.find(".layui-layer-content"),l=a.attr("type"),f=a.find(s[1]).outerHeight()||0,c=a.find("."+s[6]).outerHeight()||0;a.attr("minLeft");l!==o.type[3]&&l!==o.type[4]&&(n||(parseFloat(t.width)<=260&&(t.width=260),parseFloat(t.height)-f-c<=64&&(t.height=64+f+c)),a.css(t),c=a.find("."+s[6]).outerHeight(),l===o.type[2]?a.find("iframe").css({height:parseFloat(t.height)-f-c}):r.css({height:parseFloat(t.height)-f-c-parseFloat(r.css("padding-top"))-parseFloat(r.css("padding-bottom"))}))},r.min=function(e,t){var a=i("#"+s[0]+e),l=a.find(s[1]).outerHeight()||0,f=a.attr("minLeft")||181*o.minIndex+"px",c=a.css("position");o.record(a),o.minLeft[0]&&(f=o.minLeft[0],o.minLeft.shift()),a.attr("position",c),r.style(e,{width:180,height:l,left:f,top:n.height()-l,position:"fixed",overflow:"hidden"},!0),a.find(".layui-layer-min").hide(),"page"===a.attr("type")&&a.find(s[4]).hide(),o.rescollbar(e),a.attr("minLeft")||o.minIndex++,a.attr("minLeft",f)},r.restore=function(e){var t=i("#"+s[0]+e),n=t.attr("area").split(",");t.attr("type");r.style(e,{width:parseFloat(n[0]),height:parseFloat(n[1]),top:parseFloat(n[2]),left:parseFloat(n[3]),position:t.attr("position"),overflow:"visible"},!0),t.find(".layui-layer-max").removeClass("layui-layer-maxmin"),t.find(".layui-layer-min").show(),"page"===t.attr("type")&&t.find(s[4]).show(),o.rescollbar(e)},r.full=function(e){var t,a=i("#"+s[0]+e);o.record(a),s.html.attr("layer-full")||s.html.css("overflow","hidden").attr("layer-full",e),clearTimeout(t),t=setTimeout(function(){var t="fixed"===a.css("position");r.style(e,{top:t?0:n.scrollTop(),left:t?0:n.scrollLeft(),width:n.width(),height:n.height()},!0),a.find(".layui-layer-min").hide()},100)},r.title=function(e,t){var n=i("#"+s[0]+(t||r.index)).find(s[1]);n.html(e)},r.close=function(e){var t=i("#"+s[0]+e),n=t.attr("type"),a="layer-anim-close";if(t[0]){var l="layui-layer-wrap",f=function(){if(n===o.type[1]&&"object"===t.attr("conType")){t.children(":not(."+s[5]+")").remove();for(var a=t.find("."+l),r=0;r<2;r++)a.unwrap();a.css("display",a.data("display")).removeClass(l)}else{if(n===o.type[2])try{var f=i("#"+s[4]+e)[0];f.contentWindow.document.write(""),f.contentWindow.close(),t.find("."+s[5])[0].removeChild(f)}catch(c){}t[0].innerHTML="",t.remove()}"function"==typeof o.end[e]&&o.end[e](),delete o.end[e]};t.data("anim")&&t.addClass(a),i("#layui-layer-moves, #layui-layer-shade"+e).remove(),6==r.ie&&o.reselect(),o.rescollbar(e),t.attr("minLeft")&&(o.minIndex--,o.minLeft.push(t.attr("minLeft"))),setTimeout(function(){f()},r.ie&&r.ie<10||!t.data("anim")?0:200)}},r.closeAll=function(e){i.each(i("."+s[0]),function(){var t=i(this),n=e?t.attr("type")===e:1;n&&r.close(t.attr("times")),n=null})};var f=r.cache||{},c=function(e){return f.skin?" "+f.skin+" "+f.skin+"-"+e:""};r.prompt=function(e,t){var a="";if(e=e||{},"function"==typeof e&&(t=e),e.area){var o=e.area;a='style="width: '+o[0]+"; height: "+o[1]+';"',delete e.area}var l,s=2==e.formType?'":function(){return' '}();return r.open(i.extend({type:1,btn:["确定","取消"],content:s,skin:"layui-layer-prompt"+c("prompt"),maxWidth:n.width(),success:function(e){l=e.find(".layui-layer-input"),l.focus()},resize:!1,yes:function(i){var n=l.val();""===n?l.focus():n.length>(e.maxlength||500)?r.tips("最多输入"+(e.maxlength||500)+"个字数",l,{tips:1}):t&&t(n,i,l)}},e))},r.tab=function(e){e=e||{};var t=e.tab||{};return r.open(i.extend({type:1,skin:"layui-layer-tab"+c("tab"),resize:!1,title:function(){var e=t.length,i=1,n="";if(e>0)for(n=''+t[0].title+" ";i"+t[i].title+"";return n}(),content:''+function(){var e=t.length,i=1,n="";if(e>0)for(n=''+(t[0].content||"no content")+" ";i'+(t[i].content||"no content")+"";return n}()+" ",success:function(t){var n=t.find(".layui-layer-title").children(),a=t.find(".layui-layer-tabmain").children();n.on("mousedown",function(t){t.stopPropagation?t.stopPropagation():t.cancelBubble=!0;var n=i(this),o=n.index();n.addClass("layui-layer-tabnow").siblings().removeClass("layui-layer-tabnow"),a.eq(o).show().siblings().hide(),"function"==typeof e.change&&e.change(o)})}},e))},r.photos=function(t,n,a){function o(e,t,i){var n=new Image;return n.src=e,n.complete?t(n):(n.onload=function(){n.onload=null,t(n)},void(n.onerror=function(e){n.onerror=null,i(e)}))}var l={};if(t=t||{},t.photos){var s=t.photos.constructor===Object,f=s?t.photos:{},d=f.data||[],u=f.start||0;if(l.imgIndex=(0|u)+1,t.img=t.img||"img",s){if(0===d.length)return r.msg("没有图片")}else{var y=i(t.photos),p=function(){d=[],y.find(t.img).each(function(e){var t=i(this);t.attr("layer-index",e),d.push({alt:t.attr("alt"),pid:t.attr("layer-pid"),src:t.attr("layer-src")||t.attr("src"),thumb:t.attr("src")})})};if(p(),0===d.length)return;if(n||y.on("click",t.img,function(){var e=i(this),n=e.attr("layer-index");r.photos(i.extend(t,{photos:{start:n,data:d,tab:t.tab},full:t.full}),!0),p()}),!n)return}l.imgprev=function(e){l.imgIndex--,l.imgIndex<1&&(l.imgIndex=d.length),l.tabimg(e)},l.imgnext=function(e,t){l.imgIndex++,l.imgIndex>d.length&&(l.imgIndex=1,t)||l.tabimg(e)},l.keyup=function(e){if(!l.end){var t=e.keyCode;e.preventDefault(),37===t?l.imgprev(!0):39===t?l.imgnext(!0):27===t&&r.close(l.index)}},l.tabimg=function(e){d.length<=1||(f.start=l.imgIndex-1,r.close(l.index),r.photos(t,!0,e))},l.event=function(){l.bigimg.hover(function(){l.imgsee.show()},function(){l.imgsee.hide()}),l.bigimg.find(".layui-layer-imgprev").on("click",function(e){e.preventDefault(),l.imgprev()}),l.bigimg.find(".layui-layer-imgnext").on("click",function(e){e.preventDefault(),l.imgnext()}),i(document).on("keyup",l.keyup)},l.loadi=r.load(1,{shade:!("shade"in t)&&.9,scrollbar:!1}),o(d[u].src,function(n){r.close(l.loadi),l.index=r.open(i.extend({type:1,area:function(){var a=[n.width,n.height],o=[i(e).width()-100,i(e).height()-100];if(!t.full&&(a[0]>o[0]||a[1]>o[1])){var r=[a[0]/o[0],a[1]/o[1]];r[0]>r[1]?(a[0]=a[0]/r[0],a[1]=a[1]/r[0]):r[0] ",success:function(e,i){l.bigimg=e.find(".layui-layer-phimg"),l.imgsee=e.find(".layui-layer-imguide,.layui-layer-imgbar"),l.event(e),t.tab&&t.tab(d[u],e)},end:function(){l.end=!0,i(document).off("keyup",l.keyup)}},t))},function(){r.close(l.loadi),r.msg("当前图片地址异常 是否继续查看下一张?",{time:3e4,btn:["下一张","不看了"],yes:function(){d.length>1&&l.imgnext(!0,!0)}})})}},o.run=function(t){i=t,n=i(e),s.html=i("html"),r.open=function(e){var t=new l(e);return t.index}},e.layui&&layui.define?(r.ready(),layui.define("jquery",function(t){r.path=layui.cache.dir,o.run(layui.jquery),e.layer=r,t("layer",r)})):"function"==typeof define?define(["jquery"],function(){return o.run(e.jQuery),r}):function(){o.run(e.jQuery),r.ready()}()}(window);
\ No newline at end of file
+ ;!function(e,t){"use strict";var i,n,a=e.layui&&layui.define,o={getPath:function(){var e=document.scripts,t=e[e.length-1],i=t.src;if(!t.getAttribute("merge"))return i.substring(0,i.lastIndexOf("/")+1)}(),config:{},end:{},minIndex:0,minLeft:[],btn:["OK","Cancel"],type:["dialog","page","iframe","loading","tips"]},r={v:"3.0.1",ie:function(){var t=navigator.userAgent.toLowerCase();return!!(e.ActiveXObject||"ActiveXObject"in e)&&((t.match(/msie\s(\d+)/)||[])[1]||"11")}(),index:e.layer&&e.layer.v?1e5:0,path:o.getPath,config:function(e,t){return e=e||{},r.cache=o.config=i.extend({},o.config,e),r.path=o.config.path||r.path,"string"==typeof e.extend&&(e.extend=[e.extend]),o.config.path&&r.ready(),e.extend?(a?layui.addcss("modules/layer/"+e.extend):r.link("skin/"+e.extend),this):this},link:function(t,n,a){if(r.path){var o=i("head")[0],l=document.createElement("link");"string"==typeof n&&(a=n);var s=(a||t).replace(/\.|\//g,""),f="layuicss-"+s,c=0;l.rel="stylesheet",l.href=r.path+t,l.id=f,i("#"+f)[0]||o.appendChild(l),"function"==typeof n&&!function d(){return++c>80?e.console&&console.error("layer.css: Invalid"):void(1989===parseInt(i("#"+f).css("width"))?n():setTimeout(d,100))}()}},ready:function(e){var t="skinlayercss",i="1110";return a?layui.addcss("modules/layer/default/layer.css?v="+r.v+i,e,t):r.link("skin/default/layer.css?v="+r.v+i,e,t),this},alert:function(e,t,n){var a="function"==typeof t;return a&&(n=t),r.open(i.extend({content:e,yes:n},a?{}:t))},confirm:function(e,t,n,a){var l="function"==typeof t;return l&&(a=n,n=t),r.open(i.extend({content:e,btn:o.btn,yes:n,btn2:a},l?{}:t))},msg:function(e,n,a){var l="function"==typeof n,f=o.config.skin,c=(f?f+" "+f+"-msg":"")||"layui-layer-msg",d=s.anim.length-1;return l&&(a=n),r.open(i.extend({content:e,time:3e3,shade:!1,skin:c,title:!1,closeBtn:!1,btn:!1,resize:!1,end:a},l&&!o.config.skin?{skin:c+" layui-layer-hui",anim:d}:function(){return n=n||{},(n.icon===-1||n.icon===t&&!o.config.skin)&&(n.skin=c+" "+(n.skin||"layui-layer-hui")),n}()))},load:function(e,t){return r.open(i.extend({type:3,icon:e||0,resize:!1,shade:.01},t))},tips:function(e,t,n){return r.open(i.extend({type:4,content:[e,t],closeBtn:!1,time:3e3,shade:!1,resize:!1,fixed:!1,maxWidth:210},n))}},l=function(e){var t=this;t.index=++r.index,t.config=i.extend({},t.config,o.config,e),document.body?t.creat():setTimeout(function(){t.creat()},50)};l.pt=l.prototype;var s=["layui-layer",".layui-layer-title",".layui-layer-main",".layui-layer-dialog","layui-layer-iframe","layui-layer-content","layui-layer-btn","layui-layer-close"];s.anim=["layer-anim","layer-anim-01","layer-anim-02","layer-anim-03","layer-anim-04","layer-anim-05","layer-anim-06"],l.pt.config={type:0,shade:.3,fixed:!0,move:s[1],title:"信息",offset:"auto",area:"auto",closeBtn:1,time:0,zIndex:19891014,maxWidth:360,anim:0,icon:-1,moveType:1,resize:!0,scrollbar:!0,tips:2},l.pt.vessel=function(e,t){var n=this,a=n.index,r=n.config,l=r.zIndex+a,f="object"==typeof r.title,c=r.maxmin&&(1===r.type||2===r.type),d=r.title?''+(f?r.title[0]:r.title)+"
":"";return r.zIndex=l,t([r.shade?'
':"",''+(e&&2!=r.type?"":d)+'
'+(0==r.type&&r.icon!==-1?' ':"")+(1==r.type&&e?"":r.content||"")+'
'+function(){var e=c?' ':"";return r.closeBtn&&(e+=' '),e}()+" "+(r.btn?function(){var e="";"string"==typeof r.btn&&(r.btn=[r.btn]);for(var t=0,i=r.btn.length;t
'+r.btn[t]+"";return' '+e+"
"}():"")+(r.resize?' ':"")+" "],d,i('
')),n},l.pt.creat=function(){var e=this,t=e.config,a=e.index,l=t.content,f="object"==typeof l,c=i("body");if(!i("#"+t.id)[0]){switch("string"==typeof t.area&&(t.area="auto"===t.area?["",""]:[t.area,""]),t.shift&&(t.anim=t.shift),6==r.ie&&(t.fixed=!1),t.type){case 0:t.btn="btn"in t?t.btn:o.btn[0],r.closeAll("dialog");break;case 2:var l=t.content=f?t.content:[t.content||"http://layer.layui.com","auto"];t.content='';break;case 3:delete t.title,delete t.closeBtn,t.icon===-1&&0===t.icon,r.closeAll("loading");break;case 4:f||(t.content=[t.content,"body"]),t.follow=t.content[1],t.content=t.content[0]+' ',delete t.title,t.tips="object"==typeof t.tips?t.tips:[t.tips,!0],t.tipsMore||r.closeAll("tips")}e.vessel(f,function(n,r,d){c.append(n[0]),f?function(){2==t.type||4==t.type?function(){i("body").append(n[1])}():function(){l.parents("."+s[0])[0]||(l.data("display",l.css("display")).show().addClass("layui-layer-wrap").wrap(n[1]),i("#"+s[0]+a).find("."+s[5]).before(r))}()}():c.append(n[1]),i(".layui-layer-move")[0]||c.append(o.moveElem=d),e.layero=i("#"+s[0]+a),t.scrollbar||s.html.css("overflow","hidden").attr("layer-full",a)}).auto(a),2==t.type&&6==r.ie&&e.layero.find("iframe").attr("src",l[0]),4==t.type?e.tips():e.offset(),t.fixed&&n.on("resize",function(){e.offset(),(/^\d+%$/.test(t.area[0])||/^\d+%$/.test(t.area[1]))&&e.auto(a),4==t.type&&e.tips()}),t.time<=0||setTimeout(function(){r.close(e.index)},t.time),e.move().callback(),s.anim[t.anim]&&e.layero.addClass(s.anim[t.anim]).data("anim",!0)}},l.pt.auto=function(e){function t(e){e=l.find(e),e.height(f[1]-c-d-2*(0|parseFloat(e.css("padding"))))}var a=this,o=a.config,l=i("#"+s[0]+e);""===o.area[0]&&o.maxWidth>0&&(r.ie&&r.ie<8&&o.btn&&l.width(l.innerWidth()),l.outerWidth()>o.maxWidth&&l.width(o.maxWidth));var f=[l.innerWidth(),l.innerHeight()],c=l.find(s[1]).outerHeight()||0,d=l.find("."+s[6]).outerHeight()||0;switch(o.type){case 2:t("iframe");break;default:""===o.area[1]?o.fixed&&f[1]>=n.height()&&(f[1]=n.height(),t("."+s[5])):t("."+s[5])}return a},l.pt.offset=function(){var e=this,t=e.config,i=e.layero,a=[i.outerWidth(),i.outerHeight()],o="object"==typeof t.offset;e.offsetTop=(n.height()-a[1])/2,e.offsetLeft=(n.width()-a[0])/2,o?(e.offsetTop=t.offset[0],e.offsetLeft=t.offset[1]||e.offsetLeft):"auto"!==t.offset&&("t"===t.offset?e.offsetTop=0:"r"===t.offset?e.offsetLeft=n.width()-a[0]:"b"===t.offset?e.offsetTop=n.height()-a[1]:"l"===t.offset?e.offsetLeft=0:"lt"===t.offset?(e.offsetTop=0,e.offsetLeft=0):"lb"===t.offset?(e.offsetTop=n.height()-a[1],e.offsetLeft=0):"rt"===t.offset?(e.offsetTop=0,e.offsetLeft=n.width()-a[0]):"rb"===t.offset?(e.offsetTop=n.height()-a[1],e.offsetLeft=n.width()-a[0]):e.offsetTop=t.offset),t.fixed||(e.offsetTop=/%$/.test(e.offsetTop)?n.height()*parseFloat(e.offsetTop)/100:parseFloat(e.offsetTop),e.offsetLeft=/%$/.test(e.offsetLeft)?n.width()*parseFloat(e.offsetLeft)/100:parseFloat(e.offsetLeft),e.offsetTop+=n.scrollTop(),e.offsetLeft+=n.scrollLeft()),i.attr("minLeft")&&(e.offsetTop=n.height()-(i.find(s[1]).outerHeight()||0),e.offsetLeft=i.css("left")),i.css({top:e.offsetTop,left:e.offsetLeft})},l.pt.tips=function(){var e=this,t=e.config,a=e.layero,o=[a.outerWidth(),a.outerHeight()],r=i(t.follow);r[0]||(r=i("body"));var l={width:r.outerWidth(),height:r.outerHeight(),top:r.offset().top,left:r.offset().left},f=a.find(".layui-layer-TipsG"),c=t.tips[0];t.tips[1]||f.remove(),l.autoLeft=function(){l.left+o[0]-n.width()>0?(l.tipLeft=l.left+l.width-o[0],f.css({right:12,left:"auto"})):l.tipLeft=l.left},l.where=[function(){l.autoLeft(),l.tipTop=l.top-o[1]-10,f.removeClass("layui-layer-TipsB").addClass("layui-layer-TipsT").css("border-right-color",t.tips[1])},function(){l.tipLeft=l.left+l.width+10,l.tipTop=l.top,f.removeClass("layui-layer-TipsL").addClass("layui-layer-TipsR").css("border-bottom-color",t.tips[1])},function(){l.autoLeft(),l.tipTop=l.top+l.height+10,f.removeClass("layui-layer-TipsT").addClass("layui-layer-TipsB").css("border-right-color",t.tips[1])},function(){l.tipLeft=l.left-o[0]-10,l.tipTop=l.top,f.removeClass("layui-layer-TipsR").addClass("layui-layer-TipsL").css("border-bottom-color",t.tips[1])}],l.where[c-1](),1===c?l.top-(n.scrollTop()+o[1]+16)<0&&l.where[2]():2===c?n.width()-(l.left+l.width+o[0]+16)>0||l.where[3]():3===c?l.top-n.scrollTop()+l.height+o[1]+16-n.height()>0&&l.where[0]():4===c&&o[0]+16-l.left>0&&l.where[1](),a.find("."+s[5]).css({"background-color":t.tips[1],"padding-right":t.closeBtn?"30px":""}),a.css({left:l.tipLeft-(t.fixed?n.scrollLeft():0),top:l.tipTop-(t.fixed?n.scrollTop():0)})},l.pt.move=function(){var e=this,t=e.config,a=i(document),l=e.layero,s=l.find(t.move),f=l.find(".layui-layer-resize"),c={};return t.move&&s.css("cursor","move"),s.on("mousedown",function(e){e.preventDefault(),t.move&&(c.moveStart=!0,c.offset=[e.clientX-parseFloat(l.css("left")),e.clientY-parseFloat(l.css("top"))],o.moveElem.css("cursor","move").show())}),f.on("mousedown",function(e){e.preventDefault(),c.resizeStart=!0,c.offset=[e.clientX,e.clientY],c.area=[l.outerWidth(),l.outerHeight()],o.moveElem.css("cursor","se-resize").show()}),a.on("mousemove",function(i){if(c.moveStart){var a=i.clientX-c.offset[0],o=i.clientY-c.offset[1],s="fixed"===l.css("position");if(i.preventDefault(),c.stX=s?0:n.scrollLeft(),c.stY=s?0:n.scrollTop(),!t.moveOut){var f=n.width()-l.outerWidth()+c.stX,d=n.height()-l.outerHeight()+c.stY;af&&(a=f),od&&(o=d)}l.css({left:a,top:o})}if(t.resize&&c.resizeStart){var a=i.clientX-c.offset[0],o=i.clientY-c.offset[1];i.preventDefault(),r.style(e.index,{width:c.area[0]+a,height:c.area[1]+o}),c.isResize=!0}}).on("mouseup",function(e){c.moveStart&&(delete c.moveStart,o.moveElem.hide(),t.moveEnd&&t.moveEnd()),c.resizeStart&&(delete c.resizeStart,o.moveElem.hide())}),e},l.pt.callback=function(){function e(){var e=a.cancel&&a.cancel(t.index,n);e===!1||r.close(t.index)}var t=this,n=t.layero,a=t.config;t.openLayer(),a.success&&(2==a.type?n.find("iframe").on("load",function(){a.success(n,t.index)}):a.success(n,t.index)),6==r.ie&&t.IE6(n),n.find("."+s[6]).children("a").on("click",function(){var e=i(this).index();if(0===e)a.yes?a.yes(t.index,n):a.btn1?a.btn1(t.index,n):r.close(t.index);else{var o=a["btn"+(e+1)]&&a["btn"+(e+1)](t.index,n);o===!1||r.close(t.index)}}),n.find("."+s[7]).on("click",e),a.shadeClose&&i("#layui-layer-shade"+t.index).on("click",function(){r.close(t.index)}),n.find(".layui-layer-min").on("click",function(){var e=a.min&&a.min(n);e===!1||r.min(t.index,a)}),n.find(".layui-layer-max").on("click",function(){i(this).hasClass("layui-layer-maxmin")?(r.restore(t.index),a.restore&&a.restore(n)):(r.full(t.index,a),setTimeout(function(){a.full&&a.full(n)},100))}),a.end&&(o.end[t.index]=a.end)},o.reselect=function(){i.each(i("select"),function(e,t){var n=i(this);n.parents("."+s[0])[0]||1==n.attr("layer")&&i("."+s[0]).length<1&&n.removeAttr("layer").show(),n=null})},l.pt.IE6=function(e){i("select").each(function(e,t){var n=i(this);n.parents("."+s[0])[0]||"none"===n.css("display")||n.attr({layer:"1"}).hide(),n=null})},l.pt.openLayer=function(){var e=this;r.zIndex=e.config.zIndex,r.setTop=function(e){var t=function(){r.zIndex++,e.css("z-index",r.zIndex+1)};return r.zIndex=parseInt(e[0].style.zIndex),e.on("mousedown",t),r.zIndex}},o.record=function(e){var t=[e.width(),e.height(),e.position().top,e.position().left+parseFloat(e.css("margin-left"))];e.find(".layui-layer-max").addClass("layui-layer-maxmin"),e.attr({area:t})},o.rescollbar=function(e){s.html.attr("layer-full")==e&&(s.html[0].style.removeProperty?s.html[0].style.removeProperty("overflow"):s.html[0].style.removeAttribute("overflow"),s.html.removeAttr("layer-full"))},e.layer=r,r.getChildFrame=function(e,t){return t=t||i("."+s[4]).attr("times"),i("#"+s[0]+t).find("iframe").contents().find(e)},r.getFrameIndex=function(e){return i("#"+e).parents("."+s[4]).attr("times")},r.iframeAuto=function(e){if(e){var t=r.getChildFrame("html",e).outerHeight(),n=i("#"+s[0]+e),a=n.find(s[1]).outerHeight()||0,o=n.find("."+s[6]).outerHeight()||0;n.css({height:t+a+o}),n.find("iframe").css({height:t})}},r.iframeSrc=function(e,t){i("#"+s[0]+e).find("iframe").attr("src",t)},r.style=function(e,t,n){var a=i("#"+s[0]+e),r=a.find(".layui-layer-content"),l=a.attr("type"),f=a.find(s[1]).outerHeight()||0,c=a.find("."+s[6]).outerHeight()||0;a.attr("minLeft");l!==o.type[3]&&l!==o.type[4]&&(n||(parseFloat(t.width)<=260&&(t.width=260),parseFloat(t.height)-f-c<=64&&(t.height=64+f+c)),a.css(t),c=a.find("."+s[6]).outerHeight(),l===o.type[2]?a.find("iframe").css({height:parseFloat(t.height)-f-c}):r.css({height:parseFloat(t.height)-f-c-parseFloat(r.css("padding-top"))-parseFloat(r.css("padding-bottom"))}))},r.min=function(e,t){var a=i("#"+s[0]+e),l=a.find(s[1]).outerHeight()||0,f=a.attr("minLeft")||181*o.minIndex+"px",c=a.css("position");o.record(a),o.minLeft[0]&&(f=o.minLeft[0],o.minLeft.shift()),a.attr("position",c),r.style(e,{width:180,height:l,left:f,top:n.height()-l,position:"fixed",overflow:"hidden"},!0),a.find(".layui-layer-min").hide(),"page"===a.attr("type")&&a.find(s[4]).hide(),o.rescollbar(e),a.attr("minLeft")||o.minIndex++,a.attr("minLeft",f)},r.restore=function(e){var t=i("#"+s[0]+e),n=t.attr("area").split(",");t.attr("type");r.style(e,{width:parseFloat(n[0]),height:parseFloat(n[1]),top:parseFloat(n[2]),left:parseFloat(n[3]),position:t.attr("position"),overflow:"visible"},!0),t.find(".layui-layer-max").removeClass("layui-layer-maxmin"),t.find(".layui-layer-min").show(),"page"===t.attr("type")&&t.find(s[4]).show(),o.rescollbar(e)},r.full=function(e){var t,a=i("#"+s[0]+e);o.record(a),s.html.attr("layer-full")||s.html.css("overflow","hidden").attr("layer-full",e),clearTimeout(t),t=setTimeout(function(){var t="fixed"===a.css("position");r.style(e,{top:t?0:n.scrollTop(),left:t?0:n.scrollLeft(),width:n.width(),height:n.height()},!0),a.find(".layui-layer-min").hide()},100)},r.title=function(e,t){var n=i("#"+s[0]+(t||r.index)).find(s[1]);n.html(e)},r.close=function(e){var t=i("#"+s[0]+e),n=t.attr("type"),a="layer-anim-close";if(t[0]){var l="layui-layer-wrap",f=function(){if(n===o.type[1]&&"object"===t.attr("conType")){t.children(":not(."+s[5]+")").remove();for(var a=t.find("."+l),r=0;r<2;r++)a.unwrap();a.css("display",a.data("display")).removeClass(l)}else{if(n===o.type[2])try{var f=i("#"+s[4]+e)[0];f.contentWindow.document.write(""),f.contentWindow.close(),t.find("."+s[5])[0].removeChild(f)}catch(c){}t[0].innerHTML="",t.remove()}"function"==typeof o.end[e]&&o.end[e](),delete o.end[e]};t.data("anim")&&t.addClass(a),i("#layui-layer-moves, #layui-layer-shade"+e).remove(),6==r.ie&&o.reselect(),o.rescollbar(e),t.attr("minLeft")&&(o.minIndex--,o.minLeft.push(t.attr("minLeft"))),setTimeout(function(){f()},r.ie&&r.ie<10||!t.data("anim")?0:200)}},r.closeAll=function(e){i.each(i("."+s[0]),function(){var t=i(this),n=e?t.attr("type")===e:1;n&&r.close(t.attr("times")),n=null})};var f=r.cache||{},c=function(e){return f.skin?" "+f.skin+" "+f.skin+"-"+e:""};r.prompt=function(e,t){var a="";if(e=e||{},"function"==typeof e&&(t=e),e.area){var o=e.area;a='style="width: '+o[0]+"; height: "+o[1]+';"',delete e.area}var l,s=2==e.formType?'":function(){return' '}();return r.open(i.extend({type:1,btn:["确定","取消"],content:s,skin:"layui-layer-prompt"+c("prompt"),maxWidth:n.width(),success:function(e){l=e.find(".layui-layer-input"),l.focus()},resize:!1,yes:function(i){var n=l.val();""===n?l.focus():n.length>(e.maxlength||500)?r.tips("最多输入"+(e.maxlength||500)+"个字数",l,{tips:1}):t&&t(n,i,l)}},e))},r.tab=function(e){e=e||{};var t=e.tab||{};return r.open(i.extend({type:1,skin:"layui-layer-tab"+c("tab"),resize:!1,title:function(){var e=t.length,i=1,n="";if(e>0)for(n=''+t[0].title+" ";i"+t[i].title+"";return n}(),content:''+function(){var e=t.length,i=1,n="";if(e>0)for(n=''+(t[0].content||"no content")+" ";i'+(t[i].content||"no content")+"";return n}()+" ",success:function(t){var n=t.find(".layui-layer-title").children(),a=t.find(".layui-layer-tabmain").children();n.on("mousedown",function(t){t.stopPropagation?t.stopPropagation():t.cancelBubble=!0;var n=i(this),o=n.index();n.addClass("layui-layer-tabnow").siblings().removeClass("layui-layer-tabnow"),a.eq(o).show().siblings().hide(),"function"==typeof e.change&&e.change(o)})}},e))},r.photos=function(t,n,a){function o(e,t,i){var n=new Image;return n.src=e,n.complete?t(n):(n.onload=function(){n.onload=null,t(n)},void(n.onerror=function(e){n.onerror=null,i(e)}))}var l={};if(t=t||{},t.photos){var s=t.photos.constructor===Object,f=s?t.photos:{},d=f.data||[],u=f.start||0;if(l.imgIndex=(0|u)+1,t.img=t.img||"img",s){if(0===d.length)return r.msg("没有图片")}else{var y=i(t.photos),p=function(){d=[],y.find(t.img).each(function(e){var t=i(this);t.attr("layer-index",e),d.push({alt:t.attr("alt"),pid:t.attr("layer-pid"),src:t.attr("layer-src")||t.attr("src"),thumb:t.attr("src")})})};if(p(),0===d.length)return;if(n||y.on("click",t.img,function(){var e=i(this),n=e.attr("layer-index");r.photos(i.extend(t,{photos:{start:n,data:d,tab:t.tab},full:t.full}),!0),p()}),!n)return}l.imgprev=function(e){l.imgIndex--,l.imgIndex<1&&(l.imgIndex=d.length),l.tabimg(e)},l.imgnext=function(e,t){l.imgIndex++,l.imgIndex>d.length&&(l.imgIndex=1,t)||l.tabimg(e)},l.keyup=function(e){if(!l.end){var t=e.keyCode;e.preventDefault(),37===t?l.imgprev(!0):39===t?l.imgnext(!0):27===t&&r.close(l.index)}},l.tabimg=function(e){d.length<=1||(f.start=l.imgIndex-1,r.close(l.index),r.photos(t,!0,e))},l.event=function(){l.bigimg.hover(function(){l.imgsee.show()},function(){l.imgsee.hide()}),l.bigimg.find(".layui-layer-imgprev").on("click",function(e){e.preventDefault(),l.imgprev()}),l.bigimg.find(".layui-layer-imgnext").on("click",function(e){e.preventDefault(),l.imgnext()}),i(document).on("keyup",l.keyup)},l.loadi=r.load(1,{shade:!("shade"in t)&&.9,scrollbar:!1}),o(d[u].src,function(n){r.close(l.loadi),l.index=r.open(i.extend({type:1,area:function(){var a=[n.width,n.height],o=[i(e).width()-100,i(e).height()-100];if(!t.full&&(a[0]>o[0]||a[1]>o[1])){var r=[a[0]/o[0],a[1]/o[1]];r[0]>r[1]?(a[0]=a[0]/r[0],a[1]=a[1]/r[0]):r[0] ",success:function(e,i){l.bigimg=e.find(".layui-layer-phimg"),l.imgsee=e.find(".layui-layer-imguide,.layui-layer-imgbar"),l.event(e),t.tab&&t.tab(d[u],e)},end:function(){l.end=!0,i(document).off("keyup",l.keyup)}},t))},function(){r.close(l.loadi),r.msg("当前图片地址异常 是否继续查看下一张?",{time:3e4,btn:["下一张","不看了"],yes:function(){d.length>1&&l.imgnext(!0,!0)}})})}},o.run=function(t){i=t,n=i(e),s.html=i("html"),r.open=function(e){var t=new l(e);return t.index}},e.layui&&layui.define?(r.ready(),layui.define("jquery",function(t){r.path=layui.cache.dir,o.run(layui.jquery),e.layer=r,t("layer",r)})):"function"==typeof define?define(["jquery"],function(){return o.run(e.jQuery),r}):function(){o.run(e.jQuery),r.ready()}()}(window);
\ No newline at end of file
diff --git a/BTPanel/templates/default/config.html b/BTPanel/templates/default/config.html
index 0af6c897..e7af4f35 100644
--- a/BTPanel/templates/default/config.html
+++ b/BTPanel/templates/default/config.html
@@ -52,6 +52,13 @@
+
@@ -257,32 +264,104 @@
height: 100%;
}
- .personalDetails .userList .addweChat {
- height: 50px;
- text-align: center;
- padding-top: 20px;
- color: #20a53a;
- font-size: 16px;
- }
+ .personalDetails .userList .addweChat {
+ height: 50px;
+ text-align: center;
+ padding-top: 20px;
+ color: #20a53a;
+ font-size: 16px;
+ }
- .personalDetails .userList .addweChat:hover {
- }
+ .personalDetails .userList .item {
+ height: 70px;
+ padding: 10px 15px;
+ border: 1px solid #ececec;
+ margin: 15px 65px;
+ border-radius: 5px;
+ }
- .personalDetails .userList .item {
- height: 70px;
- padding: 10px 15px;
- border: 1px solid #ececec;
- margin: 15px 65px;
- border-radius: 5px;
- }
+ .personalDetails .userList .cancelBind {
+ height: 50px;
+ width: 60px;
+ float: right;
+ line-height: 50px;
+ text-align: center;
+ }
+ .verify_title{
+ color: #555;
+ text-align: center;
+ font-size: 20px;
+ height: 40px;
+ line-height: 40px;
+ margin-bottom: 20px;
+ }
+ .verify_item{
+ padding: 0 35px;
+ }
+ .verify_item .verify_vice_title{
+ font-size: 15.5px;
+ color: #666;
+ margin-bottom: 10px;
+ }
+ .verify_tips{
+ margin: 0 15px;
+ margin-top: 25px;
+ padding: 20px 25px;
+ font-size: 14px;
+ color: #666;
+ border-top: 1px solid #ececec;
+ }
+ .verify_tips p{
+ margin-bottom: 10px;
+ }
+ .verify_box{
+ background: #f8f8f8;
+ padding: 15px 25px;
+ margin-bottom: 20px;
+ border-radius: 4px;
+ /* width: 200px; */
+ }
+ .verify_box .verify_box_line{
+ height: 25px;
+ line-height: 25px;
+ font-size: 14px;
+ color: #444;
+ font-weight: 500;
+ }
+ .verify_box .verify_box_line span{
+ color: #666;
+ }
+ .google_verify ul li{
+ height: auto;
+ line-height: 18px;
+ margin-bottom: 5px;
+ }
+ .risk_form{
+
+ }
+
+ .risk_form ul{
+ border: 1px solid #ececec;
+ border-radius: 10px;
+ font-size:12px;
+ margin: 0px auto;
+ margin:15px 0;
+ margin-top: 20px;
+ margin-bottom: 20px;
+ background: #f7f7f7;
+ width: 100%;
+ padding: 25px 40px;
+ list-style-type: inherit;
+ }
+ .risk_form ul li{
+ line-height: 18px;
+ margin-bottom: 6px;
+ }
+ .risk_tilte{
+ margin: 0 0 10px 35px;
+ font-size: 22px;
+ }
- .personalDetails .userList .cancelBind {
- height: 50px;
- width: 60px;
- float: right;
- line-height: 50px;
- text-align: center;
- }
diff --git a/BTPanel/templates/default/crontab.html b/BTPanel/templates/default/crontab.html
index 355b2f4b..0165e26f 100644
--- a/BTPanel/templates/default/crontab.html
+++ b/BTPanel/templates/default/crontab.html
@@ -178,7 +178,7 @@
{% endblock %}
{% block scripts %}
-
+
-
+
@@ -225,21 +80,6 @@
$(function () {
Wreset();
var setTime = '';
- // $.get('/public?name=app&fun=login_qrcode', function (res) {
- // if (res.status) {
- // $('#qrcode').qrcode({
- // render: "canvas",
- // width: 150,
- // height: 150,
- // text: res.msg
- // });
- // $('.entrance').show();
- // }
- // }).error(function (res, textStatus, errorThrown) {
- // if (res.status == 404) {
- // $('.entrance').hide();
- // }
- // });
function controlTime() {
$.get('/public?name=app&fun=is_scan_ok', function (res) {
if (res.status) {
@@ -303,11 +143,47 @@
var data = { username: username, password: md5(md5(password) + '' + '_bt.cn'), code: code }
var loadT = layer.msg("{{data['lan']['JS2']}}", { icon: 16, time: 0, shade: [0.3, '#000'] });
$.post('/login', data, function (rdata) {
+ if(rdata == '1'){
+ layer.close(loadT);
+ layer.open({
+ type:1,
+ title:false,
+ area: ['350px', '285px'],
+ content:'
\
+
Google authentication
\
+
\
+
\
+
\
+
',
+ success:function(){
+ // 点击验证登录
+ $('[name="v_code"]').keyup(function(e){
+ if(e.keyCode == 13){
+ $('#auth_verif_btn').click();
+ }
+ });
+ $('#auth_verif_btn').click(function(e){
+ data['vcode'] = $('[name="v_code"]').val();
+ var loadT = layer.msg("{{data['lan']['JS2']}}", { icon: 16, time: 0, shade: [0.3, '#000'] });
+ $.post('/login', data, function (rdata) {
+ layer.close(loadT);
+ if(!rdata.status){
+ layer.msg(rdata.msg, { icon: 2 });
+ return false;
+ }
+ layer.msg(rdata.msg, { icon: 16, time: 0, shade: [0.3, '#000'] });
+ window.location.href = '/';
+ });
+ });
+ }
+ });
+ return false;
+ }
layer.close(loadT);
if (!rdata.status) {
$("#errorStr").html(rdata.msg);
$("input[name='password']").val('');
- num = rdata.msg.substring(rdata.msg.indexOf('[') + 1, rdata.msg.indexOf(']'))
+ num = rdata.msg.substring(rdata.msg.indexOf('[') + 1, rdata.msg.indexOf(']'));
if (num < 5) {
$(".yzm").show();
$(".login").css("height", "332px");
diff --git a/class/ajax.py b/class/ajax.py
index d2b4ff30..db65ce2d 100644
--- a/class/ajax.py
+++ b/class/ajax.py
@@ -34,6 +34,7 @@ class ajax:
#取Nginx负载状态
self.CheckStatusConf()
result = public.HttpGet('http://127.0.0.1/nginx_status')
+ return result
tmp = result.split()
data = {}
if "request_time" in tmp:
diff --git a/class/common.py b/class/common.py
index 1ce203cf..21642704 100644
--- a/class/common.py
+++ b/class/common.py
@@ -27,7 +27,7 @@ class panelSetup:
if ua:
ua = ua.lower();
if ua.find('spider') != -1 or ua.find('bot') != -1: return redirect('https://www.baidu.com');
- g.version = '6.2.4'
+ g.version = '6.2.6'
g.title = public.GetConfigValue('title')
g.uri = request.path
session['version'] = g.version;
diff --git a/class/config.py b/class/config.py
index a540deeb..89dcd694 100644
--- a/class/config.py
+++ b/class/config.py
@@ -7,11 +7,17 @@
# | Author: 黄文良 <287962566@qq.com>
# +-------------------------------------------------------------------
-import public,re,sys,os,nginx,apache,json,time
+import public,re,sys,os,nginx,apache,json,time,pyotp
from BTPanel import session,admin_path_checks
from flask import request
class config:
+ _setup_path = "/www/server/panel"
+ _key_file = _setup_path+"/data/two_step_auth.txt"
+ _bk_key_file = _setup_path + "/data/bk_two_step_auth.txt"
+ _username_file = _setup_path + "/data/username.txt"
+ _core_fle_path = _setup_path + '/data/qrcode'
+
def getPanelState(self,get):
return os.path.exists('/www/server/panel/data/close.pl');
@@ -707,8 +713,7 @@ class config:
d=["/tmp","/www/php_session"]
count = 0
for i in d:
- if not os.path.exists(i):
- continue
+ if not os.path.exists(i): os.system('mkdir -p %s'%i)
list = os.listdir(i)
for l in list:
if os.path.isdir(i+"/"+l):
@@ -875,6 +880,8 @@ class config:
if not os.path.exists(link_re): return self.auto_cli_php_version(get)
import panelSite
php_versions = panelSite.panelSite().GetPHPVersion(get)
+ if len(php_versions)==0:
+ return public.returnMsg(False,'Failed to get php version!')
del(php_versions[0])
for v in php_versions:
if link_re.find(v['version']) != -1: return {"select":v,"versions":php_versions}
@@ -1045,4 +1052,71 @@ class config:
conf = public.readFile(user_ini_file)
if conf and "session.save_path" in conf:
return True
- return False
\ No newline at end of file
+ return False
+
+ def _create_key(self):
+ get_token = pyotp.random_base32() # returns a 16 character base32 secret. Compatible with Google Authenticator
+ public.writeFile(self._key_file,get_token)
+ username = self.get_random()
+ public.writeFile(self._username_file, username)
+
+ def get_key(self,get):
+ key = public.readFile(self._key_file)
+ username = public.readFile(self._username_file)
+ if not key:
+ return public.returnMsg(False, "The key does not exist. Please turn on and try again.")
+ if not username:
+ return public.returnMsg(False, "The username does not exist. Please turn on and try again.")
+ return {"key":key,"username":username}
+
+ def get_random(self):
+ import random
+ seed = "1234567890abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
+ sa = []
+ for i in range(8):
+ sa.append(random.choice(seed))
+ salt = ''.join(sa)
+ return salt
+
+ def set_two_step_auth(self,get):
+ if not hasattr(get,"act") or not get.act:
+ return public.returnMsg(False, "Please enter the operation mode")
+ if get.act == "1":
+ if not os.path.exists(self._core_fle_path):
+ os.makedirs(self._core_fle_path)
+ username = public.readFile(self._username_file)
+ if not os.path.exists(self._bk_key_file):
+ secret_key = public.readFile(self._key_file)
+ if not secret_key or not username:
+ self._create_key()
+ else:
+ os.rename(self._bk_key_file,self._key_file)
+ secret_key = public.readFile(self._key_file)
+ username = public.readFile(self._username_file)
+ local_ip = public.GetLocalIp()
+ if not secret_key:
+ return public.returnMsg(False,"Failed to generate key or username. Please check if the hard disk space is insufficient or the directory cannot be written.[ {} ]".format(self._setup_path+"/data/"))
+ try:
+ data = pyotp.totp.TOTP(secret_key).provisioning_uri(username, issuer_name=str(local_ip))
+ public.writeFile(self._core_fle_path+'/qrcode.txt',str(data))
+ return public.returnMsg(True, "Open successfully")
+ except Exception as e:
+ return public.returnMsg(False, e)
+ else:
+ if os.path.exists(self._key_file):
+ os.rename(self._key_file,self._bk_key_file)
+ return public.returnMsg(True, "Closed successfully")
+
+ # 检测是否开启双因素验证
+ def check_two_step(self,get):
+ secret_key = public.readFile(self._key_file)
+ if not secret_key:
+ return public.returnMsg(False, "Did not open Google authentication")
+ return public.returnMsg(True, "Google authentication has been turned on")
+
+ # 读取二维码data
+ def get_qrcode_data(self,get):
+ data = public.readFile(self._core_fle_path + '/qrcode.txt')
+ if data:
+ return data
+ return public.returnMsg(True, "No QR code data, please re-open")
\ No newline at end of file
diff --git a/class/crontab.py b/class/crontab.py
index 7c533164..475e7493 100644
--- a/class/crontab.py
+++ b/class/crontab.py
@@ -412,6 +412,7 @@ echo "--------------------------------------------------------------------------
if not os.path.exists(file): public.writeFile(file,'')
conf = public.readFile(file)
+ if type(conf)==bool:return public.returnMsg(False,'Failed to read file!')
conf += config + "\n"
if public.writeFile(file,conf):
if not os.path.exists(u_file):
diff --git a/class/database.py b/class/database.py
index 86b1a9dc..f7c59313 100644
--- a/class/database.py
+++ b/class/database.py
@@ -24,7 +24,7 @@ class database(datatool.datatools):
reg = "^[\w\.-]+$"
if not re.match(reg, data_name): return public.returnMsg(False,'DATABASE_NAME_ERR_T')
if not hasattr(get,'db_user'): get.db_user = data_name;
- username = get.db_user.strip();
+ username = get.db_user.strip()
checks = ['root','mysql','test','sys','panel_logs']
if username in checks or len(username) < 1: return public.returnMsg(False,'DATABASE_USER_NAME_ERR');
if data_name in checks or len(data_name) < 1: return public.returnMsg(False,'DATABASE_NAME_ERR');
@@ -389,14 +389,14 @@ SetLink
name = public.M('databases').where("id=?",(id,)).getField('name')
root = public.M('config').where('id=?',(1,)).getField('mysql_root');
if not os.path.exists(session['config']['backup_path'] + '/database'): os.system('mkdir -p ' + session['config']['backup_path'] + '/database');
- self.mypass(True, root);
+ if not self.mypass(True, root):return public.returnMsg(False, 'Database configuration file failed to get checked, please check if MySQL configuration file exists')
fileName = name + '_' + time.strftime('%Y%m%d_%H%M%S',time.localtime()) + '.sql.gz'
backupName = session['config']['backup_path'] + '/database/' + fileName
public.ExecShell("/www/server/mysql/bin/mysqldump --default-character-set="+ public.get_database_character(name) +" --force --opt \"" + name + "\" | gzip > " + backupName)
if not os.path.exists(backupName): return public.returnMsg(False,'BACKUP_ERROR');
-
- self.mypass(False, root);
+
+ if not self.mypass(True, root): return public.returnMsg(False, 'Database configuration file failed to get checked, please check if MySQL configuration file exists')
sql = public.M('backup')
addTime = time.strftime('%Y-%m-%d %X',time.localtime())
@@ -457,17 +457,17 @@ SetLink
public.ExecShell("cd " + backupPath + " && gunzip -q " + '"'+file+'"')
isgzip = True
if not os.path.exists(backupPath + '/' + tmpFile) or tmpFile == '': return public.returnMsg(False, 'FILE_NOT_EXISTS',(tmpFile,))
- self.mypass(True, root);
+ if not self.mypass(True, root): return public.returnMsg(False, 'Database configuration file failed to get checked, please check if MySQL configuration file exists')
os.system(public.GetConfigValue('setup_path') + "/mysql/bin/mysql -uroot -p" + root + " --force \"" + name + "\" < " +'"'+ backupPath + '/' +tmpFile+'"')
- self.mypass(False, root);
+ if not self.mypass(True, root): return public.returnMsg(False, 'Database configuration file failed to get checked, please check if MySQL configuration file exists')
if isgzip:
os.system('cd ' +backupPath+ ' && gzip ' + file.split('/')[-1][:-3]);
else:
os.system("rm -f " + backupPath + '/' +tmpFile)
else:
- self.mypass(True, root);
+ if not self.mypass(True, root): return public.returnMsg(False, 'Database configuration file failed to get checked, please check if MySQL configuration file exists')
os.system(public.GetConfigValue('setup_path') + "/mysql/bin/mysql -uroot -p" + root + " --force \"" + name + "\" < "+'"' + file+'"')
- self.mypass(False, root);
+ if not self.mypass(True, root): return public.returnMsg(False, 'Database configuration file failed to get checked, please check if MySQL configuration file exists')
public.WriteLog("TYPE_DATABASE", 'DATABASE_INPUT_SUCCESS',(name,))
@@ -508,9 +508,11 @@ SetLink
rep = "\[mysqldump\]\nuser=root"
sea = "[mysqldump]\n"
subStr = sea + "user=root\npassword=\"" + root + "\"\n";
+ if type(mycnf)==bool:return False
mycnf = mycnf.replace(sea,subStr)
if len(mycnf) > 100: public.writeFile('/etc/my.cnf',mycnf);
-
+ return True
+
#添加到服务器
def ToDataBase(self,find):
#if find['username'] == 'bt_default': return 0
@@ -593,14 +595,16 @@ SetLink
db_name = public.M('databases').where('username=?',(name,)).getField('name');
access = get['access']
password = public.M('databases').where("username=?",(name,)).getField('password')
- users = panelMysql.panelMysql().query("select Host from mysql.user where User='" + name + "' AND Host!='localhost'")
+ mysql_obj = panelMysql.panelMysql()
+ result = mysql_obj.query("show databases")
+ isError = self.IsSqlError(result)
+ if isError != None: return isError
+ users = mysql_obj.query("select Host from mysql.user where User='" + name + "' AND Host!='localhost'")
for us in users:
- panelMysql.panelMysql().execute("drop user '" + name + "'@'" + us[0] + "'")
+ mysql_obj.execute("drop user '" + name + "'@'" + us[0] + "'")
self.__CreateUsers(db_name,name,password,access)
-
return public.returnMsg(True, 'SET_SUCCESS')
-
-
+
#获取数据库配置信息
def GetMySQLInfo(self,get):
data = {}
@@ -756,10 +760,15 @@ SetLink
except:pass
for d in data:
for g in gets:
- if d[0] == g: result[g] = d[1];
- result['Run'] = int(time.time()) - int(result['Uptime'])
+ try:
+ if d[0] == g: result[g] = d[1];
+ except:
+ pass
+ if not 'Run' in result and result:
+ result['Run'] = int(time.time()) - int(result['Uptime'])
tmp = panelMysql.panelMysql().query('show master status');
try:
+
result['File'] = tmp[0][0];
result['Position'] = tmp[0][1];
except:
@@ -767,6 +776,7 @@ SetLink
result['Position'] = 'OFF';
return result;
+
#取慢日志
def GetSlowLogs(self,get):
path = self.GetMySQLInfo(get)['datadir'] + '/mysql-slow.log';
diff --git a/class/files.py b/class/files.py
index 72f8b3dd..28c5550f 100644
--- a/class/files.py
+++ b/class/files.py
@@ -10,6 +10,7 @@
import sys,os,public,time,json,pwd,cgi,shutil
from BTPanel import session,request
class files:
+ run_path = None
#检查敏感目录
def CheckDir(self,path):
path = path.replace('//','/');
@@ -44,7 +45,66 @@ class files:
public.GetConfigValue('setup_path'))
return not path in nDirs
-
+
+ #网站文件操作前置检测
+ def site_path_check(self,get):
+ try:
+ if not 'site_id' in get: return True
+ if not self.run_path:
+ self.run_path,self.path,self.site_name = self.GetSiteRunPath(get.site_id)
+ if 'path' in get:
+ if get.path.find(self.path) != 0: return False
+ if 'sfile' in get:
+ if get.sfile.find(self.path) != 0: return False
+ if 'dfile' in get:
+ if get.dfile.find(self.path) != 0: return False
+ return True
+ except: return True
+
+ #网站目录后续安全处理
+ def site_path_safe(self,get):
+ try:
+ if not 'site_id' in get: return True
+ run_path,path,site_name = self.GetSiteRunPath(get.site_id)
+ if not os.path.exists(run_path): os.makedirs(run_path)
+ ini_path = run_path + '/.user.ini'
+ if os.path.exists(ini_path): return True
+ sess_path = '/www/php_session/%s' % site_name
+ if not os.path.exists(sess_path): os.makedirs(sess_path)
+ ini_conf = '''open_basedir={}/:/tmp/:/proc/:{}/
+session.save_path={}/
+session.save_handler = files'''.format(path,sess_path,sess_path)
+ public.writeFile(ini_path, ini_conf)
+ public.ExecShell("chmod 644 %s" % ini_path)
+ public.ExecShell("chdir +i %s" % ini_path)
+ return True
+ except: return False
+
+
+ #取当站点前运行目录
+ def GetSiteRunPath(self,site_id):
+ try:
+ find = public.M('sites').where('id=?',(site_id,)).field('path,name').find();
+ siteName = find['name']
+ sitePath = find['path']
+ if public.get_webserver() == 'nginx':
+ filename = '/www/server/panel/vhost/nginx/' + siteName + '.conf'
+ if os.path.exists(filename):
+ conf = public.readFile(filename)
+ rep = '\s*root\s*(.+);'
+ tmp1 = re.search(rep,conf)
+ if tmp1: path = tmp1.groups()[0];
+ else:
+ filename = '/www/server/panel/vhost/apache/' + siteName + '.conf'
+ if os.path.exists(filename):
+ conf = public.readFile(filename)
+ rep = '\s*DocumentRoot\s*"(.+)"\s*\n'
+ tmp1 = re.search(rep,conf)
+ if tmp1: path = tmp1.groups()[0];
+ return path,sitePath,siteName
+ except:
+ return sitePath,sitePath,siteName
+
#检测文件名
def CheckFileName(self,filename):
nots = ['\\','&','*','|',';','"',"'",'<','>']
@@ -83,19 +143,18 @@ class files:
public.WriteLog('TYPE_FILE','FILE_UPLOAD_SUCCESS',(filename,get['path']));
return public.returnMsg(True,'FILE_UPLOAD_SUCCESS');
-
-
#上传文件2
def upload(self,args):
- if sys.version_info[0] == 2:
- args.f_name = args.f_name.encode('utf-8')
- args.f_path = args.f_path.encode('utf-8')
if not 'f_name' in args:
args.f_name = request.form.get('f_name')
args.f_path = request.form.get('f_path')
args.f_size = request.form.get('f_size')
args.f_start = request.form.get('f_start')
+ if sys.version_info[0] == 2:
+ args.f_name = args.f_name.encode('utf-8')
+ args.f_path = args.f_path.encode('utf-8')
+
if args.f_name.find('./') != -1 or args.f_path.find('./') != -1:
return public.returnMsg(False,'错误的参数')
if not os.path.exists(args.f_path):
@@ -118,7 +177,10 @@ class files:
if os.path.exists(new_name):
if new_name.find('.user.ini') != -1:
public.ExecShell("chattr -i " + new_name)
- os.remove(new_name)
+ try:
+ os.remove(new_name)
+ except:
+ os.system("rm -f %s" % new_name)
os.renames(save_path, new_name)
if 'dir_mode' in args and 'file_mode' in args:
mode_tmp1 = args.dir_mode.split(',')
@@ -158,7 +220,9 @@ class files:
if not os.path.isdir(get.path):
get.path = os.path.dirname(get.path)
- import pwd
+ if not os.path.isdir(get.path): return public.returnMsg(False,'This is not a directory!')
+
+ import pwd
dirnames = []
filenames = []
@@ -329,14 +393,18 @@ class files:
if count >= max: break;
d = self.xssencode(d)
if d.lower().find(search) != -1:
- my_dirs.append(self.__get_stat(d_list[0] + '/' + d,get.path))
+ filename = d_list[0] + '/' + d
+ if not os.path.exists(filename): continue
+ my_dirs.append(self.__get_stat(filename,get.path))
count += 1
for f in d_list[2]:
if count >= max: break;
f = self.xssencode(f)
if f.lower().find(search) != -1:
- my_files.append(self.__get_stat(d_list[0] + '/' + f,get.path))
+ filename = d_list[0] + '/' + f
+ if not os.path.exists(filename): continue
+ my_files.append(self.__get_stat(filename,get.path))
count += 1
data = {}
data['DIR'] = sorted(my_dirs)
@@ -367,6 +435,7 @@ class files:
#计算文件数量
def GetFilesCount(self,path,search):
if os.path.isfile(path): return 1
+ if not os.path.exists(path):return 0
i=0;
for name in os.listdir(path):
if search:
@@ -426,10 +495,13 @@ class files:
if not self.delete_empty(get.path): return public.returnMsg(False,'DIR_ERR_NOT_EMPTY');
if os.path.exists('data/recycle_bin.pl'):
- if self.Mv_Recycle_bin(get): return public.returnMsg(True,'DIR_MOVE_RECYCLE_BIN');
+ if self.Mv_Recycle_bin(get):
+ self.site_path_safe(get)
+ return public.returnMsg(True,'DIR_MOVE_RECYCLE_BIN');
import shutil
shutil.rmtree(get.path)
+ self.site_path_safe(get)
public.WriteLog('TYPE_FILE','DIR_DEL_SUCCESS',(get.path,))
return public.returnMsg(True,'DIR_DEL_SUCCESS')
except:
@@ -453,8 +525,11 @@ class files:
os.system("chattr -i '"+get.path+"'")
try:
if os.path.exists('data/recycle_bin.pl'):
- if self.Mv_Recycle_bin(get): return public.returnMsg(True,'FILE_MOVE_RECYCLE_BIN');
+ if self.Mv_Recycle_bin(get):
+ self.site_path_safe(get)
+ return public.returnMsg(True,'FILE_MOVE_RECYCLE_BIN');
os.remove(get.path)
+ self.site_path_safe(get)
public.WriteLog('TYPE_FILE','FILE_DEL_SUCCESS',(get.path,))
return public.returnMsg(True,'FILE_DEL_SUCCESS')
except:
@@ -566,11 +641,16 @@ class files:
database.database().DeleteTo(path);
continue;
if os.path.isdir(path):
- #os.system('rm -rf ' + path);
- shutil.rmtree(path);
+ try:
+ shutil.rmtree(path);
+ except:
+ os.system('rm -rf ' + path);
else:
- #os.system('rm -f ' + path);
- os.remove(path);
+ try:
+ os.remove(path);
+ except:
+ os.system('rm -f ' + path);
+
public.writeSpeed(None,0,0);
public.WriteLog('TYPE_FILE','FILE_CLOSE_RECYCLE_BIN');
return public.returnMsg(True,'FILE_CLOSE_RECYCLE_BIN');
@@ -653,6 +733,7 @@ class files:
return public.returnMsg(False,'FILE_DANGER');
try:
self.move(get.sfile,get.dfile)
+ self.site_path_safe(get)
if hasattr(get,'rename'):
public.WriteLog('TYPE_FILE','RENAME',(get.sfile,get.dfile))
return public.returnMsg(True,'RENAME_SUCCESS')
@@ -668,6 +749,7 @@ class files:
data = [];
filesx = [];
if not hasattr(get,'filename'):
+ if not 'selected' in session: return []
filesx = json.loads(session['selected']['data']);
else:
filesx.append(get.filename);
@@ -694,6 +776,7 @@ class files:
public.writeFile(get.path,'');
if os.path.getsize(get.path) > 2097152: return public.returnMsg(False,'CANT_EDIT_ONLINE_FILE');
+ if not os.path.isfile(get.path): return public.returnMsg(False,'This is not a file!')
fp = open(get.path,'rb')
data = {}
data['status'] = True
@@ -725,11 +808,7 @@ class files:
else:
data['data'] = srcBody.decode(data['encoding'])
else:
- if sys.version_info[0] == 2:
- data['data'] = srcBody.decode('utf-8').encode('utf-8');
- else:
- data['data'] = srcBody.decode('utf-8')
- data['encoding'] = u'utf-8';
+ return public.returnMsg(False,'Failed to open file, file may be occupied by other processes!')
if hasattr(get,'filename'): get.path = get.filename
data['historys'] = self.get_history(get.path)
return data;
@@ -739,6 +818,8 @@ class files:
#保存文件
def SaveFileBody(self,get):
+ if not 'path' in get: return public.returnMsg(False,'path parameter cannot be empty!')
+ if not 'data' in get: return public.returnMsg(False,'data parameter cannot be empty!')
if sys.version_info[0] == 2: get.path = get.path.encode('utf-8');
if not os.path.exists(get.path):
if get.path.find('.htaccess') == -1:
@@ -969,6 +1050,7 @@ class files:
except:
continue;
public.writeSpeed(None,0,0);
+ self.site_path_safe(get)
public.WriteLog('TYPE_FILE','FILE_ALL_DEL')
return public.returnMsg(True,'FILE_ALL_DEL')
@@ -978,7 +1060,9 @@ class files:
import shutil
if sys.version_info[0] == 2: get.path = get.path.encode('utf-8');
if not self.CheckDir(get.path): return public.returnMsg(False,'FILE_DANGER');
+ if not 'selected' in session: return public.returnMsg(False,'The operation failed, please re-copy the copy or cut process')
i = 0;
+ if not 'selected' in session:return public.returnMsg(False,'The operation failed, please re-operate');
myfiles = json.loads(session['selected']['data'])
l = len(myfiles);
if get.type == '1':
@@ -1016,6 +1100,7 @@ class files:
self.move(sfile,dfile)
except:
continue;
+ self.site_path_safe(get)
public.WriteLog('TYPE_FILE','FILE_ALL_MOTE',(session['selected']['path'],get.path))
public.writeSpeed(None,0,0);
errorCount = len(myfiles) - i
@@ -1027,6 +1112,7 @@ class files:
if not os.path.exists(sfile): return False
is_dir = os.path.isdir(sfile)
if not os.path.exists(dfile) or not is_dir:
+ if os.path.exists(dfile): os.remove(dfile)
shutil.move(sfile, dfile)
else:
self.copytree(sfile,dfile)
@@ -1311,6 +1397,7 @@ cd %s
#添加收藏夹分类
def add_files_store_types(self,get):
file_type = get.file_type
+ if sys.version_info[0] == 2: file_type = file_type.decode('utf-8')
data = self.get_store_data()
if file_type in data: return public.returnMsg(False,'Do not add categories repeatedly!')
@@ -1321,11 +1408,14 @@ cd %s
#删除收藏夹分类
def del_files_store_types(self,get):
file_type = get.file_type
+ if sys.version_info[0] == 2: file_type = file_type.decode('utf-8')
if file_type == 'Default category': return public.returnMsg(False,'Default category cannot be deleted!')
data = self.get_store_data()
- del data[file_type]
- self.set_store_data(data)
- return public.returnMsg(True,'Delete [' + file_type + '] successfully!')
+ if file_type in data:
+ del data[file_type]
+ self.set_store_data(data)
+ return public.returnMsg(True,'Delete [' + file_type + '] successfully!')
+ return public.returnMsg(False,'Delete [' + file_type + '] successfully!')
#获取收藏夹
def get_files_store(self,get):
@@ -1348,6 +1438,7 @@ cd %s
#添加收藏夹
def add_files_store(self,get):
file_type = get.file_type
+ if sys.version_info[0] == 2: file_type = file_type.decode('utf-8')
path = get.path
if not os.path.exists(path): return public.returnMsg(False,'File or directory does not exist!')
@@ -1361,8 +1452,8 @@ cd %s
#删除收藏夹
def del_files_store(self,get):
file_type = get.file_type
+ if sys.version_info[0] == 2: file_type = file_type.decode('utf-8')
path = get.path
-
data = self.get_store_data()
if not file_type in data: return public.returnMsg(False,'Cannot find this favorite category!')
data[file_type].remove(path)
diff --git a/class/nginx.py b/class/nginx.py
index 2df4a9e4..3ba32dfc 100644
--- a/class/nginx.py
+++ b/class/nginx.py
@@ -2,7 +2,7 @@
#-------------------------------------------------------------------
# 宝塔Linux面板
#-------------------------------------------------------------------
-# Copyright (c) 2015-2018 宝塔软件(http:#bt.cn) All rights reserved.
+# Copyright (c) 2015-2099 宝塔软件(http:#bt.cn) All rights reserved.
#-------------------------------------------------------------------
# Author: 黄文良 <287962566@qq.com>
#-------------------------------------------------------------------
diff --git a/class/panelDnsapi.py b/class/panelDnsapi.py
index 1137f91c..78692def 100644
--- a/class/panelDnsapi.py
+++ b/class/panelDnsapi.py
@@ -50,16 +50,31 @@ import public
def extract_zone(domain_name):
- domain_name = domain_name.lstrip("*.")
- if domain_name.count(".") > 1:
- zone, middle, last = str(domain_name).rsplit(".", 2)
- root = ".".join([middle, last])
- acme_txt = "_acme-challenge.%s" % zone
- else:
- zone = ""
- root = domain_name
- acme_txt = "_acme-challenge"
- return root, zone, acme_txt
+ domain_name = domain_name.lstrip("*.")
+
+ #处理地区域名
+ top_domain_list = ['.ac.cn', '.ah.cn', '.bj.cn', '.com.cn', '.cq.cn', '.fj.cn', '.gd.cn',
+ '.gov.cn', '.gs.cn', '.gx.cn', '.gz.cn', '.ha.cn', '.hb.cn', '.he.cn',
+ '.hi.cn', '.hk.cn', '.hl.cn', '.hn.cn', '.jl.cn', '.js.cn', '.jx.cn',
+ '.ln.cn', '.mo.cn', '.net.cn', '.nm.cn', '.nx.cn', '.org.cn']
+ m_count = domain_name.count(".")
+ top_domain = "."+".".join(domain_name.rsplit('.')[-2:])
+ new_top_domain = "." + top_domain.replace(".","")
+ is_tow_top = False
+ if top_domain in top_domain_list:
+ is_tow_top = True
+ domain_name = domain_name[:-len(top_domain)] + new_top_domain #地区域名后缀去点处理
+
+ if domain_name.count(".") > 1:
+ zone, middle, last = domain_name.rsplit(".", 2)
+ acme_txt = "_acme-challenge.%s" % zone
+ if is_tow_top: last = top_domain[1:] #还原地区域名后缀
+ root = ".".join([middle, last])
+ else:
+ zone = ""
+ root = domain_name
+ acme_txt = "_acme-challenge"
+ return root, zone, acme_txt
class AliyunDns(object):
def __init__(self, key, secret, ):
@@ -257,17 +272,6 @@ class CloudxnsDns(object):
class Dns_com(object):
- def extract_zone(self,domain_name):
- domain_name = domain_name.lstrip("*.")
- if domain_name.count(".") > 1:
- zone, middle, last = str(domain_name).rsplit(".", 2)
- root = ".".join([middle, last])
- acme_txt = "_acme-challenge.%s" % zone
- else:
- zone = ""
- root = domain_name
- acme_txt = "_acme-challenge"
- return root, zone, acme_txt
def get_dns_obj(self):
p_path = '/www/server/panel/plugin/dns'
diff --git a/class/panelLets.py b/class/panelLets.py
index e8517d4e..63a16f25 100644
--- a/class/panelLets.py
+++ b/class/panelLets.py
@@ -57,27 +57,45 @@ class panelLets:
ret = p12.set_ca_certificates((crypto.load_certificate(crypto.FILETYPE_PEM, ca_pem.encode()),) )
if friendly_name:
ret = p12.set_friendlyname(friendly_name.encode())
- return p12
+ return p12
+
+ #域名处理
+ def extract_zone(self,domain_name):
+ domain_name = domain_name.lstrip("*.")
+
+ #处理地区域名
+ top_domain_list = ['.ac.cn', '.ah.cn', '.bj.cn', '.com.cn', '.cq.cn', '.fj.cn', '.gd.cn',
+ '.gov.cn', '.gs.cn', '.gx.cn', '.gz.cn', '.ha.cn', '.hb.cn', '.he.cn',
+ '.hi.cn', '.hk.cn', '.hl.cn', '.hn.cn', '.jl.cn', '.js.cn', '.jx.cn',
+ '.ln.cn', '.mo.cn', '.net.cn', '.nm.cn', '.nx.cn', '.org.cn']
+ m_count = domain_name.count(".")
+ top_domain = "."+".".join(domain_name.rsplit('.')[-2:])
+ new_top_domain = "." + top_domain.replace(".","")
+ is_tow_top = False
+ if top_domain in top_domain_list:
+ is_tow_top = True
+ domain_name = domain_name[:-len(top_domain)] + new_top_domain #地区域名后缀去点处理
+
+ if domain_name.count(".") > 1:
+ zone, middle, last = domain_name.rsplit(".", 2)
+ acme_txt = "_acme-challenge.%s" % zone
+ if is_tow_top: last = top_domain[1:] #还原地区域名
+ root = ".".join([middle, last])
+ else:
+ zone = ""
+ root = domain_name
+ acme_txt = "_acme-challenge"
+ return root, zone, acme_txt
#获取根域名
def get_root_domain(self,domain_name):
- if domain_name.count(".") != 1:
- pos = domain_name.rfind(".", 0, domain_name.rfind("."))
- subd = domain_name[:pos]
- domain_name = domain_name[pos + 1 :]
- return domain_name
-
+ d_root,tow_name,acme_txt = self.extract_zone(domain_name)
+ return d_root
+
#获取acmename
def get_acme_name(self,domain_name):
- domain_name = domain_name.lstrip("*.")
- if domain_name.count(".") > 1:
- zone, middle, last = str(domain_name).rsplit(".", 2)
- root = ".".join([middle, last])
- acme_name = "_acme-challenge.%s.%s" % (zone,root)
- else:
- root = domain_name
- acme_name = "_acme-challenge.%s" % root
- return acme_name
+ d_root,tow_name,acme_txt = self.extract_zone(domain_name)
+ return acme_txt + '.' + d_root
#格式化错误输出
def get_error(self,error):
@@ -286,7 +304,7 @@ class panelLets:
cronPath = public.GetConfigValue('setup_path') + '/cron/' + echo
shell = 'python %s/panel/class/panelLets.py renew_lets_ssl ' % (self.setupPath)
public.writeFile(cronPath,shell)
- args_obj.id = public.M('crontab').add('name,type,where1,where_hour,where_minute,echo,addtime,status,save,backupTo,sType,sName,sBody,urladdress',("续签Let's Encrypt证书",'day','','0','10',echo,time.strftime('%Y-%m-%d %X',time.localtime()),0,'','localhost','toShell','',shell,''))
+ args_obj.id = public.M('crontab').add('name,type,where1,where_hour,where_minute,echo,addtime,status,save,backupTo,sType,sName,sBody,urladdress',("Renew the Letter's Encrypt certificate",'day','','0','10',echo,time.strftime('%Y-%m-%d %X',time.localtime()),0,'','localhost','toShell','',shell,''))
crontab.crontab().set_cron_status(args_obj)
else:
cron_path = public.get_cron_path()
diff --git a/class/panelPlugin.py b/class/panelPlugin.py
index bfffc6b0..fca43db5 100644
--- a/class/panelPlugin.py
+++ b/class/panelPlugin.py
@@ -6,7 +6,7 @@
#-------------------------------------------------------------------
# Author: 黄文良 <287962566@qq.com>
#-------------------------------------------------------------------
-import public,os,sys,json,time,psutil,py_compile
+import public,os,sys,json,time,psutil,py_compile,re
from BTPanel import session,cache
if sys.version_info[0] == 3: from importlib import reload
class mget: pass;
@@ -119,6 +119,7 @@ class panelPlugin:
#安装插件
def install_plugin(self,get):
if not self.check_sys_write(): return public.returnMsg(False,'CANT_WRITE_SYS_DIR')
+ if not 'sName' in get: return public.returnMsg(False,'Please specify the software name!')
pluginInfo = self.get_soft_find(get.sName);
p_node = '/www/server/panel/install/public.sh'
if os.path.exists(p_node):
@@ -343,6 +344,10 @@ class panelPlugin:
else:
if name1 == 'pure': name1 = 'pure-ftpd';
if name1 == sName: isTask = task['status'];
+
+ if isTask == '-1' or isTask == '0':
+ if task['name'].find('upgrade') != -1: isTask = '-2'
+ break
return isTask
@@ -712,23 +717,38 @@ class panelPlugin:
versions[i]['setup'] = (version[:vLen] == vTmp)
return versions
+ #取pids
+ def get_pids(self):
+ pids = []
+ for pid in os.listdir('/proc'):
+ if re.match("^\d+$",pid): pids.append(pid)
+ return pids
#进程是否存在
def process_exists(self,pname,exe = None):
- try:
- if not self.pids: self.pids = psutil.pids()
- for pid in self.pids:
- try:
- p = psutil.Process(pid)
- if p.name() == pname:
- if not exe:
- return True;
- else:
- if p.exe() == exe: return True
- except:pass
- return False
- except: return True
+ if not self.pids: self.pids = psutil.pids() #self.get_pids() #
+ for pid in self.pids:
+ l = '/proc/%s/exe' % pid
+ f = '/proc/%s/comm' % pid
+ p_exe = ''
+ p_name = ''
+ if os.path.exists(l):
+ p_exe = os.readlink(l)
+ if not p_name: p_name = p_exe.split('/')[-1]
+
+ if not p_name and os.path.exists(f):
+ fp = open(f,'r')
+ p_name = fp.read().strip()
+ fp.close()
+
+ if not p_name: continue
+ if p_name == pname:
+ if not exe:
+ return True;
+ else:
+ if p_exe == exe: return True
+ return False
#取分页
def get_page(self,data,get):
diff --git a/class/panelSite.py b/class/panelSite.py
index 2f47c728..2ad1db5b 100644
--- a/class/panelSite.py
+++ b/class/panelSite.py
@@ -289,7 +289,10 @@ class panelSite(panelRedirect):
#创建根目录
if not os.path.exists(self.sitePath):
- os.makedirs(self.sitePath)
+ try:
+ os.makedirs(self.sitePath)
+ except Exception as ex:
+ return public.returnMsg(False,'Failed to create site root directory, %s' % ex)
public.ExecShell('chmod -R 755 ' + self.sitePath);
public.ExecShell('chown -R www:www ' + self.sitePath);
@@ -549,6 +552,7 @@ class panelSite(panelRedirect):
if isError != True:
return public.returnMsg(False,'ERROR: %s
' % public.GetMsg("GET_ERR_IN_CONFILE") +isError.replace("\n",' ')+' ');
+ if not 'domain' in get: return public.returnMsg(False,'Please fill in the domain name!')
if len(get.domain) < 3: return public.returnMsg(False,'SITE_ADD_DOMAIN_ERR_EMPTY');
domains = get.domain.replace(' ','').split(',')
@@ -576,8 +580,6 @@ class panelSite(panelRedirect):
if public.M('binding').where('domain=?',(get.domain,)).count():
return public.returnMsg(False,'SITE_ADD_ERR_DOMAIN_EXISTS');
-
-
#写配置文件
self.NginxDomain(get)
try:
@@ -720,6 +722,8 @@ class panelSite(panelRedirect):
#删除域名
def DelDomain(self,get):
+ if not 'id' in get:return public.returnMsg(False,'Please choose a domain name')
+ if not 'port' in get: return public.returnMsg(False, 'Please choose a port')
sql = public.M('domain');
id=get['id'];
port = get.port;
@@ -846,7 +850,9 @@ class panelSite(panelRedirect):
if not get.id: return False;
if type(get.id) == list: get.id = get.id[0]['id'];
result = self.GetSiteRunPath(get);
- return result['runPath'];
+ if 'runPath' in result:
+ return result['runPath'];
+ return False
# 创建Let's Encrypt免费证书
@@ -1838,7 +1844,7 @@ server
public.M('binding').where("id=?",(id,)).delete();
filename = self.setupPath + '/panel/vhost/rewrite/' + siteName + '_' + binding['path'] + '.conf';
- if os.path.exists(filename): os.remove(filename)
+ if os.path.exists(filename): os.system('rm -rf %s'%filename)
public.serviceReload();
public.WriteLog('TYPE_SITE', 'SITE_BINDING_DEL_SUCCESS',(siteName,binding['path']));
return public.returnMsg(True,'DEL_SUCCESS')
@@ -1888,9 +1894,10 @@ server
rep = "\s+index\s+(.+);";
else:
rep = "DirectoryIndex\s+(.+)\n";
-
- tmp = re.search(rep,conf).groups()
- return tmp[0].replace(' ',',')
+ if re.search(rep,conf):
+ tmp = re.search(rep,conf).groups()
+ return tmp[0].replace(' ',',')
+ return public.returnMsg(False,'Failed to get, there is no default document in the configuration file')
#设置默认文档
def SetIndex(self,get):
@@ -2044,7 +2051,8 @@ server
#是否开启目录防御
def GetDirUserINI(self,get):
- path = get.path + self.GetRunPath(get);
+ path = get.path + self.GetRunPath(get)
+ if not path:return public.returnMsg(False,'Failed to get directory')
id = get.id;
get.name = public.M('sites').where("id=?",(id,)).getField('name');
data = {}
@@ -3011,6 +3019,7 @@ location %s
get.siteName = public.M('sites').where('id=?',(get.id,)).getField('name');
get.configFile = self.setupPath + '/panel/vhost/nginx/' + get.siteName + '.conf';
conf = public.readFile(get.configFile);
+ if type(conf)==bool:return False
if conf.find('#AUTH_START') != -1: return True;
return False;
diff --git a/class/panelTask.py b/class/panelTask.py
index e0d8b038..f9f9b093 100644
--- a/class/panelTask.py
+++ b/class/panelTask.py
@@ -59,7 +59,6 @@ class bt_task:
if type(data) == str:
public.WriteLog('Task queue',data)
return []
-
if not 'num' in get: get.num = 15
num = int(get.num)
for i in range(len(data)):
@@ -160,11 +159,14 @@ class bt_task:
#开始检测任务
def start_task(self):
noe = False
- while True:
+ n = 0
+ while True:
try:
time.sleep(1);
- if not os.path.exists(self.__task_tips) and noe: continue;
+ n+=1
+ if not os.path.exists(self.__task_tips) and noe and n < 60: continue;
if os.path.exists(self.__task_tips): os.remove(self.__task_tips)
+ n = 0
public.M(self.__table).where('status=?',('-1',)).setField('status',0)
task_list = self.get_task_list(0)
for task_info in task_list:
@@ -385,7 +387,6 @@ class bt_task:
os.system('chown -R www:www ' + filename)
# os.system('chmod -R 755 ' + filename)
a = 'find {filename} -type d |xargs chmod 0755'.format(filename=filename)
- public.writeFile("/tmp/2",str(a))
os.system(a)
os.system('find {filename} -type f |xargs chmod 0644'.format(filename=filename))
diff --git a/class/plugin_deployment.py b/class/plugin_deployment.py
index 605b758f..e977aeb5 100644
--- a/class/plugin_deployment.py
+++ b/class/plugin_deployment.py
@@ -243,10 +243,11 @@ class plugin_deployment:
site_name = get.site_name;
php_version = get.php_version;
#取基础信息
- find = public.M('sites').where('name=?',(site_name,)).field('id,path,name').find();
- path = find['path'];
+ find = public.M('sites').where('name=?',(site_name,)).field('id,path,name').find()
+ if not 'path' in find:
+ return public.returnMsg(False, 'Site not exist!')
+ path = find['path']
if path.replace('//','/') == '/': return public.returnMsg(False,'Dangerous website root directory!')
-
#获取包信息
pinfo = self.GetPackageInfo(name);
id = pinfo['id']
diff --git a/class/public.py b/class/public.py
index e03e70b8..a3b14ebc 100644
--- a/class/public.py
+++ b/class/public.py
@@ -285,8 +285,10 @@ def GetJson(data):
"""
from json import dumps
if data == bytes: data = data.decode('utf-8')
- return dumps(data)
-
+ try:
+ return dumps(data)
+ except:
+ return dumps(returnMsg(False,"Wrong response: %s" % str(data)))
def getJson(data):
return GetJson(data)
@@ -304,11 +306,18 @@ def ReadFile(filename, mode='r'):
fp = open(filename, mode)
f_body = fp.read()
fp.close()
- except:
- fp = open(filename, mode, encoding="utf-8")
- f_body = fp.read()
- fp.close()
-
+ except Exception as ex:
+ if sys.version_info[0] != 2:
+ try:
+ fp = open(filename, mode,encoding="utf-8");
+ f_body = fp.read()
+ fp.close()
+ except Exception as ex2:
+ WriteLog('Open File',str(ex2))
+ return False
+ else:
+ WriteLog('Open File',str(ex))
+ return False
return f_body
@@ -505,17 +514,14 @@ def GetLocalIp():
filename = 'data/iplist.txt'
ipaddress = readFile(filename)
if not ipaddress:
- import urllib2
url = 'http://pv.sohu.com/cityjson?ie=utf-8'
- opener = urllib2.urlopen(url)
- m_str = opener.read()
- ipaddress = re.search('\d+.\d+.\d+.\d+', m_str).group(0)
- WriteFile(filename, ipaddress)
+ m_str = HttpGet(url)
+ ipaddress = re.search('\d+.\d+.\d+.\d+',m_str).group(0)
+ WriteFile(filename,ipaddress)
c_ip = check_ip(ipaddress)
if not c_ip: return GetHost()
return ipaddress
except:
- return get_error_info()
try:
url = GetConfigValue('home') + '/Api/getIpAddress';
return HttpGet(url)
diff --git a/class/pyotp/__init__.py b/class/pyotp/__init__.py
new file mode 100644
index 00000000..b400ff4f
--- /dev/null
+++ b/class/pyotp/__init__.py
@@ -0,0 +1,23 @@
+from __future__ import (absolute_import, division,
+ print_function, unicode_literals)
+
+from pyotp.hotp import HOTP # noqa
+from pyotp.otp import OTP # noqa
+from pyotp.totp import TOTP # noqa
+from . import utils # noqa
+
+def random_base32(length=16, random=None,
+ chars=list('ABCDEFGHIJKLMNOPQRSTUVWXYZ234567')):
+
+ # Use secrets module if available (Python version >= 3.6) per PEP 506
+ try:
+ import secrets
+ random = secrets.SystemRandom()
+ except ImportError:
+ import random as _random
+ random = _random.SystemRandom()
+
+ return ''.join(
+ random.choice(chars)
+ for _ in range(length)
+ )
diff --git a/class/pyotp/compat.py b/class/pyotp/compat.py
new file mode 100644
index 00000000..56499098
--- /dev/null
+++ b/class/pyotp/compat.py
@@ -0,0 +1,10 @@
+from __future__ import absolute_import, division, print_function, unicode_literals
+
+import sys
+
+USING_PYTHON2 = True if sys.version_info < (3, 0) else False
+
+if USING_PYTHON2:
+ str = unicode # noqa
+else:
+ str = str
diff --git a/class/pyotp/hotp.py b/class/pyotp/hotp.py
new file mode 100644
index 00000000..bb9e3f1d
--- /dev/null
+++ b/class/pyotp/hotp.py
@@ -0,0 +1,58 @@
+from __future__ import absolute_import, division, print_function, unicode_literals
+
+from . import utils
+from .otp import OTP
+from .compat import str
+
+class HOTP(OTP):
+ """
+ Handler for HMAC-based OTP counters.
+ """
+ def at(self, count):
+ """
+ Generates the OTP for the given count.
+
+ :param count: the OTP HMAC counter
+ :type count: int
+ :returns: OTP
+ :rtype: str
+ """
+ return self.generate_otp(count)
+
+ def verify(self, otp, counter):
+ """
+ Verifies the OTP passed in against the current counter OTP.
+
+ :param otp: the OTP to check against
+ :type otp: str
+ :param count: the OTP HMAC counter
+ :type count: int
+ """
+ return utils.strings_equal(str(otp), str(self.at(counter)))
+
+ def provisioning_uri(self, name, initial_count=0, issuer_name=None):
+ """
+ Returns the provisioning URI for the OTP. This can then be
+ encoded in a QR Code and used to provision an OTP app like
+ Google Authenticator.
+
+ See also:
+ https://github.com/google/google-authenticator/wiki/Key-Uri-Format
+
+ :param name: name of the user account
+ :type name: str
+ :param initial_count: starting HMAC counter value, defaults to 0
+ :type initial_count: int
+ :param issuer_name: the name of the OTP issuer; this will be the
+ organization title of the OTP entry in Authenticator
+ :returns: provisioning URI
+ :rtype: str
+ """
+ return utils.build_uri(
+ self.secret,
+ name,
+ initial_count=initial_count,
+ issuer_name=issuer_name,
+ algorithm=self.digest().name,
+ digits=self.digits
+ )
diff --git a/class/pyotp/otp.py b/class/pyotp/otp.py
new file mode 100644
index 00000000..45afcf3d
--- /dev/null
+++ b/class/pyotp/otp.py
@@ -0,0 +1,66 @@
+from __future__ import absolute_import, division, print_function, unicode_literals
+
+import base64
+import hashlib
+import hmac
+from .compat import str
+
+class OTP(object):
+ """
+ Base class for OTP handlers.
+ """
+ def __init__(self, s, digits=6, digest=hashlib.sha1):
+ """
+ :param s: secret in base32 format
+ :type s: str
+ :param digits: number of integers in the OTP. Some apps expect this to be 6 digits, others support more.
+ :type digits: int
+ :param digest: digest function to use in the HMAC (expected to be sha1)
+ :type digest: callable
+ """
+ self.digits = digits
+ self.digest = digest
+ self.secret = s
+
+ def generate_otp(self, input):
+ """
+ :param input: the HMAC counter value to use as the OTP input.
+ Usually either the counter, or the computed integer based on the Unix timestamp
+ :type input: int
+ """
+ if input < 0:
+ raise ValueError('input must be positive integer')
+ hasher = hmac.new(self.byte_secret(), self.int_to_bytestring(input), self.digest)
+ hmac_hash = bytearray(hasher.digest())
+ offset = hmac_hash[-1] & 0xf
+ code = ((hmac_hash[offset] & 0x7f) << 24 |
+ (hmac_hash[offset + 1] & 0xff) << 16 |
+ (hmac_hash[offset + 2] & 0xff) << 8 |
+ (hmac_hash[offset + 3] & 0xff))
+ str_code = str(code % 10 ** self.digits)
+ while len(str_code) < self.digits:
+ str_code = '0' + str_code
+
+ return str_code
+
+ def byte_secret(self):
+ missing_padding = len(self.secret) % 8
+ if missing_padding != 0:
+ self.secret += '=' * (8 - missing_padding)
+ return base64.b32decode(self.secret, casefold=True)
+
+ @staticmethod
+ def int_to_bytestring(i, padding=8):
+ """
+ Turns an integer to the OATH specified
+ bytestring, which is fed to the HMAC
+ along with the secret
+ """
+ result = bytearray()
+ while i != 0:
+ result.append(i & 0xFF)
+ i >>= 8
+ # It's necessary to convert the final result from bytearray to bytes
+ # because the hmac functions in python 2.6 and 3.3 don't work with
+ # bytearray
+ return bytes(bytearray(reversed(result)).rjust(padding, b'\0'))
diff --git a/class/pyotp/totp.py b/class/pyotp/totp.py
new file mode 100644
index 00000000..50faf8bd
--- /dev/null
+++ b/class/pyotp/totp.py
@@ -0,0 +1,92 @@
+from __future__ import absolute_import, division, print_function, unicode_literals
+
+import datetime
+import time
+
+from . import utils
+from .otp import OTP
+from .compat import str
+
+class TOTP(OTP):
+ """
+ Handler for time-based OTP counters.
+ """
+ def __init__(self, *args, **kwargs):
+ """
+ :param interval: the time interval in seconds
+ for OTP. This defaults to 30.
+ :type interval: int
+ """
+ self.interval = kwargs.pop('interval', 30)
+ super(TOTP, self).__init__(*args, **kwargs)
+
+ def at(self, for_time, counter_offset=0):
+ """
+ Accepts either a Unix timestamp integer or a datetime object.
+
+ :param for_time: the time to generate an OTP for
+ :type for_time: int or datetime
+ :param counter_offset: the amount of ticks to add to the time counter
+ :returns: OTP value
+ :rtype: str
+ """
+ if not isinstance(for_time, datetime.datetime):
+ for_time = datetime.datetime.fromtimestamp(int(for_time))
+ return self.generate_otp(self.timecode(for_time) + counter_offset)
+
+ def now(self):
+ """
+ Generate the current time OTP
+
+ :returns: OTP value
+ :rtype: str
+ """
+ return self.generate_otp(self.timecode(datetime.datetime.now()))
+
+ def verify(self, otp, for_time=None, valid_window=0):
+ """
+ Verifies the OTP passed in against the current time OTP.
+
+ :param otp: the OTP to check against
+ :type otp: str
+ :param for_time: Time to check OTP at (defaults to now)
+ :type for_time: int or datetime
+ :param valid_window: extends the validity to this many counter ticks before and after the current one
+ :type valid_window: int
+ :returns: True if verification succeeded, False otherwise
+ :rtype: bool
+ """
+ if for_time is None:
+ for_time = datetime.datetime.now()
+
+ if valid_window:
+ for i in range(-valid_window, valid_window + 1):
+ if utils.strings_equal(str(otp), str(self.at(for_time, i))):
+ return True
+ return False
+
+ return utils.strings_equal(str(otp), str(self.at(for_time)))
+
+ def provisioning_uri(self, name, issuer_name=None):
+ """
+ Returns the provisioning URI for the OTP. This can then be
+ encoded in a QR Code and used to provision an OTP app like
+ Google Authenticator.
+
+ See also:
+ https://github.com/google/google-authenticator/wiki/Key-Uri-Format
+
+ :param name: name of the user account
+ :type name: str
+ :param issuer_name: the name of the OTP issuer; this will be the
+ organization title of the OTP entry in Authenticator
+ :returns: provisioning URI
+ :rtype: str
+ """
+ return utils.build_uri(self.secret, name, issuer_name=issuer_name,
+ algorithm=self.digest().name,
+ digits=self.digits, period=self.interval)
+
+ def timecode(self, for_time):
+ i = time.mktime(for_time.timetuple())
+ return int(i / self.interval)
diff --git a/class/pyotp/utils.py b/class/pyotp/utils.py
new file mode 100644
index 00000000..ebfe3adb
--- /dev/null
+++ b/class/pyotp/utils.py
@@ -0,0 +1,109 @@
+from __future__ import absolute_import, division, print_function, unicode_literals
+
+import unicodedata
+try:
+ from itertools import izip_longest
+except ImportError:
+ from itertools import zip_longest as izip_longest
+
+try:
+ from urllib.parse import quote, urlencode
+except ImportError:
+ from urllib import quote, urlencode
+
+
+def build_uri(secret, name, initial_count=None, issuer_name=None,
+ algorithm=None, digits=None, period=None):
+ """
+ Returns the provisioning URI for the OTP; works for either TOTP or HOTP.
+
+ This can then be encoded in a QR Code and used to provision the Google
+ Authenticator app.
+
+ For module-internal use.
+
+ See also:
+ https://github.com/google/google-authenticator/wiki/Key-Uri-Format
+
+ :param secret: the hotp/totp secret used to generate the URI
+ :type secret: str
+ :param name: name of the account
+ :type name: str
+ :param initial_count: starting counter value, defaults to None.
+ If none, the OTP type will be assumed as TOTP.
+ :type initial_count: int
+ :param issuer_name: the name of the OTP issuer; this will be the
+ organization title of the OTP entry in Authenticator
+ :type issuer_name: str
+ :param algorithm: the algorithm used in the OTP generation.
+ :type algorithm: str
+ :param digits: the length of the OTP generated code.
+ :type digits: int
+ :param period: the number of seconds the OTP generator is set to
+ expire every code.
+ :type period: int
+ :returns: provisioning uri
+ :rtype: str
+ """
+ # initial_count may be 0 as a valid param
+ is_initial_count_present = (initial_count is not None)
+
+ # Handling values different from defaults
+ is_algorithm_set = (algorithm is not None and algorithm != 'sha1')
+ is_digits_set = (digits is not None and digits != 6)
+ is_period_set = (period is not None and period != 30)
+
+ otp_type = 'hotp' if is_initial_count_present else 'totp'
+ base_uri = 'otpauth://{0}/{1}?{2}'
+
+ url_args = {'secret': secret}
+
+ label = quote(name)
+ if issuer_name is not None:
+ label = quote(issuer_name) + ':' + label
+ url_args['issuer'] = issuer_name
+
+ if is_initial_count_present:
+ url_args['counter'] = initial_count
+ if is_algorithm_set:
+ url_args['algorithm'] = algorithm.upper()
+ if is_digits_set:
+ url_args['digits'] = digits
+ if is_period_set:
+ url_args['period'] = period
+
+ uri = base_uri.format(otp_type, label, urlencode(url_args).replace("+", "%20"))
+ return uri
+
+
+def _compare_digest(s1, s2):
+ differences = 0
+ for c1, c2 in izip_longest(s1, s2):
+ if c1 is None or c2 is None:
+ differences = 1
+ continue
+ differences |= ord(c1) ^ ord(c2)
+ return differences == 0
+
+
+try:
+ # Python 3.3+ and 2.7.7+ include a timing-attack-resistant
+ # comparison function, which is probably more reliable than ours.
+ # Use it if available.
+ from hmac import compare_digest
+except ImportError:
+ compare_digest = _compare_digest
+
+
+def strings_equal(s1, s2):
+ """
+ Timing-attack resistant string comparison.
+
+ Normal comparison using == will short-circuit on the first mismatching
+ character. This avoids that by scanning the whole string, though we
+ still reveal to a timing attack whether the strings are the same
+ length.
+ """
+ s1 = unicodedata.normalize('NFKC', s1)
+ s2 = unicodedata.normalize('NFKC', s2)
+ return compare_digest(s1.encode("utf-8"), s2.encode("utf-8"))
diff --git a/class/site_dir_auth.py b/class/site_dir_auth.py
index ff4eb16a..83f09b0e 100644
--- a/class/site_dir_auth.py
+++ b/class/site_dir_auth.py
@@ -71,10 +71,11 @@ class SiteDirAuth:
if not get.name:
return public.returnMsg(False, 'Please enter the Name')
- if site_dir[0] == "/":
- site_dir = site_dir[1:]
- if site_dir[-1] == "/":
- site_dir = site_dir[:-1]
+ if site_dir[0] != "/" or site_dir[-1] != "/":
+ return public.returnMsg(False, 'Directory format is incorrect')
+ # site_dir = site_dir[1:]
+ # if site_dir[-1] == "/":
+ # site_dir = site_dir[:-1]
passwd = public.hasPwd(get.password)
site_info = self.get_site_info(get.id)
site_name = site_info["site_name"]
@@ -126,7 +127,7 @@ class SiteDirAuth:
file_path = "{setup_path}/panel/vhost/{webserver}/dir_auth/{site_name}"
if i == "nginx":
# 设置nginx
- conf = '''location ~* ^/%s/* {
+ conf = '''location ~* ^%s* {
#AUTH_START
auth_basic "Authorization";
auth_basic_user_file %s;
@@ -134,7 +135,7 @@ class SiteDirAuth:
}''' % (site_dir,auth_file)
else:
# 设置apache
- conf = '''
+ conf = '''
#AUTH_START
AuthType basic
AuthName "Authorization "
diff --git a/class/userlogin.py b/class/userlogin.py
index 2996a0fa..e9d58936 100644
--- a/class/userlogin.py
+++ b/class/userlogin.py
@@ -14,7 +14,7 @@ from flask import request,redirect,g
class userlogin:
def request_post(self,post):
- if not (hasattr(post, 'username') or hasattr(post, 'password') or hasattr(post, 'code')):
+ if not hasattr(post, 'username') or not hasattr(post, 'password'):
return public.returnJson(False,'LOGIN_USER_EMPTY'),json_header
self.error_num(False)
@@ -27,6 +27,7 @@ class userlogin:
m_code = cache.get('codeStr')
if 'code' in session:
if session['code']:
+ if not hasattr(post, 'code'): return public.returnMsg(False,'Verification code can not be empty!')
if not public.checkCode(post.code):
public.WriteLog('TYPE_LOGIN','LOGIN_ERR_CODE',('****','****',public.GetClientIp()));
return public.returnJson(False,'CODE_ERR'),json_header
@@ -36,18 +37,28 @@ class userlogin:
public.WriteLog('TYPE_LOGIN','LOGIN_ERR_PASS',('****','******',public.GetClientIp()));
num = self.limit_address('+');
return public.returnJson(False,'LOGIN_USER_ERR',(str(num),)),json_header
-
- session['login'] = True;
- session['username'] = userInfo['username'];
- public.WriteLog('TYPE_LOGIN','LOGIN_SUCCESS',(userInfo['username'],public.GetClientIp()));
- self.limit_address('-');
- cache.delete('panelNum')
- cache.delete('dologin')
- sess_input_path = 'data/session_last.pl'
- public.writeFile(sess_input_path,str(int(time.time())))
- self.set_request_token()
- self.login_token()
- return public.returnJson(True,'LOGIN_SUCCESS'),json_header
+ _key_file = "/www/server/panel/data/two_step_auth.txt"
+ if hasattr(post,'vcode'):
+ import pyotp
+ secret_key = public.readFile(_key_file)
+ if not secret_key:
+ return public.returnMsg(False, "Did not find the key, please close Google verification on the command line and trun on again")
+ t = pyotp.TOTP(secret_key)
+ result = t.verify(post.vcode)
+ if not result:
+ num = self.limit_address('++',v="vcode")
+ return public.returnJson(False, 'Invalid Verification code. You have [{}] times left to try!'.format(num)), json_header
+ now = int(time.time())
+ public.writeFile("/www/server/panel/data/dont_vcode_ip.txt",json.dumps({"client_ip":public.GetClientIp(),"add_time":now}))
+ self.limit_address('--',v="vcode")
+ return self._set_login_session(userInfo)
+
+ acc_client_ip = self.check_two_step_auth()
+
+ if not os.path.exists(_key_file) or acc_client_ip:
+ return self._set_login_session(userInfo)
+ self.limit_address('-')
+ return "1"
except Exception as ex:
stringEx = str(ex)
if stringEx.find('unsupported') != -1 or stringEx.find('-1') != -1:
@@ -139,10 +150,10 @@ class userlogin:
if num > 6: session['code'] = True;
#IP限制
- def limit_address(self,type):
+ def limit_address(self,type,v=""):
import time
clientIp = public.GetClientIp();
- numKey = 'limitIpNum_' + clientIp
+ numKey = 'limitIpNum_' + v + clientIp
limit = 6;
outTime = 600;
try:
@@ -158,14 +169,63 @@ class userlogin:
self.error_num();
session['code'] = True;
return limit - (num1+1);
+
+ #计数验证器
+ if type == '++':
+ cache.inc(numKey,1)
+ self.error_num();
+ session['code'] = False;
+ return limit - (num1+1);
#清空
if type == '-':
cache.delete(numKey);
session['code'] = False;
return 1;
+
+ #清空验证器
+ if type == '--':
+ cache.delete(numKey);
+ session['code'] = False;
+ return 1;
return limit - num1;
except:
return limit;
+ # 登录成功设置session
+ def _set_login_session(self,userInfo):
+ try:
+ session['login'] = True
+ session['username'] = userInfo['username']
+ public.WriteLog('TYPE_LOGIN','LOGIN_SUCCESS',(userInfo['username'],public.GetClientIp()))
+ self.limit_address('-')
+ cache.delete('panelNum')
+ cache.delete('dologin')
+ sess_input_path = 'data/session_last.pl'
+ public.writeFile(sess_input_path,str(int(time.time())))
+ self.set_request_token()
+ self.login_token()
+ return public.returnJson(True,'LOGIN_SUCCESS'),json_header
+ except Exception as ex:
+ stringEx = str(ex)
+ if stringEx.find('unsupported') != -1 or stringEx.find('-1') != -1:
+ os.system("rm -f /tmp/sess_*")
+ os.system("rm -f /www/wwwlogs/*log")
+ public.ServiceReload()
+ return public.returnJson(False,'USER_INODE_ERR'),json_header
+ public.WriteLog('TYPE_LOGIN','LOGIN_ERR_PASS',('****','******',public.GetClientIp()))
+ num = self.limit_address('+');
+ return public.returnJson(False,'LOGIN_USER_ERR',(str(num),)),json_header
+ # 检查是否需要进行二次验证
+ def check_two_step_auth(self):
+ dont_vcode_ip_info = public.readFile("/www/server/panel/data/dont_vcode_ip.txt")
+ acc_client_ip = False
+ if dont_vcode_ip_info:
+ dont_vcode_ip_info = json.loads(dont_vcode_ip_info)
+ ip = dont_vcode_ip_info["client_ip"] == public.GetClientIp()
+ now = int(time.time())
+ v_time = now - int(dont_vcode_ip_info["add_time"])
+ if ip and v_time < 86400:
+ acc_client_ip = True
+ return acc_client_ip
\ No newline at end of file
diff --git a/tools.py b/tools.py
index 1d9bb542..59cb1244 100644
--- a/tools.py
+++ b/tools.py
@@ -409,14 +409,15 @@ def bt_cli(u_input = 0):
print("(7) %s (14) %s"% (public.GetMsg("CHANGE_MYSQL_PASS_FORCE"),public.GetMsg("GET_PANEL_DEFAULT_MSG")))
print("(22) %s (15) %s"% ("Display panel error log",public.GetMsg("CLEAR_SYS_RUBBISH")))
print("(23) %s (16) %s"% ("Turn off BasicAuth authentication","Repair panel (check for errors and update panel files to the latest version)"))
- print("(0) Cancel (17) Set log cutting on/off compression")
+ print("(24) Turn off Google Authenticator (17) Set log cutting on/off compression")
+ print("(0) Cancel")
print(raw_tip)
try:
u_input = input(public.GetMsg("INPUT_CMD_NUM"))
if sys.version_info[0] == 3: u_input = int(u_input)
except: u_input = 0
- nums = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,22,23]
+ nums = [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,22,23,24]
if not u_input in nums:
print(raw_tip)
print(public.GetMsg("CANCELLED"))
@@ -543,6 +544,10 @@ def bt_cli(u_input = 0):
if os.path.exists(filename): os.remove(filename)
os.system('bt reload')
print("|-BasicAuth authentication has been turned off")
+ elif u_input == 24:
+ filename = '/www/server/panel/data/two_step_auth.txt'
+ if os.path.exists(filename): os.remove(filename)
+ print("|-Google Authenticator has been turned off")