Skip to content
Permalink
Browse files

6.9.19

  • Loading branch information
showpy committed Aug 1, 2019
1 parent 0c8553b commit faf39a6c8062e55ee7c807f0bf1552c4c1467608
Showing with 230 additions and 96 deletions.
  1. +35 −32 BTPanel/__init__.py
  2. +15 −0 BTPanel/static/js/config.js
  3. +7 −0 BTPanel/templates/default/config.html
  4. +3 −42 class/common.py
  5. +13 −0 class/config.py
  6. +1 −1 class/database.py
  7. +12 −1 class/jobs.py
  8. +3 −3 class/panelLets.py
  9. +1 −1 class/panelPlugin.py
  10. +24 −9 class/panelSite.py
  11. +65 −7 class/public.py
  12. +20 −0 task.py
  13. +31 −0 update.sh
@@ -64,7 +64,7 @@
app.config['SESSION_USE_SIGNER'] = True
app.config['SESSION_KEY_PREFIX'] = 'BT_:'
app.config['SESSION_COOKIE_NAME'] = "BT_PANEL_6"
app.config['PERMANENT_SESSION_LIFETIME'] = 86400 * 7
app.config['PERMANENT_SESSION_LIFETIME'] = 86400
Session(app)

if s_sqlite: sdb.create_all()
@@ -90,7 +90,16 @@ def service_status():


@app.before_request
def basic_auth_check():
def request_check():
ip_check = public.check_ip_panel()
if ip_check: return ip_check
domain_check = public.check_domain_panel()
if domain_check: return domain_check
if public.is_local():
not_networks = ['uninstall_plugin','install_plugin','UpdatePanel']
if request.args.get('action') in not_networks:
return public.returnJson(False,'离线模式下无法使用此功能!'),json_header

if app.config['BASIC_AUTH_OPEN']:
if request.path in ['/public','/download']: return;
auth = request.authorization
@@ -99,12 +108,18 @@ def basic_auth_check():
tips = '_bt.cn'
if public.md5(auth.username.strip() + tips) != app.config['BASIC_AUTH_USERNAME'] or public.md5(auth.password.strip() + tips) != app.config['BASIC_AUTH_PASSWORD']:
return send_authenticated()


@app.teardown_request
def request_end(reques = None):
not_acts = ['GetTaskSpeed','GetNetWork','check_pay_status','get_re_order_status','get_order_stat']
key = request.args.get('action')
if not key in not_acts and request.full_path.find('/static/') == -1: public.write_request_log()

def send_authenticated():
global local_ip
if not local_ip: local_ip = public.GetLocalIp()
return Response('', 401,{'WWW-Authenticate': 'Basic realm="%s"' % local_ip})
return Response('', 401,{'WWW-Authenticate': 'Basic realm="%s"' % local_ip.strip()})

@app.route('/',methods=method_all)
def home():
@@ -117,6 +132,7 @@ def home():
data['databaseCount'] = public.M('databases').count()
data['lan'] = public.GetLan('index')
data['724'] = public.format_date("%m%d") == '0724'
public.auto_backup_panel()
return render_template( 'index.html',data = data)

