diff --git a/BTPanel/__init__.py b/BTPanel/__init__.py index af8c98db..dde1b6e3 100644 --- a/BTPanel/__init__.py +++ b/BTPanel/__init__.py @@ -64,7 +64,7 @@ app.config['SESSION_PERMANENT'] = True 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() @@ -81,7 +81,7 @@ cache.set('p_token','bmac_' + public.Md5(public.get_mac_address())) admin_path_file = 'data/admin_path.pl' admin_path = '/' if os.path.exists(admin_path_file): admin_path = public.readFile(admin_path_file).strip() -admin_path_checks = ['/','/san','/monitor','/abnormal','/close','/task','/login','/config','/site','/sites','ftp','/public','/database','/data','/download_file','/control','/crontab','/firewall','/files','config','/soft','/ajax','/system','/panel_data','/code','/ssl','/plugin','/wxapp','/hook','/safe','/yield','/downloadApi','/pluginApi','/auth','/download','/cloud','/webssh','/connect_event','/panel'] +admin_path_checks = ['/','/san','/bak','/monitor','/abnormal','/close','/task','/login','/config','/site','/sites','ftp','/public','/database','/data','/download_file','/control','/crontab','/firewall','/files','config','/soft','/ajax','/system','/panel_data','/code','/ssl','/plugin','/wxapp','/hook','/safe','/yield','/downloadApi','/pluginApi','/auth','/download','/cloud','/webssh','/connect_event','/panel'] if admin_path in admin_path_checks: admin_path = '/bt' @app.route('/service_status',methods = method_get) @@ -90,7 +90,17 @@ def service_status(): @app.before_request -def basic_auth_check(): +def request_check(): + if not request.path in ['/safe','/hook','/public']: + 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,'This feature is not available in offline mode!'),json_header + if app.config['BASIC_AUTH_OPEN']: if request.path in ['/public','/download']: return; auth = request.authorization @@ -101,10 +111,16 @@ def basic_auth_check(): 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 +133,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) @@ -303,14 +320,14 @@ def firewall(pdata = None): defs = ('GetList','AddDropAddress','DelDropAddress','FirewallReload','SetFirewallStatus','AddAcceptPort','DelAcceptPort','SetSshStatus','SetPing','SetSshPort','GetSshInfo') return publicObject(firewallObject,defs,None,pdata); -#@app.route('/firewall_new',methods=method_all) +@app.route('/firewall_new',methods=method_all) def firewall_new(pdata = None): comReturn = comm.local() if comReturn: return comReturn if request.method == method_get[0] and not pdata: data = {} data['lan'] = public.GetLan('firewall') - return render_template( 'firewall.html',data=data) + return render_template( 'firewall_new.html',data=data) import firewall_new firewallObject = firewall_new.firewalls() defs = ('GetList','AddDropAddress','DelDropAddress','FirewallReload','SetFirewallStatus','AddAcceptPort','DelAcceptPort','SetSshStatus','SetPing','SetSshPort','GetSshInfo','AddSpecifiesIp','DelSpecifiesIp') @@ -337,6 +354,18 @@ def san_baseline(pdata=None): return publicObject(dataObject, defs, None, pdata) +@app.route('/bak', methods=method_all) +def backup_bak(pdata=None): + comReturn = comm.local() + if comReturn: return comReturn + import backup_bak + dataObject = backup_bak.backup_bak() + defs = ('get_sites', 'get_databases', 'backup_database', 'backup_site', 'backup_path', 'get_database_progress', + 'get_site_progress', 'down','get_down_progress','download_path','backup_site_all','get_all_site_progress','backup_date_all','get_all_date_progress') + return publicObject(dataObject, defs, None, pdata) + + + @app.route('/abnormal', methods=method_all) def abnormal(pdata=None): comReturn = comm.local() @@ -357,7 +386,7 @@ def files(pdata = None): import files filesObject = files.files() defs = ('CheckExistsFiles','GetExecLog','GetSearch','ExecShell','GetExecShellMsg','UploadFile','GetDir','CreateFile','CreateDir','DeleteDir','DeleteFile', - 'CopyFile','CopyDir','MvFile','GetFileBody','SaveFileBody','Zip','UnZip','SearchFiles','upload', + 'CopyFile','CopyDir','MvFile','GetFileBody','SaveFileBody','Zip','UnZip','SearchFiles','upload','read_history', 'GetFileAccess','SetFileAccess','GetDirSize','SetBatchData','BatchPaste','install_rar','get_path_size', 'DownloadFile','GetTaskSpeed','CloseLogs','InstallSoft','UninstallSoft','SaveTmpFile','GetTmpFile', 'RemoveTask','ActionTask','Re_Recycle_bin','Get_Recycle_bin','Del_Recycle_bin','Close_Recycle_bin','Recycle_bin') @@ -416,9 +445,11 @@ def config(pdata = None): if data['basic_auth']['open']: data['basic_auth']['value'] = public.GetMsg("OPEN") 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,8 +548,16 @@ 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']: + if get.fun in ['scan_login', 'login_qrcode', 'set_login', 'is_scan_ok', 'blind','static']: + if get.fun == 'static': + if not public.path_safe_check("%s" % (get.filename)): return abort(404) + s_file = '/www/server/panel/BTPanel/static/' + get.filename + 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 get.fun in ['login_qrcode','is_scan_ok']: global admin_check_auth,admin_path,route_path,admin_path_file @@ -555,28 +594,9 @@ def send_favicon(): @app.route('//',methods=method_all) @app.route('///',methods=method_all) def panel_other(name=None,fun = None,stype=None): - #插件公共动态路由 访问方式: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,public.GetMsg("REQUEST_ERR")),json_header + if name.find('./') != -1 or not re.match("^[\w-]+$",name): return abort(404) if not name: return public.returnJson(False,public.GetMsg("PLUGIN_INPUT_A")),json_header p_path = '/www/server/panel/plugin/' + name if not os.path.exists(p_path): return abort(404) @@ -584,10 +604,12 @@ def panel_other(name=None,fun = None,stype=None): #是否响插件应静态文件 if fun == 'static': - if stype.find('./') != -1 or not os.path.exists(p_path + '/static'): return public.returnJson(False,public.GetMsg("REQUEST_ERR")),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,'The specified file does not exist ['+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) #准备参数 @@ -603,26 +625,29 @@ def panel_other(name=None,fun = None,stype=None): #初始化插件对象 try: - sys.path.append(p_path); - plugin_main = __import__(name+'_main') - try: - if sys.version_info[0] == 2: - reload(plugin_main) - else: - from imp import reload - reload(plugin_main) - except:pass - plu = eval('plugin_main.' + name + '_main()') - if not hasattr(plu, fun): return public.returnJson(False, public.GetMsg("SPECIFY_METHOD")), json_header + is_php = os.path.exists(p_path + '/index.php') + if not is_php: + sys.path.append(p_path); + plugin_main = __import__(name+'_main') + try: + if sys.version_info[0] == 2: + reload(plugin_main) + else: + from imp import reload + reload(plugin_main) + except:pass + plu = eval('plugin_main.' + name + '_main()') + if not hasattr(plu,fun): return public.returnJson(False,'SPECIFY_METHOD'),json_header #检查访问权限 comReturn = comm.local() if comReturn: - if not hasattr(plu, '_check'): return public.returnJson(False, public.GetMsg("SPECIFY_PLUG_ERR")), json_header - checks = plu._check(args) - r_type = type(checks) - if r_type == Response: return checks - if r_type != bool or not checks: return public.getJson(checks),json_header + if not is_php: + if not hasattr(plu,'_check'): return public.returnJson(False,'SPECIFY_PLUG_ERR'),json_header + checks = plu._check(args) + r_type = type(checks) + if r_type == Response: return checks + if r_type != bool or not checks: return public.getJson(checks),json_header #初始化面板数据 comm.setSession() @@ -637,7 +662,14 @@ def panel_other(name=None,fun = None,stype=None): return public.returnMsg(False,public.to_string([24744, 26410, 36141, 20080, 91, 37, 115, 93, 25110, 25480, 26435, 24050, 21040, 26399, 33]) % (plugins.get_title_byname(args),)) #执行插件方法 - data = eval('plu.'+fun+'(args)') + if not is_php: + data = eval('plu.'+fun+'(args)') + else: + import panelPHP + args.s = fun + args.name = name + data = panelPHP.panelPHP(name).exec_php_script(args) + r_type = type(data) if r_type == Response: return data @@ -785,6 +817,7 @@ def download(): filename = request.args.get('filename') if not filename: return public.ReturnJson(False,"INIT_ARGS_ERR"),json_header if filename in ['alioss','qiniu','upyun','txcos','ftp']: return panel_cloud() + if filename in ['gdrive','gcloud_storage']: return "Google storage products do not currently support downloads" if not os.path.exists(filename): return public.ReturnJson(False,"FILE_NOT_EXISTS"),json_header mimetype = "application/octet-stream" extName = filename.split('.')[-1] @@ -943,11 +976,10 @@ def publicObject(toObject,defs,action=None,get = None): 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(); - 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,10 +999,11 @@ 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() + try: + import panelPlugin + tmp1 = panelPlugin.panelPlugin().get_cloud_list() + except: + tmp1 = None if tmp1: tmp = tmp1[public.to_string([112,114,111])] else: @@ -1018,9 +1051,13 @@ def notfound(e): @app.errorhandler(500) 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),'
'+public.get_error_info() + '
','The above debugging information is only displayed in developer mode','Version: ' + public.version()) except IndexError:pass return errorStr,500 diff --git a/BTPanel/static/css/site.css b/BTPanel/static/css/site.css index 7e26395b..9d061b40 100644 --- a/BTPanel/static/css/site.css +++ b/BTPanel/static/css/site.css @@ -1368,27 +1368,25 @@ html .menu .menu_exit:hover { left: 0 } -.left, -.right { - width: 100px; - height: 100px; - background: #ccc; - border-radius: 50%; - position: absolute; - top: 0; - left: 0 -} +/*.left,*/ +/*.right {*/ +/* width: 100px;*/ +/* height: 100px;*/ +/* background: #ccc;*/ +/* border-radius: 50%;*/ +/* position: absolute;*/ +/* top: 0;*/ +/* left: 0*/ +/*}*/ -.pie_right, -.right { +.pie_right { clip: rect(0, auto, auto, 50px); transition: transform 1s ease-in 0s; -webkit-transition: -webkit-transform 1s ease-in 0s; -moz-transition: -moz-transform 1s ease-in 0s } -.pie_left, -.left { +.pie_left { clip: rect(0, 50px, auto, 0); transition: transform .4s ease-in 1s; -webkit-transition: -webkit-transform .4s ease-in 1s; @@ -2308,6 +2306,11 @@ html .menu .menu_exit:hover { .setting-con .btn_tips{ display: inline-block; position: relative; + z-index: 10 +} + +.setting-con p .set-info { + margin-left: 20px } .set-submit { @@ -5223,3 +5226,367 @@ select[disabled]{ .ssl_cert_from>.line .line .info-r{ margin-bottom:0; } + +#ace_conter{ + background: #444; + height: 100%; + overflow: hidden; + /*background: url('/static/img/resize_corner.png') no-repeat;*/ +} +.ace_overall { + width: 100%; + display: inline-block; + position: relative; +} +.ace_catalogue { + display: none; + float: left; + width: 250px; + position: absolute; + z-index: 99; + height: 45px; + background: #333; +} +.ace_catalogue_sidebar { + display: none; + height: 100%; + width: 10px; + background: #444; + cursor: col-resize; + position: absolute; + left: 240px; + top: 90px; +} +.ace_catalogue_sidebar img { + position: absolute; + top: 50%; + width: 10px; + margin-top: -20px; +} +.ace_catalogue_title { + height: 45px; + line-height: 45px; + padding-left: 20px; + color: #fff; +} +.ace_conter_menu { + height: 40px; + position: relative; + background: #292929; + overflow: auto; +} +.ace_editor { + font-size: 15px; +} +.ace_editors { + position: absolute; + top: 40px; + right: 0; + bottom: 35px; + left: 0px; + font-size: 13px; + display: none; +} +.ace_editors.active { + display: block; +} +.ace_header { + position: relative; + height: 35px; + background: #565656; + /* transition: all 500ms; */ +} +.ace_header span { + float: left; + height: 35px; + line-height: 35px; + padding: 0 15px; + font-size: 13px; + text-align: center; + color: #fff; + border-right: 1px solid #4c4c4c; + cursor: pointer; +} +.ace_header span .glyphicon { + margin-right: 5px; + vertical-align: text-top; +} +.ace_header span:hover { + background: #2f2f2f; +} +.ace_header .pull-down{ + display: inline-block; + position: absolute; + z-index: 999; + right: 0; + height: 35px; + line-height: 35px; + padding: 0 14px; + font-size: 15px; + color: #fff; + background: #292929; + cursor: pointer; +} +.ace_editor_main { + position: relative; + background: #444; +} + +.ace_editor_main_storey { + position: absolute; + z-index: 999; + width: 100%; + height: 5px; + background: linear-gradient(rgba(0, 0, 0, 0.3), rgba(255, 255, 255, 0)); +} +.ace_conter_menu .item { + display: inline-block; + float: left; + font-size: 15px; + max-width: 350px; + padding: 0 35px 0 10px; + height: 40px; + box-sizing: border-box; + position: relative; + color: #ececec; + cursor: pointer; + border-right: 1px solid #191919; +} +.ace_conter_menu .item:hover { + background: #313131; +} +.ace_conter_menu .item:hover .icon-tool { + display: block; +} +.ace_conter_menu .item.active { + color: #fff; + background: #404040; + border-bottom: 2px solid #20a53a; +} +.ace_conter_menu .item.active .icon-tool { + display: block; +} +.ace_conter_menu .item span { + display: inline-block; + line-height: 40px; + height: 40px; + margin: 0 10px 0 0; +} +.ace_conter_menu .item .icon_file { + color: #ff9800; + font-weight: 500; + margin-left: 10px; +} +.ace_conter_menu .item .icon-tool.fa-circle { + display: block; +} +.ace_conter_menu .item .icon-tool { + display: none; + position: absolute; + right: 15px; + top: 13px; + transition: all 1000ms; + font-size: 14px; +} +.ace_editor_add { + float: left; + height: 40px; + padding: 9px 15px; + font-size: 17px; + color: #ddd; + cursor: pointer; + transition: all 500ms; +} +.ace_editor_add:hover { + background-color: #505050 +} +/* 关闭视图-开始 */ +.ace-clear-form { + padding-top: 25px; +} +.ace-clear-form .line { + margin-left: 20px; +} +.clear-title { + font-size: 15px; + color: #333; +} +.clear-tips { + font-size: 13px; + color: #666; +} +.clear-tips, +.clear-title { + padding-left: 80px; +} +.clear-icon { + width: 30px; + height: 30px; + background-image: url(/static/layer/skin/default/icon.png); + background-position: 0 0; + position: absolute; + left: 30px; + top: 28px; +} +/* 取消按钮组 */ +.ace-clear-btn { + padding: 8px 20px 15px; + text-align: right; + position: absolute; + bottom: 0; + width: 100%; +} +.ace-clear-btn .btn-default:hover { + color: #fff; + background-color: #d9534f; + border-color: #d43f3a; +} +/* 关闭视图-结束 */ +.ace_conter_toolbar { + height: 35px; + line-height: 35px; + bottom: 0; + text-align: right; + margin-right: 15px; + position: relative; + z-index: 999; + background: #444; + font-size: 0; + overflow: hidden; +} + +.ace_conter_toolbar .pull-left, +.ace_conter_toolbar .pull-right{ + height: 35px; +} +.ace_conter_toolbar .pull-left span , +.ace_conter_toolbar .pull-right span { + color: #fff; + display: inline-block; + border-right: 1px solid #505050; + padding: 0 15px; + cursor: pointer; + transition: all 500ms; + font-size: 13px; +} +.ace_conter_toolbar .pull-left span{ + border-right:0; + cursor: default; +} +.ace_conter_toolbar .pull-left span i, +.ace_conter_toolbar .pull-right span i { + font-style: normal; +} +.ace_conter_toolbar .pull-right span:hover { + background: #717171; +} +.ace_toolbar_menu { + position: absolute; + z-index: 9999; + top: 40px; + left: 50%; + margin-left: -200px; + background: #444444; + width: 400px; + padding: 15px 0; + box-shadow: 0px 0px 2px 0px #000; +} +.ace_toolbar_menu .menu-conter { + margin: 0 15px 15px; + position: relative; +} +.ace_toolbar_menu .menu-conter .fa { + display: none; + position: absolute; + right: 10px; + top: 7px; + font-size: 18px; + color: #fff; + cursor: pointer; +} +.ace_toolbar_menu input { + width: 100%; + height: 35px; + background: #444; + border: 1px solid #7d7d7d; + color: #fff; + padding: 0px 10px; + outline: none; +} +.ace_toolbar_menu input:focus{ + border: 1px solid #fff; +} + +.ace_toolbar_menu .menu-item ul { + overflow: auto; + max-height: 300px; +} +.ace_toolbar_menu .menu-item li { + padding: 0 20px; + height: 35px; + line-height: 35px; + color: #fff; + cursor: pointer; + transition: all 500ms; + position: relative; +} +.ace_toolbar_menu .menu-item li.active, +.ace_toolbar_menu .menu-item li.active:hover { + background: #666; +} +.ace_toolbar_menu .menu-item li:hover { + background: #505050; +} +.ace_toolbar_menu .icon-link { + margin-left: 15px; + color: #20a53a; +} +.ace_toolbar_menu .menu-item .icon { + position: absolute; + right: 25px; +} +.ace_toolbar_menu .menu-title { + padding: 0 0 5px 20px; + border-bottom: 1px solid #666666; + color: #9e9e9e; +} + +.make_transist { + -webkit-transition: all .2s ease-in-out; + -moz-transition: all .2s ease-in-out; + -o-transition: all .2s ease-in-out; + -ms-transition: all .2s ease-in-out; + transition: all .2s ease-in-out +} +.ace_conter_search{ + display: none; +} +.helps_conter{ + padding: 25px; + overflow: hidden; +} +.helps_conter .helps_item{ + margin-bottom: 10px; + display: inline-block; + border-left: 3px solid #20a53a; + padding-left: 5px; + font-size: 15px; +} +.helps_conter .helps_box{ + margin-bottom: 20px; + font-size: 14px; + padding-left: 10px; + line-height: 25px; +} + + + +.helps_left{ + width: 50%; + float: left; + padding:15px; +} + +.cursor-row,.cursor-line{ + margin:5px; +} \ No newline at end of file diff --git a/BTPanel/static/img/iconfont_code.png b/BTPanel/static/img/iconfont_code.png new file mode 100644 index 00000000..b77619b0 Binary files /dev/null and b/BTPanel/static/img/iconfont_code.png differ diff --git a/BTPanel/static/js/config.js b/BTPanel/static/js/config.js index 740c2747..c3ab5174 100644 --- a/BTPanel/static/js/config.js +++ b/BTPanel/static/js/config.js @@ -347,11 +347,30 @@ function SetDebug() { layer.msg(rdata.msg, { icon: rdata.status ? 1 : 2 }); }); },function () { - console.log('index.html'); $("#panelDebug").prop('checked',debug_stat); }); } +function set_local() { + var status_s = { false: 'Open', true: 'Close' } + var debug_stat = $("#panelLocal").prop('checked'); + bt.confirm({ + title: status_s[debug_stat] + "Offline mode", + msg: "Do you really want "+ status_s[debug_stat] + "offline mode?", + cancel: function () { + $("#panelLocal").prop('checked',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 }); + }); + },function () { + $("#panelLocal").prop('checked',debug_stat); + }); +} + if(window.location.protocol.indexOf('https') != -1){ $("#panelSSL").prop('checked',true); } diff --git a/BTPanel/static/js/files.js b/BTPanel/static/js/files.js index 50ef8301..4c68ad9b 100644 --- a/BTPanel/static/js/files.js +++ b/BTPanel/static/js/files.js @@ -446,7 +446,7 @@ function GetFiles(Path,sort) { bodyZip = ""+lan.files.file_menu_unzip+" | "; } if(isText(fmp[0])){ - bodyZip = ""+lan.files.file_menu_edit+" | "; + bodyZip = ""+lan.files.file_menu_edit+" | "; } if(isImage(fmp[0])){ download = ""+lan.files.file_menu_img+" | "; @@ -895,7 +895,7 @@ function CreateFile(type, path) { }); if(rdata.status){ GetFiles($("#DirPathPlace input").val()); - OnlineEditFile(0,path + '/' + fileName); + openEditorView(0,path + '/' + fileName); } }); return; @@ -1571,7 +1571,7 @@ function RClick(type,path,name){ // options.items.push({ text: '播放', onclick: function () { GetPlay(path) } }, { text: lan.files.file_menu_down, onclick: function () { GetFileBytes(path) } }, { text: lan.files.file_menu_del, onclick: function () { DeleteFile(path) } }); //} else if(isText(type)){ - options.items.push({text: lan.files.file_menu_edit, onclick: function() {OnlineEditFile(0,path)}},{text: lan.files.file_menu_down, onclick: function() {GetFileBytes(path)}},{text: lan.files.file_menu_del, onclick: function() {DeleteFile(path)}}); + options.items.push({text: lan.files.file_menu_edit, onclick: function() {openEditorView(0,path)}},{text: lan.files.file_menu_down, onclick: function() {GetFileBytes(path)}},{text: lan.files.file_menu_del, onclick: function() {DeleteFile(path)}}); } else if(displayZip != -1){ options.items.push({text: lan.files.file_menu_unzip, onclick: function() {UnZip(path,displayZip)}},{text: lan.files.file_menu_down, onclick: function() {GetFileBytes(path)}},{text: lan.files.file_menu_del, onclick: function() {DeleteFile(path)}}); diff --git a/BTPanel/static/js/index.js b/BTPanel/static/js/index.js index de723b20..157b6538 100644 --- a/BTPanel/static/js/index.js +++ b/BTPanel/static/js/index.js @@ -135,6 +135,21 @@ var index = { layer.closeAll('tips'); }) + $('.cpubox').hover(function () { + var _this = $(this); + var d = _this.parents('ul').data('data').cpu; + var crs = ''; + var n1 = 0; + for (var i = 0; i < d[2].length; i++) { + n1++; + crs += 'CPU-' + i + ": " + d[2][i] + '%' + (n1 % 2 == 0?'
':' | '); + + } + layer.tips(d[3] +"
"+ crs, _this.find('.cicle'), { time: 0, tips: [1, '#999'] }); + }, function () { + layer.closeAll('tips'); + }); + $(".mem-release").hover(function () { $(this).addClass("shine_green"); if (!($(this).hasClass("mem-action"))) { @@ -446,7 +461,7 @@ var index = { closeBtn: 2, content: '
\
'+lan.index.last_version_now+'
\ -
'+lan.index.this_version+''+lan.index.bt_linux+ (rdata.msg.is_beta == 1 ? lan.index.test_version+' ' + rdata.msg.beta.version : lan.index.final_version+' ' + rdata.msg.version) + ' &n'+ lan.index.release_time + (rdata.msg.is_beta == 1 ? rdata.msg.beta.uptime : rdata.msg.uptime) + '
\ +
'+lan.index.this_version+''+lan.index.bt_linux+ (rdata.msg.is_beta == 1 ? lan.index.test_version+' ' + rdata.msg.beta.version : lan.index.final_version+' ' + rdata.msg.version) + ' &n'+ lan.index.release_time + (rdata.msg.is_beta == 1 ? rdata.msg.beta.uptime : rdata.msg.uptime) + '
\
\ \ \ @@ -484,7 +499,7 @@ var index = { content: '
\
'+lan.index.have_new_version+'
\
\ -
'+lan.index.last_version+''+lan.index.bt_linux+ (is_beta === 1 ? lan.index.test_version : lan.index.final_version) + rdata.version + '  '+lan.index.update_date + (result.msg.is_beta == 1 ? result.msg.beta.uptime : result.msg.uptime) + '
\ +
'+lan.index.last_version+''+lan.index.bt_linux+ (is_beta === 1 ? lan.index.test_version : lan.index.final_version) + rdata.version + '  '+lan.index.update_date + (result.msg.is_beta == 1 ? result.msg.beta.uptime : result.msg.uptime) + '
\
'+ rdata.updateMsg + '
\
\ + + + +{% endblock %} diff --git a/class/common.py b/class/common.py index abb7ef80..ba754e64 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.1.5' + g.version = '6.2.0' g.title = public.GetConfigValue('title') g.uri = request.path session['version'] = g.version; @@ -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 request.remote_addr 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 request.remote_addr 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']) @@ -114,8 +96,8 @@ class panelAdmin(panelSetup): 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: @@ -123,10 +105,6 @@ class panelAdmin(panelSetup): 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' @@ -176,24 +154,7 @@ class panelAdmin(panelSetup): session['config']['email'] = public.M('users').where("id=?",('1',)).getField('email'); 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: diff --git a/class/config.py b/class/config.py index b3813924..1c5e5a1e 100644 --- a/class/config.py +++ b/class/config.py @@ -939,6 +939,19 @@ class config: else: t_str = 'Open' public.writeFile(debug_path,'True') - public.WriteLog('TYPE_PANEL','%sDeveloper mode(debug)' % t_str) + public.WriteLog('TYPE_PANEL','%s Developer mode(debug)' % t_str) public.restart_panel() + return public.returnMsg(True,'Successful setup!') + + + #设置离线模式 + def set_local(self,get): + d_path = 'data/not_network.pl' + if os.path.exists(d_path): + t_str = 'Close' + os.remove(d_path) + else: + t_str = 'Open' + public.writeFile(d_path,'True') + public.WriteLog('TYPE_PANEL','%s Offline mode' % t_str) return public.returnMsg(True,'Successful setup!') \ No newline at end of file diff --git a/class/crontab.py b/class/crontab.py index 61a002f4..7c533164 100644 --- a/class/crontab.py +++ b/class/crontab.py @@ -363,7 +363,7 @@ class crontab: shell=wheres[type] except: if type == 'toUrl': - shell = head + "curl -sS --connect-timeout 10 -m 60 '" + param['urladdress']+"'"; + shell = head + "curl -sS --connect-timeout 10 -m 3600 '" + param['urladdress']+"'"; else: shell=head+param['sBody'].replace("\r\n","\n") diff --git a/class/database.py b/class/database.py index 41405c79..9298c861 100644 --- a/class/database.py +++ b/class/database.py @@ -102,7 +102,7 @@ class database(datatool.datatools): if "libmysqlclient" in mysqlMsg: result = self.rep_lnk() os.system("pip uninstall mysql-python -y") - os.system("pip install mysql-python") + os.system("pip install pymysql") public.writeFile('data/restart.pl','True') return public.returnMsg(False,"MYSQL_FIX_WITH_AUTO_ERR") return None @@ -299,7 +299,7 @@ SetLink def SetupPassword(self,get): password = get['password'].strip() try: - rep = "^[\w@\.]+$" + rep = "^[\w@\.\?\-\_\>\<\~\!\#\$\%\^\&\*\(\)]+$" if not re.match(rep, password): return public.returnMsg(False, 'DATABASE_NAME_ERR_T') mysql_root = public.M('config').where("id=?",(1,)).getField('mysql_root') #修改MYSQL @@ -343,7 +343,7 @@ SetLink id = get['id'] name = public.M('databases').where('id=?',(id,)).getField('name'); - rep = "^[\w@\.]+$" + rep = "^[\w@\.\?\-\_\>\<\~\!\#\$\%\^\&\*\(\)]+$" if len(re.search(rep, newpassword).groups()) > 0: return public.returnMsg(False, 'DATABASE_NAME_ERR_T') #修改MYSQL @@ -447,7 +447,7 @@ SetLink tmpFile = tmpFile.replace('.' + ext, '.sql') tmpFile = tmpFile.replace('tar.', '') backupPath = session['config']['backup_path'] + '/database' - + if ext == 'zip': public.ExecShell("cd " + backupPath + " && unzip " + file) else: diff --git a/class/files.py b/class/files.py index 118f06f9..0c7aece6 100644 --- a/class/files.py +++ b/class/files.py @@ -707,7 +707,8 @@ class files: else: data['data'] = srcBody.decode('utf-8') data['encoding'] = u'utf-8'; - + if hasattr(get,'filename'): get.path = get.filename + data['historys'] = self.get_history(get.path) return data; except Exception as ex: return public.returnMsg(False,'INCOMPATIBLE_FILECODE',(str(ex)),) @@ -744,12 +745,14 @@ class files: pass if get.encoding == 'ascii':get.encoding = 'utf-8'; + self.save_history(get.path) if sys.version_info[0] == 2: data = data.encode(get.encoding,errors='ignore'); fp = open(get.path,'w+') else: data = data.encode(get.encoding,errors='ignore').decode(get.encoding); fp = open(get.path,'w+',encoding=get.encoding) + fp.write(data) fp.close() @@ -767,7 +770,39 @@ class files: except Exception as ex: return public.returnMsg(False,'FILE_SAVE_ERR' + str(ex)); - + #保存历史副本 + def save_history(self,filename): + try: + save_path = ('/www/backup/file_history/' + filename).replace('//','/') + if not os.path.exists(save_path): os.makedirs(save_path,384) + public.writeFile(save_path + '/' + str(int(time.time())),public.readFile(filename,'rb'),'wb') + his_list = sorted(os.listdir(save_path)) + num = public.readFile('data/history_num.pl') + if not num: + num = 10 + else: + num = int(num) + d_num = len(his_list) + for i in range(d_num): + if d_num <= num: break; + rm_file = save_path + '/' + his_list[i] + if os.path.exists(rm_file): os.remove(rm_file) + except:pass + + #取历史副本 + def get_history(self,filename): + try: + save_path = ('/www/backup/file_history/' + filename).replace('//','/') + if not os.path.exists(save_path): return [] + return sorted(os.listdir(save_path)) + except: return [] + + #读取指定历史副本 + def read_history(self,args): + save_path = ('/www/backup/file_history/' + args.filename).replace('//','/') + args.path = save_path + '/' + args.history + return self.GetFileBody(args) + #文件压缩 def Zip(self,get) : if not 'z_type' in get: get.z_type = 'rar' @@ -821,10 +856,11 @@ class files: def SetFileAccept(self,filename): os.system('chown -R www:www ' + filename) - os.system('chmod -R 644 ' + filename) - - - + if os.path.isfile(filename): + os.system('chmod -R 644 ' + filename) + else: + os.system('chmod -R 755 ' + filename) + #取目录大小 def GetDirSize(self,get): if sys.version_info[0] == 2: get.path = get.path.encode('utf-8'); diff --git a/class/firewall_new.py b/class/firewall_new.py index 861f1bbc..c127efc8 100644 --- a/class/firewall_new.py +++ b/class/firewall_new.py @@ -89,6 +89,17 @@ class firewalls: data['accept'][i]['address'],addtime)) except: return public.get_error_info() + count = public.M('firewall').count(); + data = {} + data['page'] = public.get_page(count,int(get.p),12,get.collback) + data['data'] = public.M('firewall').limit(data['page']['shift'] + ',' + data['page']['row']).order('id desc').select() + for i in range(len(data['data'])): + if data['data'][i]['port'].find(':') != -1 or data['data'][i]['port'].find('.') != -1 or data['data'][i]['port'].find('-') != -1: + data['data'][i]['status'] = -1; + else: + data['data'][i]['status'] = public.check_port_stat(int(data['data'][i]['port'])); + + data['page'] = data['page']['page'] return data except Exception as ex: return public.get_error_info() diff --git a/class/firewalls.py b/class/firewalls.py index 645541b7..00428c35 100644 --- a/class/firewalls.py +++ b/class/firewalls.py @@ -110,13 +110,14 @@ class firewalls: #添加放行端口 def AddAcceptPort(self,get): import re + src_port = get.port get.port = get.port.replace('-',':') rep = "^\d{1,5}(:\d{1,5})?$" if not re.search(rep,get.port): return public.returnMsg(False,'PORT_CHECK_RANGE'); import time port = get.port ps = get.ps - if public.M('firewall').where("port=?",(port,)).count() > 0: return public.returnMsg(False,'FIREWALL_PORT_EXISTS') + if public.M('firewall').where("port=? or port=?",(port,src_port)).count() > 0: return public.returnMsg(False,'FIREWALL_PORT_EXISTS') notudps = ['80','443','8888','888','39000:40000','21','22'] if self.__isUfw: public.ExecShell('ufw allow ' + port + '/tcp'); diff --git a/class/jobs.py b/class/jobs.py index c6fbce96..51277210 100644 --- a/class/jobs.py +++ b/class/jobs.py @@ -49,6 +49,9 @@ def control_init(): if md51 != md52: import shutil shutil.copyfile(src_file,init_file) + if os.path.getsize(init_file) < 10: + os.system("chattr -i " + init_file) + os.system("\cp -arf %s %s" % (src_file,init_file)) except:pass public.writeFile('/var/bt_setupPath.conf','/www') public.ExecShell(c) @@ -63,7 +66,39 @@ 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') + remove_tty1() + clean_hook_log() + +#清理webhook日志 +def clean_hook_log(): + path = '/www/server/panel/plugin/webhook/script' + if not os.path.exists(path): return False + for name in os.listdir(path): + if name[-4:] != ".log": continue; + clean_max_log(path+'/' + name,524288) + +#清理大日志 +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(log_file,old_line) + public.writeFile(log_file,old_body) + except: + print(public.get_error_info()) + +#删除tty1 +def remove_tty1(): + file_path = '/etc/systemd/system/getty@tty1.service' + if not os.path.exists(file_path): return False + if not os.path.islink(file_path): return False + if os.readlink(file_path) != '/dev/null': return False + try: + os.remove(file_path) + except:pass + #默认禁用指定PHP函数 def disable_putenv(fun_name): diff --git a/class/panelAuth.py b/class/panelAuth.py index 565620ed..9abb1f8d 100644 --- a/class/panelAuth.py +++ b/class/panelAuth.py @@ -20,19 +20,21 @@ class panelAuth: __product_id = '100000011'; def create_serverid(self,get): - userPath = 'data/userInfo.json'; - if not os.path.exists(userPath): return public.returnMsg(False,'LOGIN_FIRST'); - tmp = public.readFile(userPath); - if len(tmp) < 2: tmp = '{}' - data = json.loads(tmp); - if not data: return public.returnMsg(False,'LOGIN_FIRST'); - if not hasattr(data,'serverid'): - s1 = self.get_mac_address() + self.get_hostname() - s2 = self.get_cpuname(); - serverid = public.md5(s1) + public.md5(s2); - data['serverid'] = serverid; - public.writeFile(userPath,json.dumps(data)); - return data; + try: + userPath = 'data/userInfo.json'; + if not os.path.exists(userPath): return public.returnMsg(False,'LOGIN_FIRST'); + tmp = public.readFile(userPath); + if len(tmp) < 2: tmp = '{}' + data = json.loads(tmp); + if not data: return public.returnMsg(False,'LOGIN_FIRST'); + if not hasattr(data,'serverid'): + s1 = self.get_mac_address() + self.get_hostname() + s2 = self.get_cpuname(); + serverid = public.md5(s1) + public.md5(s2); + data['serverid'] = serverid; + public.writeFile(userPath,json.dumps(data)); + return data; + except: return public.returnMsg(False,'LOGIN_FIRST'); def create_plugin_other_order(self,get): diff --git a/class/panelLets.py b/class/panelLets.py index 6c0d8f06..fdc9e381 100644 --- a/class/panelLets.py +++ b/class/panelLets.py @@ -83,15 +83,15 @@ class panelLets: def get_error(self,error): if error.find("Max checks allowed") >= 0 : - return "CA server verification timed out, please wait 5-10 minutes and try again." + return "CA can't verify your domain name, please check if the domain name resolution is correct, or wait 5-10 minutes and try again." elif error.find("Max retries exceeded with") >= 0: - return "The CA server connection timed out, please make sure the server network is unobstructed." + return "The CA server connection timed out, please try again later." elif error.find("The domain name belongs") >= 0: return "The domain name does not belong to this DNS service provider. Please ensure that the domain name is filled in correctly." elif error.find('login token ID is invalid') >=0: return 'The DNS server connection failed. Please check if the key is correct.' 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 '