@app.route('/close',methods=method_get)
@@ -416,9 +432,11 @@ def config(pdata = None):
if data['basic_auth']['open']: data['basic_auth']['value'] = '已开启'
data['debug'] = ''
if app.config['DEBUG']: data['debug'] = 'checked'
data['is_local'] = ''
if public.is_local(): data['is_local'] = 'checked'
return render_template( 'config.html',data=data)
import config
defs = ('get_cert_source','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_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)
@@ -517,6 +535,7 @@ def plugin(pdata = None):
def panel_public():
get = get_input();
get.client_ip = public.GetClientIp();
if not hasattr(get,'name'): get.name = ''
if not public.path_safe_check("%s/%s" % (get.name,get.fun)): return abort(404)
if get.fun in ['scan_login','login_qrcode','set_login','is_scan_ok','blind']:
#检查是否验证过安全入口
@@ -555,39 +574,22 @@ def send_favicon():
@app.route('/<name>/<fun>',methods=method_all)
@app.route('/<name>/<fun>/<path:stype>',methods=method_all)
def panel_other(name=None,fun = None,stype=None):
#插件公共动态路由 <name: 插件名称, fun: 被访问的插件方法名, stype:fun=static时则为文件相对于插件static目录下的路径> 访问方式:http://面板地址:端口/插件名称/插件方法.响应类型(html|json)
'''
插件静态文件存储目录: static (允许多级目录,请不要将重要文件放在静态目录),访问方式:http://面板地址:端口/插件名称/static/相对于static的文件路径 如:http://demo.cn:8888/demo/static/js/test.js
插件模板文件存储目录: templates (请不要在里面创建二级目录) 使用模板方法: http://demo.cn:8888/demo/get_logs.html
插件模板文件格式:方法名.html (支持jinja2语法,但无法使用extends语句),请在被访问的方法中返回一个dict,它将被当作data参数传入到模板变量
响应JSON数据: 示例: http://demo.cn:8888/demo/get_logs.json 注意:此处会将插件方法中返回的数据自动转换成JSON字符串响应
直接响应: 示例:http://demo.cn:8888/demo/get_logs ,此时直接响应插件方法返回的数据,注意: 支持 int、float、string、list、redirect对象
'''

#前置准备

if not name: name = 'coll'
if not public.path_safe_check("%s/%s/%s" % (name,fun,stype)): return abort(404)

#是否响应面板默认静态文件
if name == 'static':
s_file = '/www/server/panel/BTPanel/static/' + fun + '/' + stype
if s_file.find('..') != -1 or s_file.find('./') != -1: return abort(404)
if not os.path.exists(s_file): return abort(404)
return send_file(s_file,conditional=True,add_etags=True)

if name.find('./') != -1 or not re.match("^[\w-]+$",name): return public.returnJson(False,'错误的请求!'),json_header
if name.find('./') != -1 or not re.match("^[\w-]+$",name): return abort(404)
if not name: return public.returnJson(False,'请传入插件名称!'),json_header
p_path = '/www/server/panel/plugin/' + name
if not os.path.exists(p_path): return abort(404)


#是否响插件应静态文件
if fun == 'static':
if stype.find('./') != -1 or not os.path.exists(p_path + '/static'): return public.returnJson(False,'错误的请求!'),json_header
if stype.find('./') != -1 or not os.path.exists(p_path + '/static'): return abort(404)
s_file = p_path + '/static/' + stype
if s_file.find('..') != -1: return abort(404)
if not os.path.exists(s_file): return public.returnJson(False,'指定文件不存在['+stype+']'),json_header
if not re.match("^[\w\./-]+$",s_file): return abort(404)
if not public.path_safe_check(s_file): return abort(404)
if not os.path.exists(s_file): return abort(404)
return send_file(s_file,conditional=True,add_etags=True)

#准备参数
@@ -943,11 +945,10 @@ def publicObject(toObject,defs,action=None,get = None):
if get.path.find('..') != -1: return public.ReturnJson(False,'不安全的路径'),json_header
if get.path.find('->') != -1:
get.path = get.path.split('->')[0].strip();
not_acts = ['GetTaskSpeed','GetNetWork','check_pay_status','get_re_order_status','get_order_stat']

for key in defs:
if key == get.action:
fun = 'toObject.'+key+'(get)'
if not key in not_acts: public.write_request_log()
if hasattr(get,'html') or hasattr(get,'s_module'):
return eval(fun)
else:
@@ -967,8 +968,6 @@ def check_login(http_token=None):

def get_pd():
tmp = -1
#tmp1 = cache.get(public.to_string([112, 108, 117, 103, 105, 110, 95, 115, 111, 102, 116, 95, 108, 105, 115, 116]))
#if not tmp1:
import panelPlugin
tmp1 = panelPlugin.panelPlugin().get_cloud_list()
if tmp1:
@@ -1016,11 +1015,15 @@ def notfound(e):
except IndexError: pass
return errorStr,404

@app.errorhandler(500)
@app.errorhandler(Exception)
def internalerror(e):
if str(e).find('Permanent Redirect') != -1: return e
errorStr = public.ReadFile('./BTPanel/templates/' + public.GetConfigValue('template') + '/error.html')
try:
errorStr = errorStr.format(public.getMsg('PAGE_ERR_500_TITLE'),public.getMsg('PAGE_ERR_500_H1'),public.getMsg('PAGE_ERR_500_P1'),public.getMsg('NAME'),public.getMsg('PAGE_ERR_HELP'))
if not app.config['DEBUG']:
errorStr = errorStr.format(public.getMsg('PAGE_ERR_500_TITLE'),public.getMsg('PAGE_ERR_500_H1'),public.getMsg('PAGE_ERR_500_P1'),public.getMsg('NAME'),public.getMsg('PAGE_ERR_HELP'))
else:
errorStr = errorStr.format(public.getMsg('PAGE_ERR_500_TITLE'),str(e),'<pre>'+public.get_error_info() + '</pre>','以上调试信息仅在开发者模式显示','版本号: ' + public.version())
except IndexError:pass
return errorStr,500

@@ -344,6 +344,21 @@ function SetDebug() {
});
}

function set_local() {
var status_s = { false: '开启', true: '关闭' }
var debug_stat = $("#panelLocal").prop('checked');
bt.confirm({ title: status_s[debug_stat] + "离线模式", msg: "您真的要" + status_s[debug_stat] + "离线模式?" }, function () {
var loadT = layer.msg(lan.public.the, { icon: 16, time: 0, shade: [0.3, '#000'] });
$.post('/config?action=set_local', {}, function (rdata) {
layer.close(loadT);
if (rdata.status) {
layer.closeAll();
}
layer.msg(rdata.msg, { icon: rdata.status ? 1 : 2 });
});
});
}

if(window.location.protocol.indexOf('https') != -1){
$("#panelSSL").attr('checked',true);
}
@@ -45,6 +45,13 @@
<label class='btswitch-btn' for='panelDebug' onclick="SetDebug()"></label>
</div>
</div>
<div class="ss-text pull-left mr50">
<em title="开启后面板将停止连接云端,介时软件安装、卸载、面板更新等功能将无法使用">离线模式</em>
<div class='ssh-item'>
<input class='btswitch btswitch-ios' id='panelLocal' type='checkbox' {{data['is_local']}}>
<label class='btswitch-btn' for='panelLocal' onclick="set_local()"></label>
</div>
</div>
</div>
</div>
<div class="setbox bgw mtb15">
@@ -42,36 +42,18 @@ class panelAdmin(panelSetup):
def local(self):
result = panelSetup().init()
if result: return result
result = self.checkLimitIp()
if result: return result
result = self.setSession();
if result: return result
result = self.checkClose();
if result: return result
result = self.checkWebType();
if result: return result
result = self.checkDomain();
result = self.check_login();
if result: return result
result = self.checkConfig();
#self.checkSafe();
self.GetOS();


#检查IP白名单
def checkAddressWhite(self):
token = self.GetToken();
if not token: return redirect('/login');
if not public.GetClientIp() in token['address']: return redirect('/login');


#检查IP限制
def checkLimitIp(self):
if os.path.exists('data/limitip.conf'):
iplist = public.ReadFile('data/limitip.conf')
if iplist:
iplist = iplist.strip();
if not public.GetClientIp() in iplist.split(','): return redirect('/login')

#设置基础Session
def setSession(self):
session['menus'] = sorted(json.loads(public.ReadFile('config/menu.json')),key=lambda x:x['sort'])
@@ -110,19 +92,15 @@ def checkClose(self):
if os.path.exists('data/close.pl'):
return redirect('/close');

#检查域名绑定
def checkDomain(self):
#检查登录
def check_login(self):
try:
api_check = True
if not 'login' in session:
api_check = self.get_sk()
if api_check: return api_check
else:
if session['login'] == False: return redirect('/login')
tmp = public.GetHost()
domain = public.ReadFile('data/domain.conf')
if domain:
if(tmp.strip().lower() != domain.strip().lower()): return redirect('/login')
if api_check:
try:
sess_out_path = 'data/session_timeout.pl'
@@ -173,23 +151,6 @@ def checkConfig(self):
if not 'address' in session:
session['address'] = public.GetLocalIp()