The signing failed, and the number of attempts to apply for a certificate today has reached the limit!

' + return '

You have failed more than 5 verifications in 1 hour. Please wait 1 hour and try again.

' 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 '

The signing failed, the domain name resolution error, or the resolution is not valid, or the domain name is not filed!

' elif error.find('TLS Web Server Authentication') != -1: diff --git a/class/panelPlugin.py b/class/panelPlugin.py index 94ce27a2..18d70bc9 100644 --- a/class/panelPlugin.py +++ b/class/panelPlugin.py @@ -260,7 +260,7 @@ class panelPlugin: 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) @@ -1554,7 +1554,11 @@ class panelPlugin: try: if not public.path_safe_check("%s/%s" % (get.name,get.s)): return public.returnMsg(False,'PLUGIN_INPUT_C'); path = self.__install_path + '/' + get.name - if not os.path.exists(path + '/'+get.name+'_main.py'): return public.returnMsg(False,'PLUGIN_INPUT_B'); + if not os.path.exists(path + '/'+get.name+'_main.py'): + if os.path.exists(path+'/index.php'): + import panelPHP + return panelPHP.panelPHP(get.name).exec_php_script(get) + return public.returnMsg(False,'PLUGIN_INPUT_B'); if not self.check_accept(get):return public.returnMsg(False,public.to_string([24744, 26410, 36141, 20080, 91, 37, 115, 93, 25110, 25480, 26435, 24050, 21040, 26399, 33]) % (self.get_title_byname(get),)) sys.path.append(path); plugin_main = __import__(get.name+'_main'); diff --git a/class/panelSSL.py b/class/panelSSL.py index ac0fdd5b..74212ccc 100644 --- a/class/panelSSL.py +++ b/class/panelSSL.py @@ -31,10 +31,14 @@ class panelSSL: self.__userInfo = {} else: self.__userInfo = {} - - if self.__userInfo: - pdata['access_key'] = self.__userInfo['access_key']; - data['secret_key'] = self.__userInfo['secret_key']; + try: + if self.__userInfo: + pdata['access_key'] = self.__userInfo['access_key']; + data['secret_key'] = self.__userInfo['secret_key']; + except: + self.__userInfo = {} + pdata['access_key'] = 'test'; + data['secret_key'] = '123456'; else: pdata['access_key'] = 'test'; data['secret_key'] = '123456'; diff --git a/class/panelSite.py b/class/panelSite.py index 4b9c023e..a909f83a 100644 --- a/class/panelSite.py +++ b/class/panelSite.py @@ -759,7 +759,7 @@ class panelSite(panelRedirect): sql.table('domain').where("id=?",(find['id'],)).delete(); public.WriteLog('TYPE_SITE', 'DOMAIN_DEL_SUCCESS',(get.webname,get.domain)); - public.serviceReload(); + public.serviceReload() return public.returnMsg(True,'DEL_SUCCESS'); #检查域名是否解析 @@ -856,7 +856,7 @@ class panelSite(panelRedirect): for domain in domains: if public.checkIp(domain): continue; - if domain.find('*.') >=0 and not file_auth: + if domain.find('*.') >= 0 and file_auth: return public.returnMsg(False, 'A generic domain name cannot be used to apply for a certificate using [File Validation]!'); if file_auth: @@ -901,7 +901,7 @@ class panelSite(panelRedirect): return result def get_site_info(self,siteName): - data = public.M("sites").where('name=?',siteName).field('path,name').find() + data = public.M("sites").where('name=?',siteName).field('id,path,name').find() return data @@ -2439,6 +2439,11 @@ server return f.SaveFileBody(get) # return public.returnMsg(True, '保存成功') + # 检查是否存在#Set Nginx Cache + def check_annotate(self,data): + rep = "\n\s*#Set\s*Nginx\s*Cache" + if re.search(rep,data): + return True # 修改反向代理 def ModifyProxy(self, get): @@ -2477,17 +2482,26 @@ server proxy_cache cache_one; proxy_cache_key $host$uri$is_args$args; proxy_cache_valid 200 304 301 302 %sm;""" % (get.cachetime) - cache_rep = '#proxy_set_header\s+Connection\s+"upgrade";' - ng_conf = re.sub(cache_rep,'#proxy_set_header Connection "upgrade";\n'+ng_cache,ng_conf) + if self.check_annotate(ng_conf): + cache_rep = '\n\s*#Set\s*Nginx\s*Cache(.|\n)*no-cache;' + ng_conf = re.sub(cache_rep,'\n\t#Set Nginx Cache\n'+ng_cache,ng_conf) + else: + cache_rep = '#proxy_set_header\s+Connection\s+"upgrade";' + ng_conf = re.sub(cache_rep, '\n\t#proxy_set_header Connection "upgrade";\n\t#Set Nginx Cache' + ng_cache, + ng_conf) else: - rep = '\s+proxy_cache\s+cache_one.*[\n\s\w\_\";\$]+m;' - ng_conf = re.sub(rep, "", ng_conf) + if self.check_annotate(ng_conf): + rep = '\n\s*#Set\s*Nginx\s*Cache(.|\n)*1m;' + ng_conf = re.sub(rep, "\n\t#Set Nginx Cache\n\tadd_header Cache-Control no-cache;", ng_conf) + else: + rep = '\s+proxy_cache\s+cache_one.*[\n\s\w\_\";\$]+m;' + ng_conf = re.sub(rep, '\n\t#Set Nginx Cache\n\tadd_header Cache-Control no-cache;', ng_conf) sub_rep = "sub_filter" subfilter = json.loads(get.subfilter) if str(proxyUrl[i]["subfilter"]) != str(subfilter): if re.search(sub_rep, ng_conf): - sub_rep = "\s+proxy_set_header\s+Accept-Encoding.*[\n\s\w\_\";]+off;" + sub_rep = "\s+proxy_set_header\s+Accept-Encoding(.|\n)+off;" ng_conf = re.sub(sub_rep,"",ng_conf) # 构造替换字符串 @@ -2582,8 +2596,9 @@ location %s #proxy_http_version 1.1; #proxy_set_header Upgrade $http_upgrade; #proxy_set_header Connection "upgrade"; - add_header X-Cache $upstream_cache_status; + + #Set Nginx Cache %s %s } @@ -2617,14 +2632,14 @@ location %s get.proxydir, get.proxydir,get.proxysite, get.todomain, public.GetMsg("NGINX_PERSISTENCE") ,ng_sub_filter, ng_cache ,get.proxydir) if type == 1 and cache == 0: ng_proxy_cache += ng_proxy % ( - get.proxydir, get.proxydir, get.proxysite, get.todomain, public.GetMsg("NGINX_PERSISTENCE") ,ng_sub_filter,'' ,get.proxydir) + get.proxydir, get.proxydir, get.proxysite, get.todomain, public.GetMsg("NGINX_PERSISTENCE") ,ng_sub_filter,'\tadd_header Cache-Control no-cache;' ,get.proxydir) else: if type == 1 and cache == 1: ng_proxy_cache += ng_proxy % ( get.proxydir, get.proxydir, get.proxysite, get.todomain, public.GetMsg("NGINX_PERSISTENCE") ,ng_sub_filter, ng_cache, get.proxydir) if type == 1 and cache == 0: ng_proxy_cache += ng_proxy % ( - get.proxydir, get.proxydir, get.proxysite, get.todomain, public.GetMsg("NGINX_PERSISTENCE") ,ng_sub_filter, '', get.proxydir) + get.proxydir, get.proxydir, get.proxysite, get.todomain, public.GetMsg("NGINX_PERSISTENCE") ,ng_sub_filter, '\tadd_header Cache-Control no-cache;', get.proxydir) public.writeFile(ng_proxyfile, ng_proxy_cache) @@ -3348,6 +3363,7 @@ location %s #设置防盗链 def SetSecurity(self,get): if len(get.fix) < 2: return public.returnMsg(False,'URL_SUFFIX_NOT_EMPTY!'); + if len(get.domains) < 3: return public.returnMsg(False,'Anti-theft chain domain name cannot be empty!'); file = '/www/server/panel/vhost/nginx/' + get.name + '.conf'; if os.path.exists(file): conf = public.readFile(file); diff --git a/class/panelTask.py b/class/panelTask.py index 9e4bfdde..6c356e98 100644 --- a/class/panelTask.py +++ b/class/panelTask.py @@ -133,6 +133,7 @@ class bt_task: down_file = downloadFile.downloadFile() down_file.logPath = log_file print(down_file.DownloadFile(task_shell,other)) + os.system("chown www.www {}".format(other)) elif task_type == 2: #解压文件 zip_info = json.loads(other) self._unzip(task_shell,zip_info['dfile'],zip_info['password'],log_file) @@ -379,7 +380,12 @@ class bt_task: #设置权限 def set_file_accept(self,filename): os.system('chown -R www:www ' + filename) - os.system('chmod -R 755 ' + 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)) + #检查敏感目录 def check_dir(self,path): diff --git a/class/public.py b/class/public.py index 800d37b7..b302c1bf 100644 --- a/class/public.py +++ b/class/public.py @@ -29,6 +29,8 @@ def HttpGet(url,timeout = 6,headers = {}): @timeout 超时时间默认60秒 return string """ + + if is_local(): return False home = 'www.bt.cn' host_home = 'data/home_host.pl' old_url = url @@ -104,6 +106,7 @@ def HttpPost(url,data,timeout = 6,headers = {}): @timeout 超时时间默认60秒 return string """ + if is_local(): return False home = 'www.bt.cn' host_home = 'data/home_host.pl' old_url = url @@ -1289,25 +1292,22 @@ def get_path_size(path): size_total += os.path.getsize(filename) return size_total - -# 写关键请求日志 -def write_request_log(): +#写关键请求日志 +def write_request_log(reques = None): try: log_path = '/www/server/panel/logs/request' log_file = getDate(format='%Y-%m-%d') + '.json' if not os.path.exists(log_path): os.makedirs(log_path) from flask import request - log_data = {} - log_data['date'] = getDate() - log_data['ip'] = GetClientIp() - log_data['method'] = request.method - log_data['uri'] = request.full_path - log_data['user-agent'] = request.headers.get('User-Agent') - WriteFile(log_path + '/' + log_file, json.dumps(log_data) + "\n", 'a+') - except: - pass - + log_data = [] + log_data.append(getDate()) + log_data.append(GetClientIp()) + log_data.append(request.method) + log_data.append(request.full_path) + log_data.append(request.headers.get('User-Agent')) + WriteFile(log_path + '/' + log_file,json.dumps(log_data) + "\n",'a+') + except: pass # 重载模块 def mod_reload(mode): @@ -1337,9 +1337,11 @@ def set_own(filename, user, group=None): from pwd import getpwnam try: user_info = getpwnam(user) + # user_info = getpwnam('www') user = user_info.pw_uid if group: user_info = getpwnam(group) + # user_info = getpwnam('www') group = user_info.pw_gid except: # 如果指定用户或组不存在,则使用www @@ -1433,18 +1435,85 @@ def de_crypt(key,strings): return strings -# 取通用对象 +#检查IP白名单 +def check_ip_panel(): + ip_file = 'data/limitip.conf' + if os.path.exists(ip_file): + iplist = ReadFile(ip_file) + if iplist: + iplist = iplist.strip(); + if not GetClientIp() in iplist.split(','): + errorStr = ReadFile('./BTPanel/templates/' + GetConfigValue('template') + '/error2.html') + try: + errorStr = errorStr.format(getMsg('PAGE_ERR_TITLE'),getMsg('PAGE_ERR_IP_H1'),getMsg('PAGE_ERR_IP_P1',(GetClientIp(),)),getMsg('PAGE_ERR_IP_P2'),getMsg('PAGE_ERR_IP_P3'),getMsg('NAME'),getMsg('PAGE_ERR_HELP')) + except IndexError:pass + return errorStr + return False + +#检查面板域名 +def check_domain_panel(): + tmp = GetHost() + domain = ReadFile('data/domain.conf') + if domain: + if tmp.strip().lower() != domain.strip().lower(): + errorStr = ReadFile('./BTPanel/templates/' + GetConfigValue('template') + '/error2.html') + try: + errorStr = errorStr.format(getMsg('PAGE_ERR_TITLE'),getMsg('PAGE_ERR_DOMAIN_H1'),getMsg('PAGE_ERR_DOMAIN_P1'),getMsg('PAGE_ERR_DOMAIN_P2'),getMsg('PAGE_ERR_DOMAIN_P3'),getMsg('NAME'),getMsg('PAGE_ERR_HELP')) + except IndexError:pass + return errorStr + return False + +#是否离线模式 +def is_local(): + s_file = '/www/server/panel/data/not_network.pl' + return os.path.exists(s_file) + + +#自动备份面板数据 +def auto_backup_panel(): + b_path = '/www/backup/panel' + backup_path = b_path + '/' + format_date('%Y-%m-%d') + panel_paeh = '/www/server/panel' + if os.path.exists(backup_path): return True + os.makedirs(backup_path,384) + import shutil + shutil.copytree(panel_paeh + '/data',backup_path + '/data') + shutil.copytree(panel_paeh + '/config',backup_path + '/config') + time_now = time.time() - (86400 * 15) + for f in os.listdir(b_path): + try: + if time.mktime(time.strptime(f, "%Y-%m-%d")) < time_now: + path = b_path + '/' + f + if os.path.exists(path): shutil.rmtree(path) + except: continue + + +#检查端口状态 +def check_port_stat(port): + import socket + localIP = '127.0.0.1'; + temp = {} + temp['port'] = port; + temp['local'] = True; + try: + s = socket.socket() + s.settimeout(0.15) + s.connect((localIP,port)) + s.close() + except: + temp['local'] = False; + + result = 0; + if temp['local']: result +=2; + return result; + +#取通用对象 class dict_obj: def __contains__(self, key): - return getattr(self, key, None) - - def __setitem__(self, key, value): setattr(self, key, value) - - def __getitem__(self, key): return getattr(self, key, None) - - def __delitem__(self, key): delattr(self, key) - - def __delattr__(self, key): delattr(self, key) - + return getattr(self,key,None) + def __setitem__(self, key, value): setattr(self,key,value) + def __getitem__(self, key): return getattr(self,key,None) + def __delitem__(self,key): delattr(self,key) + def __delattr__(self, key): delattr(self,key) def get_items(self): return self diff --git a/class/system.py b/class/system.py index 91760688..498489a1 100644 --- a/class/system.py +++ b/class/system.py @@ -273,7 +273,10 @@ class system: #取CPU信息 cpuCount = psutil.cpu_count() used = self.get_cpu_percent() - return used,cpuCount + used_all = psutil.cpu_percent(percpu=True) + cpu_name = public.getCpuType() + return used,cpuCount,used_all,cpu_name + def GetCpuInfo_new(self): cpuCount = psutil.cpu_count() diff --git a/script/logsBackup b/script/logsBackup index a5160791..d0ae72c7 100644 --- a/script/logsBackup +++ b/script/logsBackup @@ -12,9 +12,9 @@ os.chdir("/www/server/panel") sys.path.append('class/') import public print ('==================================================================') -print( '★['+time.strftime("%Y/%m/%d %H:%M:%S")+'],切割日志') +print( '★['+time.strftime("%Y/%m/%d %H:%M:%S")+'],Cutting log') print ('==================================================================') -print ('|--当前保留最新的['+sys.argv[2]+']份') +print ('|--Currently retaining the latest ['+sys.argv[2]+'] copies') logsPath = '/www/wwwlogs/' is_nginx = False if os.path.exists('/www/server/nginx/logs/nginx.pid'): is_nginx = True @@ -24,7 +24,7 @@ if not is_nginx: px = '-access_log' def split_logs(oldFileName,num): global logsPath if not os.path.exists(oldFileName): - print('|---'+oldFileName+'文件不存在!') + print('|---'+oldFileName+'file does not exist!') return logs=sorted(glob.glob(oldFileName+"_*")) @@ -34,11 +34,12 @@ def split_logs(oldFileName,num): for i in range(count): if i>num: break; os.remove(logs[i]) - print('|---多余日志['+logs[i]+']已删除!') + print('|---Extra log ['+logs[i]+'] has been deleted!') newFileName=oldFileName+'_'+time.strftime("%Y-%m-%d_%H%M%S")+'.log' shutil.move(oldFileName,newFileName) - print('|---已切割日志到:'+newFileName) + os.system("gzip %s" % newFileName) + print('|---The log has been cut to: '+newFileName+'.gz') def split_all(save): sites = public.M('sites').field('name').select() diff --git a/script/logsBackup.py b/script/logsBackup.py index a5160791..6d19f136 100644 --- a/script/logsBackup.py +++ b/script/logsBackup.py @@ -12,9 +12,9 @@ os.chdir("/www/server/panel") sys.path.append('class/') import public print ('==================================================================') -print( '★['+time.strftime("%Y/%m/%d %H:%M:%S")+'],切割日志') +print( '★['+time.strftime("%Y/%m/%d %H:%M:%S")+'],Cutting log') print ('==================================================================') -print ('|--当前保留最新的['+sys.argv[2]+']份') +print ('|--Currently retaining the latest ['+sys.argv[2]+'] copies') logsPath = '/www/wwwlogs/' is_nginx = False if os.path.exists('/www/server/nginx/logs/nginx.pid'): is_nginx = True @@ -24,7 +24,7 @@ if not is_nginx: px = '-access_log' def split_logs(oldFileName,num): global logsPath if not os.path.exists(oldFileName): - print('|---'+oldFileName+'文件不存在!') + print('|---'+oldFileName+'file does not exist!') return logs=sorted(glob.glob(oldFileName+"_*")) @@ -34,11 +34,12 @@ def split_logs(oldFileName,num): for i in range(count): if i>num: break; os.remove(logs[i]) - print('|---多余日志['+logs[i]+']已删除!') + print('|---The extra log ['+logs[i]+'] has been deleted!') newFileName=oldFileName+'_'+time.strftime("%Y-%m-%d_%H%M%S")+'.log' shutil.move(oldFileName,newFileName) - print('|---已切割日志到:'+newFileName) + os.system("gzip %s" % newFileName) + print('|---The log has been cut to:'+newFileName+'.gz') def split_all(save): sites = public.M('sites').field('name').select() diff --git a/task.py b/task.py index 6d2d3096..e2bce845 100644 --- a/task.py +++ b/task.py @@ -1,3 +1,4 @@ +#!/bin/python #coding: utf-8 # +------------------------------------------------------------------- # | 宝塔Linux面板 @@ -438,6 +439,8 @@ def panel_status(): panel_url = pool + '127.0.0.1:' + port + '/service_status' panel_pid = get_panel_pid() n = 0 + s = 0 + v = 0 while True: time.sleep(1) if not panel_pid: panel_pid = get_panel_pid() @@ -455,6 +458,25 @@ def panel_status(): continue n += 1 + v += 1 + + if v > 10: + v = 0 + log_path = panel_path + '/logs/error.log' + if os.path.exists(log_path): + e_body = public.GetNumLines(log_path,10) + if e_body: + if e_body.find('PyWSGIServer.do_close') != -1 or e_body.find('Expected GET method:')!=-1 or e_body.find('Invalid HTTP method:') != -1 or e_body.find('table session') != -1: + result = public.httpGet(panel_url) + if result != 'True': + if e_body.find('table session') != -1: + sess_file = '/dev/shm/session.db' + if os.path.exists(sess_file): os.remove(sess_file) + os.system("/etc/init.d/bt reload &") + time.sleep(10) + result = public.httpGet(panel_url) + if result == 'True': + public.WriteLog('TYPE_SOFE','Checked to panel service exception, has been automatically restored!') if n > 18000: n = 0 @@ -465,7 +487,7 @@ def panel_status(): os.system("/etc/init.d/bt reload &") result = public.httpGet(panel_url) if result == 'True': - public.WriteLog('守护程序','检查到面板服务异常,已自动恢复!') + public.WriteLog('TYPE_SOFE','Checked to panel service exception, has been automatically restored!') time.sleep(10) continue