def checkSafe(self):
mods = ['/','/site','/ftp','/database','/plugin','/soft','/public'];
if not os.path.exists('/www/server/panel/data/userInfo.json'):
if 'vip' in session: del(session.vip);
if not request.path in mods: return True
if 'vip' in session: return True

import panelAuth
data = panelAuth.panelAuth().get_order_status(None);
try:
if data['status'] == True:
session.vip = data
return True
return redirect('/vpro');
except:pass
return False

#获取操作系统类型
def GetOS(self):
if not 'server_os' in session:
@@ -943,3 +943,16 @@ def set_debug(self,get):
public.WriteLog('面板配置','%s开发者模式(debug)' % t_str)
public.restart_panel()
return public.returnMsg(True,'设置成功!')


#设置离线模式
def set_local(self,get):
d_path = 'data/not_network.pl'
if os.path.exists(d_path):
t_str = '关闭'
os.remove(d_path)
else:
t_str = '开启'
public.writeFile(d_path,'True')
public.WriteLog('面板配置','%s离线模式' % t_str)
return public.returnMsg(True,'设置成功!')
@@ -767,7 +767,7 @@ def GetRunStatus(self,get):
def GetSlowLogs(self,get):
path = self.GetMySQLInfo(get)['datadir'] + '/mysql-slow.log';
if not os.path.exists(path): return public.returnMsg(False,'日志文件不存在!');
return public.returnMsg(True,public.GetNumLines(path,1000));
return public.returnMsg(True,public.GetNumLines(path,100));


# 获取当前数据库信息
@@ -63,7 +63,18 @@ def control_init():
public.ExecShell("chown -R root:root /www/server/panel/config")
#disable_putenv('putenv')
clean_session()
set_crond()
#set_crond()
clean_max_log('/www/server/panel/plugin/rsync/lsyncd.log')


#清理大日志
def clean_max_log(log_file,max_size = 104857600,old_line = 100):
if not os.path.exists(log_file): return False
if os.path.getsize(log_file) > max_size:
try:
old_body = public.GetNumLines(old_line)
public.writeFile(log_file,old_body)
except:pass

#默认禁用指定PHP函数
def disable_putenv(fun_name):
@@ -83,15 +83,15 @@ def get_acme_name(self,domain_name):
def get_error(self,error):

if error.find("Max checks allowed") >= 0 :
return "CA服务器验证超时,请等待5-10分钟后重试."
return "CA无法验证您的域名,请检查域名解析是否正确,或等待5-10分钟后重试."
elif error.find("Max retries exceeded with") >= 0:
return "CA服务器连接超时,请确保服务器网络通畅."
return "CA服务器连接超时,请稍候重试."
elif error.find("The domain name belongs") >= 0:
return "域名不属于此DNS服务商,请确保域名填写正确."
elif error.find('login token ID is invalid') >=0:
return 'DNS服务器连接失败,请检查密钥是否正确.'
elif "too many certificates already issued for exact set of domains" in error or "Error creating new account :: too many registrations for this IP" in error:
return '<h2>签发失败,您今天尝试申请证书的次数已达上限!</h2>'
return '<h2>签发失败,您1小时内超过5次验证失败,请等待1小时再重试!</h2>'
elif "DNS problem: NXDOMAIN looking up A for" in error or "No valid IP addresses found for" in error or "Invalid response from" in error:
return '<h2>签发失败,域名解析错误,或解析未生效,或域名未备案!</h2>'
elif error.find('TLS Web Server Authentication') != -1:
@@ -260,7 +260,7 @@ def get_cloud_list(self,get=None):
import panelAuth
pdata = panelAuth.panelAuth().create_serverid(None)
listTmp = public.httpPost(cloudUrl,pdata,10)
if len(listTmp) < 200:
if not listTmp or len(listTmp) < 200:
listTmp = public.readFile(lcoalTmp)
try:
softList = json.loads(listTmp)

0 comments on commit faf39a6

Please sign in to comment.
You can’t perform that action at this time.